1cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Copyright 2010 The Closure Library Authors. All Rights Reserved 2cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// 3cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Licensed under the Apache License, Version 2.0 (the "License"); 4cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// you may not use this file except in compliance with the License. 5cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// You may obtain a copy of the License at 6cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// 7cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// http://www.apache.org/licenses/LICENSE-2.0 8cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// 9cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Unless required by applicable law or agreed to in writing, software 10cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// distributed under the License is distributed on an "AS-IS" BASIS, 11cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// See the License for the specific language governing permissions and 13cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// limitations under the License. 14cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 15cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 16cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @fileoverview Message/plural format library with locale support. 17cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 18cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Message format grammar: 19cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 20cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * messageFormatPattern := string ( "{" messageFormatElement "}" string )* 21cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * messageFormatElement := argumentIndex [ "," elementFormat ] 22cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * elementFormat := "plural" "," pluralStyle 23cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * | "selectordinal" "," ordinalStyle 24cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * | "select" "," selectStyle 25cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * pluralStyle := pluralFormatPattern 26cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * ordinalStyle := selectFormatPattern 27cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * selectStyle := selectFormatPattern 28cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * pluralFormatPattern := [ "offset" ":" offsetIndex ] pluralForms* 29cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * selectFormatPattern := pluralForms* 30cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * pluralForms := stringKey "{" ( "{" messageFormatElement "}"|string )* "}" 31cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 32cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * This is a subset of the ICU MessageFormatSyntax: 33cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * http://userguide.icu-project.org/formatparse/messages 34cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * See also http://go/plurals and http://go/ordinals for internal details. 35cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 36cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 37cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Message example: 38cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 39cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * I see {NUM_PEOPLE, plural, offset:1 40cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * =0 {no one at all} 41cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * =1 {{WHO}} 42cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * one {{WHO} and one other person} 43cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * other {{WHO} and # other people}} 44cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * in {PLACE}. 45cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 46cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Calling format({'NUM_PEOPLE': 2, 'WHO': 'Mark', 'PLACE': 'Athens'}) would 47cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * produce "I see Mark and one other person in Athens." as output. 48cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 49cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * OR: 50cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 51cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * {NUM_FLOOR, selectordinal, 52cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * one {Take the elevator to the #st floor.} 53cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * two {Take the elevator to the #nd floor.} 54cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * few {Take the elevator to the #rd floor.} 55cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * other {Take the elevator to the #th floor.}} 56cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 57cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Calling format({'NUM_FLOOR': 22}) would produce 58cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * "Take the elevator to the 22nd floor". 59cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 60cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * See messageformat_test.html for more examples. 61cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 62cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 63cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.provide('goog.i18n.MessageFormat'); 64cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 65cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.require('goog.asserts'); 66cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.require('goog.i18n.ordinalRules'); 67cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.require('goog.i18n.pluralRules'); 68cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 69cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 70cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 71cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 72cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Constructor of MessageFormat. 73cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {string} pattern The pattern we parse and apply positional parameters 74cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * to. 75cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @constructor 76cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @final 77cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 78cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.i18n.MessageFormat = function(pattern) { 79cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) /** 80cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * All encountered literals during parse stage. Indices tell us the order of 81cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * replacement. 82cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @type {!Array.<string>} 83cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 84cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 85cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.literals_ = []; 86cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 87cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) /** 88cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Input pattern gets parsed into objects for faster formatting. 89cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @type {!Array.<!Object>} 90cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 91cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 92cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.parsedPattern_ = []; 93cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 94cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.parsePattern_(pattern); 95cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 96cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 97cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 98cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 99cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Literal strings, including '', are replaced with \uFDDF_x_ for 100cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * parsing purposes, and recovered during format phase. 101cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * \uFDDF is a Unicode nonprinting character, not expected to be found in the 102cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * typical message. 103cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @type {string} 104cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 105cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 106cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.i18n.MessageFormat.LITERAL_PLACEHOLDER_ = '\uFDDF_'; 107cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 108cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 109cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 110cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Marks a string and block during parsing. 111cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @enum {number} 112cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 113cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 114cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.i18n.MessageFormat.Element_ = { 115cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) STRING: 0, 116cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) BLOCK: 1 117cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 118cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 119cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 120cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 121cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Block type. 122cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @enum {number} 123cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 124cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 125cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.i18n.MessageFormat.BlockType_ = { 126cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) PLURAL: 0, 127cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) ORDINAL: 1, 128cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) SELECT: 2, 129cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) SIMPLE: 3, 130cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) STRING: 4, 131cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) UNKNOWN: 5 132cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 133cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 134cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 135cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 136cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Mandatory option in both select and plural form. 137cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @type {string} 138cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 139cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 140cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.i18n.MessageFormat.OTHER_ = 'other'; 141cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 142cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 143cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 144cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Regular expression for looking for string literals. 145cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @type {RegExp} 146cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 147cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 148cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.i18n.MessageFormat.REGEX_LITERAL_ = new RegExp("'([{}#].*?)'", 'g'); 149cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 150cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 151cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 152cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Regular expression for looking for '' in the message. 153cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @type {RegExp} 154cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 155cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 156cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.i18n.MessageFormat.REGEX_DOUBLE_APOSTROPHE_ = new RegExp("''", 'g'); 157cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 158cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 159cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 160cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Formats a message, treating '#' with special meaning representing 161cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * the number (plural_variable - offset). 162cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {!Object} namedParameters Parameters that either 163cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * influence the formatting or are used as actual data. 164cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * I.e. in call to fmt.format({'NUM_PEOPLE': 5, 'NAME': 'Angela'}), 165cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * object {'NUM_PEOPLE': 5, 'NAME': 'Angela'} holds positional parameters. 166cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 1st parameter could mean 5 people, which could influence plural format, 167cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * and 2nd parameter is just a data to be printed out in proper position. 168cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {string} Formatted message. 169cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 170cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.i18n.MessageFormat.prototype.format = function(namedParameters) { 171cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return this.format_(namedParameters, false); 172cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 173cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 174cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 175cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 176cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Formats a message, treating '#' as literary character. 177cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {!Object} namedParameters Parameters that either 178cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * influence the formatting or are used as actual data. 179cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * I.e. in call to fmt.format({'NUM_PEOPLE': 5, 'NAME': 'Angela'}), 180cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * object {'NUM_PEOPLE': 5, 'NAME': 'Angela'} holds positional parameters. 181cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 1st parameter could mean 5 people, which could influence plural format, 182cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * and 2nd parameter is just a data to be printed out in proper position. 183cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {string} Formatted message. 184cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 185cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.i18n.MessageFormat.prototype.formatIgnoringPound = 186cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) function(namedParameters) { 187cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return this.format_(namedParameters, true); 188cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 189cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 190cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 191cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 192cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Formats a message. 193cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {!Object} namedParameters Parameters that either 194cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * influence the formatting or are used as actual data. 195cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * I.e. in call to fmt.format({'NUM_PEOPLE': 5, 'NAME': 'Angela'}), 196cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * object {'NUM_PEOPLE': 5, 'NAME': 'Angela'} holds positional parameters. 197cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 1st parameter could mean 5 people, which could influence plural format, 198cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * and 2nd parameter is just a data to be printed out in proper position. 199cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {boolean} ignorePound If true, treat '#' in plural messages as a 200cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * literary character, else treat it as an ICU syntax character, resolving 201cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * to the number (plural_variable - offset). 202cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {string} Formatted message. 203cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 204cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 205cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.i18n.MessageFormat.prototype.format_ = 206cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) function(namedParameters, ignorePound) { 207cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (this.parsedPattern_.length == 0) { 208cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return ''; 209cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 210cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 211cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var result = []; 212cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.formatBlock_(this.parsedPattern_, namedParameters, ignorePound, result); 213cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var message = result.join(''); 214cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 215cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (!ignorePound) { 216cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) goog.asserts.assert(message.search('#') == -1, 'Not all # were replaced.'); 217cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 218cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 219cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) while (this.literals_.length > 0) { 220cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) message = message.replace(this.buildPlaceholder_(this.literals_), 221cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.literals_.pop()); 222cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 223cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 224cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return message; 225cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 226cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 227cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 228cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 229cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Parses generic block and returns a formatted string. 230cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {!Array.<!Object>} parsedPattern Holds parsed tree. 231cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {!Object} namedParameters Parameters that either influence 232cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * the formatting or are used as actual data. 233cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {boolean} ignorePound If true, treat '#' in plural messages as a 234cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * literary character, else treat it as an ICU syntax character, resolving 235cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * to the number (plural_variable - offset). 236cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {!Array.<!string>} result Each formatting stage appends its product 237cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * to the result. 238cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 239cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 240cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.i18n.MessageFormat.prototype.formatBlock_ = function( 241cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) parsedPattern, namedParameters, ignorePound, result) { 242cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) for (var i = 0; i < parsedPattern.length; i++) { 243cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) switch (parsedPattern[i].type) { 244cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) case goog.i18n.MessageFormat.BlockType_.STRING: 245cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) result.push(parsedPattern[i].value); 246cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) break; 247cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) case goog.i18n.MessageFormat.BlockType_.SIMPLE: 248cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var pattern = parsedPattern[i].value; 249cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.formatSimplePlaceholder_(pattern, namedParameters, result); 250cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) break; 251cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) case goog.i18n.MessageFormat.BlockType_.SELECT: 252cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var pattern = parsedPattern[i].value; 253cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.formatSelectBlock_(pattern, namedParameters, ignorePound, result); 254cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) break; 255cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) case goog.i18n.MessageFormat.BlockType_.PLURAL: 256cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var pattern = parsedPattern[i].value; 257cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.formatPluralOrdinalBlock_(pattern, 258cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) namedParameters, 259cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) goog.i18n.pluralRules.select, 260cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) ignorePound, 261cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) result); 262cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) break; 263cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) case goog.i18n.MessageFormat.BlockType_.ORDINAL: 264cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var pattern = parsedPattern[i].value; 265cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.formatPluralOrdinalBlock_(pattern, 266cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) namedParameters, 267cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) goog.i18n.ordinalRules.select, 268cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) ignorePound, 269cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) result); 270cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) break; 271cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) default: 272cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) goog.asserts.fail('Unrecognized block type.'); 273cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 274cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 275cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 276cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 277cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 278cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 279cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Formats simple placeholder. 280cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {!Object} parsedPattern JSON object containing placeholder info. 281cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {!Object} namedParameters Parameters that are used as actual data. 282cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {!Array.<!string>} result Each formatting stage appends its product 283cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * to the result. 284cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 285cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 286cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.i18n.MessageFormat.prototype.formatSimplePlaceholder_ = function( 287cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) parsedPattern, namedParameters, result) { 288cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var value = namedParameters[parsedPattern]; 289cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (!goog.isDef(value)) { 290cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) result.push('Undefined parameter - ' + parsedPattern); 291cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return; 292cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 293cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 294cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Don't push the value yet, it may contain any of # { } in it which 295cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // will break formatter. Insert a placeholder and replace at the end. 296cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.literals_.push(value); 297cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) result.push(this.buildPlaceholder_(this.literals_)); 298cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 299cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 300cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 301cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 302cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Formats select block. Only one option is selected. 303cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {!Object} parsedPattern JSON object containing select block info. 304cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {!Object} namedParameters Parameters that either influence 305cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * the formatting or are used as actual data. 306cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {boolean} ignorePound If true, treat '#' in plural messages as a 307cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * literary character, else treat it as an ICU syntax character, resolving 308cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * to the number (plural_variable - offset). 309cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {!Array.<!string>} result Each formatting stage appends its product 310cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * to the result. 311cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 312cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 313cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.i18n.MessageFormat.prototype.formatSelectBlock_ = function( 314cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) parsedPattern, namedParameters, ignorePound, result) { 315cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var argumentIndex = parsedPattern.argumentIndex; 316cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (!goog.isDef(namedParameters[argumentIndex])) { 317cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) result.push('Undefined parameter - ' + argumentIndex); 318cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return; 319cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 320cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 321cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var option = parsedPattern[namedParameters[argumentIndex]]; 322cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (!goog.isDef(option)) { 323cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) option = parsedPattern[goog.i18n.MessageFormat.OTHER_]; 324cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) goog.asserts.assertArray( 325cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) option, 'Invalid option or missing other option for select block.'); 326cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 327cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 328cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.formatBlock_(option, namedParameters, ignorePound, result); 329cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 330cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 331cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 332cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 333cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Formats plural or selectordinal block. Only one option is selected and all # 334cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * are replaced. 335cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {!Object} parsedPattern JSON object containing plural block info. 336cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {!Object} namedParameters Parameters that either influence 337cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * the formatting or are used as actual data. 338cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {!function(number, number=):string} pluralSelector A select function 339cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * from goog.i18n.pluralRules or goog.i18n.ordinalRules which determines 340cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * which plural/ordinal form to use based on the input number's cardinality. 341cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {boolean} ignorePound If true, treat '#' in plural messages as a 342cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * literary character, else treat it as an ICU syntax character, resolving 343cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * to the number (plural_variable - offset). 344cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {!Array.<!string>} result Each formatting stage appends its product 345cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * to the result. 346cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 347cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 348cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.i18n.MessageFormat.prototype.formatPluralOrdinalBlock_ = function( 349cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) parsedPattern, namedParameters, pluralSelector, ignorePound, result) { 350cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var argumentIndex = parsedPattern.argumentIndex; 351cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var argumentOffset = parsedPattern.argumentOffset; 352cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var pluralValue = +namedParameters[argumentIndex]; 353cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (isNaN(pluralValue)) { 354cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // TODO(user): Distinguish between undefined and invalid parameters. 355cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) result.push('Undefined or invalid parameter - ' + argumentIndex); 356cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return; 357cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 358cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var diff = pluralValue - argumentOffset; 359cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 360cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Check if there is an exact match. 361cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var option = parsedPattern[namedParameters[argumentIndex]]; 362cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (!goog.isDef(option)) { 363cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) goog.asserts.assert(diff >= 0, 'Argument index smaller than offset.'); 364cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var item; 365cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) item = pluralSelector(diff); 366cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) goog.asserts.assertString(item, 'Invalid plural key.'); 367cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 368cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) option = parsedPattern[item]; 369cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 370cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // If option is not provided fall back to "other". 371cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (!goog.isDef(option)) { 372cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) option = parsedPattern[goog.i18n.MessageFormat.OTHER_]; 373cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 374cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 375cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) goog.asserts.assertArray( 376cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) option, 'Invalid option or missing other option for plural block.'); 377cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 378cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 379cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var pluralResult = []; 380cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.formatBlock_(option, namedParameters, ignorePound, pluralResult); 381cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var plural = pluralResult.join(''); 382cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) goog.asserts.assertString(plural, 'Empty block in plural.'); 383cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (ignorePound) { 384cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) result.push(plural); 385cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else { 386cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // TODO(plundblad): Might want to use a more specific number formatter. 387cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var localeAwareDiff = diff.toLocaleString(); 388cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) result.push(plural.replace(/#/g, localeAwareDiff)); 389cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 390cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 391cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 392cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 393cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 394cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Parses input pattern into an array, for faster reformatting with 395cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * different input parameters. 396cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Parsing is locale independent. 397cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {string} pattern MessageFormat pattern to parse. 398cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 399cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 400cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.i18n.MessageFormat.prototype.parsePattern_ = function(pattern) { 401cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (pattern) { 402cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) pattern = this.insertPlaceholders_(pattern); 403cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 404cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.parsedPattern_ = this.parseBlock_(pattern); 405cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 406cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 407cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 408cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 409cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 410cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Replaces string literals with literal placeholders. 411cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Literals are string of the form '}...', '{...' and '#...' where ... is 412cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * set of characters not containing ' 413cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Builds a dictionary so we can recover literals during format phase. 414cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {string} pattern Pattern to clean up. 415cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {string} Pattern with literals replaced with placeholders. 416cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 417cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 418cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.i18n.MessageFormat.prototype.insertPlaceholders_ = function(pattern) { 419cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var literals = this.literals_; 420cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var buildPlaceholder = goog.bind(this.buildPlaceholder_, this); 421cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 422cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // First replace '' with single quote placeholder since they can be found 423cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // inside other literals. 424cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) pattern = pattern.replace( 425cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) goog.i18n.MessageFormat.REGEX_DOUBLE_APOSTROPHE_, 426cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) function() { 427cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) literals.push("'"); 428cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return buildPlaceholder(literals); 429cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) }); 430cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 431cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) pattern = pattern.replace( 432cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) goog.i18n.MessageFormat.REGEX_LITERAL_, 433cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) function(match, text) { 434cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) literals.push(text); 435cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return buildPlaceholder(literals); 436cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) }); 437cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 438cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return pattern; 439cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 440cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 441cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 442cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 443cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Breaks pattern into strings and top level {...} blocks. 444cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {string} pattern (sub)Pattern to be broken. 445cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {!Array.<Object>} Each item is {type, value}. 446cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 447cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 448cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.i18n.MessageFormat.prototype.extractParts_ = function(pattern) { 449cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var prevPos = 0; 450cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var inBlock = false; 451cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var braceStack = []; 452cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var results = []; 453cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 454cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var braces = /[{}]/g; 455cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) braces.lastIndex = 0; // lastIndex doesn't get set to 0 so we have to. 456cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var match; 457cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 458cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) while (match = braces.exec(pattern)) { 459cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var pos = match.index; 460cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (match[0] == '}') { 461cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var brace = braceStack.pop(); 462cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) goog.asserts.assert(goog.isDef(brace) && brace == '{', 463cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 'No matching { for }.'); 464cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 465cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (braceStack.length == 0) { 466cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // End of the block. 467cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var part = {}; 468cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) part.type = goog.i18n.MessageFormat.Element_.BLOCK; 469cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) part.value = pattern.substring(prevPos, pos); 470cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) results.push(part); 471cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) prevPos = pos + 1; 472cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) inBlock = false; 473cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 474cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else { 475cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (braceStack.length == 0) { 476cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) inBlock = true; 477cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var substring = pattern.substring(prevPos, pos); 478cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (substring != '') { 479cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) results.push({ 480cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) type: goog.i18n.MessageFormat.Element_.STRING, 481cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) value: substring 482cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) }); 483cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 484cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) prevPos = pos + 1; 485cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 486cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) braceStack.push('{'); 487cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 488cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 489cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 490cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Take care of the final string, and check if the braceStack is empty. 491cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) goog.asserts.assert(braceStack.length == 0, 492cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 'There are mismatched { or } in the pattern.'); 493cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 494cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var substring = pattern.substring(prevPos); 495cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (substring != '') { 496cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) results.push({ 497cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) type: goog.i18n.MessageFormat.Element_.STRING, 498cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) value: substring 499cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) }); 500cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 501cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 502cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return results; 503cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 504cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 505cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 506cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 507cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * A regular expression to parse the plural block, extracting the argument 508cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * index and offset (if any). 509cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @type {RegExp} 510cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 511cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 512cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.i18n.MessageFormat.PLURAL_BLOCK_RE_ = 513cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) /^\s*(\w+)\s*,\s*plural\s*,(?:\s*offset:(\d+))?/; 514cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 515cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 516cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 517cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * A regular expression to parse the ordinal block, extracting the argument 518cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * index. 519cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @type {RegExp} 520cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 521cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 522cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.i18n.MessageFormat.ORDINAL_BLOCK_RE_ = /^\s*(\w+)\s*,\s*selectordinal\s*,/; 523cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 524cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 525cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 526cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * A regular expression to parse the select block, extracting the argument 527cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * index. 528cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @type {RegExp} 529cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 530cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 531cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.i18n.MessageFormat.SELECT_BLOCK_RE_ = /^\s*(\w+)\s*,\s*select\s*,/; 532cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 533cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 534cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 535cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Detects which type of a block is the pattern. 536cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {string} pattern Content of the block. 537cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {goog.i18n.MessageFormat.BlockType_} One of the block types. 538cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 539cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 540cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.i18n.MessageFormat.prototype.parseBlockType_ = function(pattern) { 541cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (goog.i18n.MessageFormat.PLURAL_BLOCK_RE_.test(pattern)) { 542cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return goog.i18n.MessageFormat.BlockType_.PLURAL; 543cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 544cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 545cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (goog.i18n.MessageFormat.ORDINAL_BLOCK_RE_.test(pattern)) { 546cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return goog.i18n.MessageFormat.BlockType_.ORDINAL; 547cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 548cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 549cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (goog.i18n.MessageFormat.SELECT_BLOCK_RE_.test(pattern)) { 550cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return goog.i18n.MessageFormat.BlockType_.SELECT; 551cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 552cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 553cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (/^\s*\w+\s*/.test(pattern)) { 554cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return goog.i18n.MessageFormat.BlockType_.SIMPLE; 555cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 556cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 557cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return goog.i18n.MessageFormat.BlockType_.UNKNOWN; 558cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 559cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 560cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 561cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 562cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Parses generic block. 563cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {string} pattern Content of the block to parse. 564cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {!Array.<!Object>} Subblocks marked as strings, select... 565cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 566cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 567cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.i18n.MessageFormat.prototype.parseBlock_ = function(pattern) { 568cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var result = []; 569cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var parts = this.extractParts_(pattern); 570cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) for (var i = 0; i < parts.length; i++) { 571cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var block = {}; 572cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (goog.i18n.MessageFormat.Element_.STRING == parts[i].type) { 573cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) block.type = goog.i18n.MessageFormat.BlockType_.STRING; 574cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) block.value = parts[i].value; 575cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else if (goog.i18n.MessageFormat.Element_.BLOCK == parts[i].type) { 576cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var blockType = this.parseBlockType_(parts[i].value); 577cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 578cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) switch (blockType) { 579cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) case goog.i18n.MessageFormat.BlockType_.SELECT: 580cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) block.type = goog.i18n.MessageFormat.BlockType_.SELECT; 581cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) block.value = this.parseSelectBlock_(parts[i].value); 582cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) break; 583cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) case goog.i18n.MessageFormat.BlockType_.PLURAL: 584cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) block.type = goog.i18n.MessageFormat.BlockType_.PLURAL; 585cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) block.value = this.parsePluralBlock_(parts[i].value); 586cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) break; 587cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) case goog.i18n.MessageFormat.BlockType_.ORDINAL: 588cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) block.type = goog.i18n.MessageFormat.BlockType_.ORDINAL; 589cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) block.value = this.parseOrdinalBlock_(parts[i].value); 590cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) break; 591cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) case goog.i18n.MessageFormat.BlockType_.SIMPLE: 592cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) block.type = goog.i18n.MessageFormat.BlockType_.SIMPLE; 593cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) block.value = parts[i].value; 594cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) break; 595cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) default: 596cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) goog.asserts.fail('Unknown block type.'); 597cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 598cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else { 599cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) goog.asserts.fail('Unknown part of the pattern.'); 600cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 601cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) result.push(block); 602cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 603cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 604cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return result; 605cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 606cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 607cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 608cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 609cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Parses a select type of a block and produces JSON object for it. 610cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {string} pattern Subpattern that needs to be parsed as select pattern. 611cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {!Object} Object with select block info. 612cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 613cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 614cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.i18n.MessageFormat.prototype.parseSelectBlock_ = function(pattern) { 615cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var argumentIndex = ''; 616cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var replaceRegex = goog.i18n.MessageFormat.SELECT_BLOCK_RE_; 617cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) pattern = pattern.replace(replaceRegex, function(string, name) { 618cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) argumentIndex = name; 619cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return ''; 620cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) }); 621cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var result = {}; 622cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) result.argumentIndex = argumentIndex; 623cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 624cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var parts = this.extractParts_(pattern); 625cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Looking for (key block)+ sequence. One of the keys has to be "other". 626cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var pos = 0; 627cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) while (pos < parts.length) { 628cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var key = parts[pos].value; 629cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) goog.asserts.assertString(key, 'Missing select key element.'); 630cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 631cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) pos++; 632cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) goog.asserts.assert(pos < parts.length, 633cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 'Missing or invalid select value element.'); 634cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 635cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (goog.i18n.MessageFormat.Element_.BLOCK == parts[pos].type) { 636cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var value = this.parseBlock_(parts[pos].value); 637cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else { 638cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) goog.asserts.fail('Expected block type.'); 639cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 640cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) result[key.replace(/\s/g, '')] = value; 641cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) pos++; 642cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 643cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 644cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) goog.asserts.assertArray(result[goog.i18n.MessageFormat.OTHER_], 645cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 'Missing other key in select statement.'); 646cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return result; 647cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 648cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 649cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 650cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 651cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Parses a plural type of a block and produces JSON object for it. 652cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {string} pattern Subpattern that needs to be parsed as plural pattern. 653cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {!Object} Object with select block info. 654cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 655cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 656cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.i18n.MessageFormat.prototype.parsePluralBlock_ = function(pattern) { 657cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var argumentIndex = ''; 658cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var argumentOffset = 0; 659cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var replaceRegex = goog.i18n.MessageFormat.PLURAL_BLOCK_RE_; 660cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) pattern = pattern.replace(replaceRegex, function(string, name, offset) { 661cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) argumentIndex = name; 662cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (offset) { 663cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) argumentOffset = parseInt(offset, 10); 664cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 665cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return ''; 666cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) }); 667cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 668cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var result = {}; 669cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) result.argumentIndex = argumentIndex; 670cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) result.argumentOffset = argumentOffset; 671cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 672cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var parts = this.extractParts_(pattern); 673cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Looking for (key block)+ sequence. 674cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var pos = 0; 675cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) while (pos < parts.length) { 676cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var key = parts[pos].value; 677cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) goog.asserts.assertString(key, 'Missing plural key element.'); 678cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 679cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) pos++; 680cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) goog.asserts.assert(pos < parts.length, 681cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 'Missing or invalid plural value element.'); 682cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 683cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (goog.i18n.MessageFormat.Element_.BLOCK == parts[pos].type) { 684cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var value = this.parseBlock_(parts[pos].value); 685cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else { 686cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) goog.asserts.fail('Expected block type.'); 687cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 688cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) result[key.replace(/\s*(?:=)?(\w+)\s*/, '$1')] = value; 689cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) pos++; 690cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 691cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 692cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) goog.asserts.assertArray(result[goog.i18n.MessageFormat.OTHER_], 693cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 'Missing other key in plural statement.'); 694cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 695cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return result; 696cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 697cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 698cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 699cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 700cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Parses an ordinal type of a block and produces JSON object for it. 701cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * For example the input string: 702cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * '{FOO, selectordinal, one {Message A}other {Message B}}' 703cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Should result in the output object: 704cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * { 705cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * argumentIndex: 'FOO', 706cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * argumentOffest: 0, 707cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * one: [ { type: 4, value: 'Message A' } ], 708cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * other: [ { type: 4, value: 'Message B' } ] 709cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * } 710cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {string} pattern Subpattern that needs to be parsed as plural pattern. 711cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {!Object} Object with select block info. 712cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 713cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 714cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.i18n.MessageFormat.prototype.parseOrdinalBlock_ = function(pattern) { 715cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var argumentIndex = ''; 716cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var replaceRegex = goog.i18n.MessageFormat.ORDINAL_BLOCK_RE_; 717cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) pattern = pattern.replace(replaceRegex, function(string, name) { 718cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) argumentIndex = name; 719cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return ''; 720cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) }); 721cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 722cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var result = {}; 723cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) result.argumentIndex = argumentIndex; 724cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) result.argumentOffset = 0; 725cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 726cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var parts = this.extractParts_(pattern); 727cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Looking for (key block)+ sequence. 728cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var pos = 0; 729cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) while (pos < parts.length) { 730cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var key = parts[pos].value; 731cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) goog.asserts.assertString(key, 'Missing ordinal key element.'); 732cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 733cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) pos++; 734cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) goog.asserts.assert(pos < parts.length, 735cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 'Missing or invalid ordinal value element.'); 736cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 737cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (goog.i18n.MessageFormat.Element_.BLOCK == parts[pos].type) { 738cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var value = this.parseBlock_(parts[pos].value); 739cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else { 740cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) goog.asserts.fail('Expected block type.'); 741cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 742cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) result[key.replace(/\s*(?:=)?(\w+)\s*/, '$1')] = value; 743cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) pos++; 744cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 745cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 746cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) goog.asserts.assertArray(result[goog.i18n.MessageFormat.OTHER_], 747cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 'Missing other key in selectordinal statement.'); 748cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 749cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return result; 750cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 751cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 752cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 753cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 754cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Builds a placeholder from the last index of the array. 755cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {!Array} literals All literals encountered during parse. 756cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {string} \uFDDF_ + last index + _. 757cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 758cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 759cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)goog.i18n.MessageFormat.prototype.buildPlaceholder_ = function(literals) { 760cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) goog.asserts.assert(literals.length > 0, 'Literal array is empty.'); 761cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 762cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var index = (literals.length - 1).toString(10); 763cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return goog.i18n.MessageFormat.LITERAL_PLACEHOLDER_ + index + '_'; 764cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 765