summaryrefslogtreecommitdiffstats
path: root/chromium/chrome/third_party/chromevox/third_party/closure-library/closure/goog/i18n/messageformat.js
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/chrome/third_party/chromevox/third_party/closure-library/closure/goog/i18n/messageformat.js')
-rw-r--r--chromium/chrome/third_party/chromevox/third_party/closure-library/closure/goog/i18n/messageformat.js764
1 files changed, 764 insertions, 0 deletions
diff --git a/chromium/chrome/third_party/chromevox/third_party/closure-library/closure/goog/i18n/messageformat.js b/chromium/chrome/third_party/chromevox/third_party/closure-library/closure/goog/i18n/messageformat.js
new file mode 100644
index 00000000000..923af98b218
--- /dev/null
+++ b/chromium/chrome/third_party/chromevox/third_party/closure-library/closure/goog/i18n/messageformat.js
@@ -0,0 +1,764 @@
+// Copyright 2010 The Closure Library Authors. All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * @fileoverview Message/plural format library with locale support.
+ *
+ * Message format grammar:
+ *
+ * messageFormatPattern := string ( "{" messageFormatElement "}" string )*
+ * messageFormatElement := argumentIndex [ "," elementFormat ]
+ * elementFormat := "plural" "," pluralStyle
+ * | "selectordinal" "," ordinalStyle
+ * | "select" "," selectStyle
+ * pluralStyle := pluralFormatPattern
+ * ordinalStyle := selectFormatPattern
+ * selectStyle := selectFormatPattern
+ * pluralFormatPattern := [ "offset" ":" offsetIndex ] pluralForms*
+ * selectFormatPattern := pluralForms*
+ * pluralForms := stringKey "{" ( "{" messageFormatElement "}"|string )* "}"
+ *
+ * This is a subset of the ICU MessageFormatSyntax:
+ * http://userguide.icu-project.org/formatparse/messages
+ * See also http://go/plurals and http://go/ordinals for internal details.
+ *
+ *
+ * Message example:
+ *
+ * I see {NUM_PEOPLE, plural, offset:1
+ * =0 {no one at all}
+ * =1 {{WHO}}
+ * one {{WHO} and one other person}
+ * other {{WHO} and # other people}}
+ * in {PLACE}.
+ *
+ * Calling format({'NUM_PEOPLE': 2, 'WHO': 'Mark', 'PLACE': 'Athens'}) would
+ * produce "I see Mark and one other person in Athens." as output.
+ *
+ * OR:
+ *
+ * {NUM_FLOOR, selectordinal,
+ * one {Take the elevator to the #st floor.}
+ * two {Take the elevator to the #nd floor.}
+ * few {Take the elevator to the #rd floor.}
+ * other {Take the elevator to the #th floor.}}
+ *
+ * Calling format({'NUM_FLOOR': 22}) would produce
+ * "Take the elevator to the 22nd floor".
+ *
+ * See messageformat_test.html for more examples.
+ */
+
+goog.provide('goog.i18n.MessageFormat');
+
+goog.require('goog.asserts');
+goog.require('goog.i18n.ordinalRules');
+goog.require('goog.i18n.pluralRules');
+
+
+
+/**
+ * Constructor of MessageFormat.
+ * @param {string} pattern The pattern we parse and apply positional parameters
+ * to.
+ * @constructor
+ * @final
+ */
+goog.i18n.MessageFormat = function(pattern) {
+ /**
+ * All encountered literals during parse stage. Indices tell us the order of
+ * replacement.
+ * @type {!Array.<string>}
+ * @private
+ */
+ this.literals_ = [];
+
+ /**
+ * Input pattern gets parsed into objects for faster formatting.
+ * @type {!Array.<!Object>}
+ * @private
+ */
+ this.parsedPattern_ = [];
+
+ this.parsePattern_(pattern);
+};
+
+
+/**
+ * Literal strings, including '', are replaced with \uFDDF_x_ for
+ * parsing purposes, and recovered during format phase.
+ * \uFDDF is a Unicode nonprinting character, not expected to be found in the
+ * typical message.
+ * @type {string}
+ * @private
+ */
+goog.i18n.MessageFormat.LITERAL_PLACEHOLDER_ = '\uFDDF_';
+
+
+/**
+ * Marks a string and block during parsing.
+ * @enum {number}
+ * @private
+ */
+goog.i18n.MessageFormat.Element_ = {
+ STRING: 0,
+ BLOCK: 1
+};
+
+
+/**
+ * Block type.
+ * @enum {number}
+ * @private
+ */
+goog.i18n.MessageFormat.BlockType_ = {
+ PLURAL: 0,
+ ORDINAL: 1,
+ SELECT: 2,
+ SIMPLE: 3,
+ STRING: 4,
+ UNKNOWN: 5
+};
+
+
+/**
+ * Mandatory option in both select and plural form.
+ * @type {string}
+ * @private
+ */
+goog.i18n.MessageFormat.OTHER_ = 'other';
+
+
+/**
+ * Regular expression for looking for string literals.
+ * @type {RegExp}
+ * @private
+ */
+goog.i18n.MessageFormat.REGEX_LITERAL_ = new RegExp("'([{}#].*?)'", 'g');
+
+
+/**
+ * Regular expression for looking for '' in the message.
+ * @type {RegExp}
+ * @private
+ */
+goog.i18n.MessageFormat.REGEX_DOUBLE_APOSTROPHE_ = new RegExp("''", 'g');
+
+
+/**
+ * Formats a message, treating '#' with special meaning representing
+ * the number (plural_variable - offset).
+ * @param {!Object} namedParameters Parameters that either
+ * influence the formatting or are used as actual data.
+ * I.e. in call to fmt.format({'NUM_PEOPLE': 5, 'NAME': 'Angela'}),
+ * object {'NUM_PEOPLE': 5, 'NAME': 'Angela'} holds positional parameters.
+ * 1st parameter could mean 5 people, which could influence plural format,
+ * and 2nd parameter is just a data to be printed out in proper position.
+ * @return {string} Formatted message.
+ */
+goog.i18n.MessageFormat.prototype.format = function(namedParameters) {
+ return this.format_(namedParameters, false);
+};
+
+
+/**
+ * Formats a message, treating '#' as literary character.
+ * @param {!Object} namedParameters Parameters that either
+ * influence the formatting or are used as actual data.
+ * I.e. in call to fmt.format({'NUM_PEOPLE': 5, 'NAME': 'Angela'}),
+ * object {'NUM_PEOPLE': 5, 'NAME': 'Angela'} holds positional parameters.
+ * 1st parameter could mean 5 people, which could influence plural format,
+ * and 2nd parameter is just a data to be printed out in proper position.
+ * @return {string} Formatted message.
+ */
+goog.i18n.MessageFormat.prototype.formatIgnoringPound =
+ function(namedParameters) {
+ return this.format_(namedParameters, true);
+};
+
+
+/**
+ * Formats a message.
+ * @param {!Object} namedParameters Parameters that either
+ * influence the formatting or are used as actual data.
+ * I.e. in call to fmt.format({'NUM_PEOPLE': 5, 'NAME': 'Angela'}),
+ * object {'NUM_PEOPLE': 5, 'NAME': 'Angela'} holds positional parameters.
+ * 1st parameter could mean 5 people, which could influence plural format,
+ * and 2nd parameter is just a data to be printed out in proper position.
+ * @param {boolean} ignorePound If true, treat '#' in plural messages as a
+ * literary character, else treat it as an ICU syntax character, resolving
+ * to the number (plural_variable - offset).
+ * @return {string} Formatted message.
+ * @private
+ */
+goog.i18n.MessageFormat.prototype.format_ =
+ function(namedParameters, ignorePound) {
+ if (this.parsedPattern_.length == 0) {
+ return '';
+ }
+
+ var result = [];
+ this.formatBlock_(this.parsedPattern_, namedParameters, ignorePound, result);
+ var message = result.join('');
+
+ if (!ignorePound) {
+ goog.asserts.assert(message.search('#') == -1, 'Not all # were replaced.');
+ }
+
+ while (this.literals_.length > 0) {
+ message = message.replace(this.buildPlaceholder_(this.literals_),
+ this.literals_.pop());
+ }
+
+ return message;
+};
+
+
+/**
+ * Parses generic block and returns a formatted string.
+ * @param {!Array.<!Object>} parsedPattern Holds parsed tree.
+ * @param {!Object} namedParameters Parameters that either influence
+ * the formatting or are used as actual data.
+ * @param {boolean} ignorePound If true, treat '#' in plural messages as a
+ * literary character, else treat it as an ICU syntax character, resolving
+ * to the number (plural_variable - offset).
+ * @param {!Array.<!string>} result Each formatting stage appends its product
+ * to the result.
+ * @private
+ */
+goog.i18n.MessageFormat.prototype.formatBlock_ = function(
+ parsedPattern, namedParameters, ignorePound, result) {
+ for (var i = 0; i < parsedPattern.length; i++) {
+ switch (parsedPattern[i].type) {
+ case goog.i18n.MessageFormat.BlockType_.STRING:
+ result.push(parsedPattern[i].value);
+ break;
+ case goog.i18n.MessageFormat.BlockType_.SIMPLE:
+ var pattern = parsedPattern[i].value;
+ this.formatSimplePlaceholder_(pattern, namedParameters, result);
+ break;
+ case goog.i18n.MessageFormat.BlockType_.SELECT:
+ var pattern = parsedPattern[i].value;
+ this.formatSelectBlock_(pattern, namedParameters, ignorePound, result);
+ break;
+ case goog.i18n.MessageFormat.BlockType_.PLURAL:
+ var pattern = parsedPattern[i].value;
+ this.formatPluralOrdinalBlock_(pattern,
+ namedParameters,
+ goog.i18n.pluralRules.select,
+ ignorePound,
+ result);
+ break;
+ case goog.i18n.MessageFormat.BlockType_.ORDINAL:
+ var pattern = parsedPattern[i].value;
+ this.formatPluralOrdinalBlock_(pattern,
+ namedParameters,
+ goog.i18n.ordinalRules.select,
+ ignorePound,
+ result);
+ break;
+ default:
+ goog.asserts.fail('Unrecognized block type.');
+ }
+ }
+};
+
+
+/**
+ * Formats simple placeholder.
+ * @param {!Object} parsedPattern JSON object containing placeholder info.
+ * @param {!Object} namedParameters Parameters that are used as actual data.
+ * @param {!Array.<!string>} result Each formatting stage appends its product
+ * to the result.
+ * @private
+ */
+goog.i18n.MessageFormat.prototype.formatSimplePlaceholder_ = function(
+ parsedPattern, namedParameters, result) {
+ var value = namedParameters[parsedPattern];
+ if (!goog.isDef(value)) {
+ result.push('Undefined parameter - ' + parsedPattern);
+ return;
+ }
+
+ // Don't push the value yet, it may contain any of # { } in it which
+ // will break formatter. Insert a placeholder and replace at the end.
+ this.literals_.push(value);
+ result.push(this.buildPlaceholder_(this.literals_));
+};
+
+
+/**
+ * Formats select block. Only one option is selected.
+ * @param {!Object} parsedPattern JSON object containing select block info.
+ * @param {!Object} namedParameters Parameters that either influence
+ * the formatting or are used as actual data.
+ * @param {boolean} ignorePound If true, treat '#' in plural messages as a
+ * literary character, else treat it as an ICU syntax character, resolving
+ * to the number (plural_variable - offset).
+ * @param {!Array.<!string>} result Each formatting stage appends its product
+ * to the result.
+ * @private
+ */
+goog.i18n.MessageFormat.prototype.formatSelectBlock_ = function(
+ parsedPattern, namedParameters, ignorePound, result) {
+ var argumentIndex = parsedPattern.argumentIndex;
+ if (!goog.isDef(namedParameters[argumentIndex])) {
+ result.push('Undefined parameter - ' + argumentIndex);
+ return;
+ }
+
+ var option = parsedPattern[namedParameters[argumentIndex]];
+ if (!goog.isDef(option)) {
+ option = parsedPattern[goog.i18n.MessageFormat.OTHER_];
+ goog.asserts.assertArray(
+ option, 'Invalid option or missing other option for select block.');
+ }
+
+ this.formatBlock_(option, namedParameters, ignorePound, result);
+};
+
+
+/**
+ * Formats plural or selectordinal block. Only one option is selected and all #
+ * are replaced.
+ * @param {!Object} parsedPattern JSON object containing plural block info.
+ * @param {!Object} namedParameters Parameters that either influence
+ * the formatting or are used as actual data.
+ * @param {!function(number, number=):string} pluralSelector A select function
+ * from goog.i18n.pluralRules or goog.i18n.ordinalRules which determines
+ * which plural/ordinal form to use based on the input number's cardinality.
+ * @param {boolean} ignorePound If true, treat '#' in plural messages as a
+ * literary character, else treat it as an ICU syntax character, resolving
+ * to the number (plural_variable - offset).
+ * @param {!Array.<!string>} result Each formatting stage appends its product
+ * to the result.
+ * @private
+ */
+goog.i18n.MessageFormat.prototype.formatPluralOrdinalBlock_ = function(
+ parsedPattern, namedParameters, pluralSelector, ignorePound, result) {
+ var argumentIndex = parsedPattern.argumentIndex;
+ var argumentOffset = parsedPattern.argumentOffset;
+ var pluralValue = +namedParameters[argumentIndex];
+ if (isNaN(pluralValue)) {
+ // TODO(user): Distinguish between undefined and invalid parameters.
+ result.push('Undefined or invalid parameter - ' + argumentIndex);
+ return;
+ }
+ var diff = pluralValue - argumentOffset;
+
+ // Check if there is an exact match.
+ var option = parsedPattern[namedParameters[argumentIndex]];
+ if (!goog.isDef(option)) {
+ goog.asserts.assert(diff >= 0, 'Argument index smaller than offset.');
+ var item;
+ item = pluralSelector(diff);
+ goog.asserts.assertString(item, 'Invalid plural key.');
+
+ option = parsedPattern[item];
+
+ // If option is not provided fall back to "other".
+ if (!goog.isDef(option)) {
+ option = parsedPattern[goog.i18n.MessageFormat.OTHER_];
+ }
+
+ goog.asserts.assertArray(
+ option, 'Invalid option or missing other option for plural block.');
+ }
+
+ var pluralResult = [];
+ this.formatBlock_(option, namedParameters, ignorePound, pluralResult);
+ var plural = pluralResult.join('');
+ goog.asserts.assertString(plural, 'Empty block in plural.');
+ if (ignorePound) {
+ result.push(plural);
+ } else {
+ // TODO(plundblad): Might want to use a more specific number formatter.
+ var localeAwareDiff = diff.toLocaleString();
+ result.push(plural.replace(/#/g, localeAwareDiff));
+ }
+};
+
+
+/**
+ * Parses input pattern into an array, for faster reformatting with
+ * different input parameters.
+ * Parsing is locale independent.
+ * @param {string} pattern MessageFormat pattern to parse.
+ * @private
+ */
+goog.i18n.MessageFormat.prototype.parsePattern_ = function(pattern) {
+ if (pattern) {
+ pattern = this.insertPlaceholders_(pattern);
+
+ this.parsedPattern_ = this.parseBlock_(pattern);
+ }
+};
+
+
+/**
+ * Replaces string literals with literal placeholders.
+ * Literals are string of the form '}...', '{...' and '#...' where ... is
+ * set of characters not containing '
+ * Builds a dictionary so we can recover literals during format phase.
+ * @param {string} pattern Pattern to clean up.
+ * @return {string} Pattern with literals replaced with placeholders.
+ * @private
+ */
+goog.i18n.MessageFormat.prototype.insertPlaceholders_ = function(pattern) {
+ var literals = this.literals_;
+ var buildPlaceholder = goog.bind(this.buildPlaceholder_, this);
+
+ // First replace '' with single quote placeholder since they can be found
+ // inside other literals.
+ pattern = pattern.replace(
+ goog.i18n.MessageFormat.REGEX_DOUBLE_APOSTROPHE_,
+ function() {
+ literals.push("'");
+ return buildPlaceholder(literals);
+ });
+
+ pattern = pattern.replace(
+ goog.i18n.MessageFormat.REGEX_LITERAL_,
+ function(match, text) {
+ literals.push(text);
+ return buildPlaceholder(literals);
+ });
+
+ return pattern;
+};
+
+
+/**
+ * Breaks pattern into strings and top level {...} blocks.
+ * @param {string} pattern (sub)Pattern to be broken.
+ * @return {!Array.<Object>} Each item is {type, value}.
+ * @private
+ */
+goog.i18n.MessageFormat.prototype.extractParts_ = function(pattern) {
+ var prevPos = 0;
+ var inBlock = false;
+ var braceStack = [];
+ var results = [];
+
+ var braces = /[{}]/g;
+ braces.lastIndex = 0; // lastIndex doesn't get set to 0 so we have to.
+ var match;
+
+ while (match = braces.exec(pattern)) {
+ var pos = match.index;
+ if (match[0] == '}') {
+ var brace = braceStack.pop();
+ goog.asserts.assert(goog.isDef(brace) && brace == '{',
+ 'No matching { for }.');
+
+ if (braceStack.length == 0) {
+ // End of the block.
+ var part = {};
+ part.type = goog.i18n.MessageFormat.Element_.BLOCK;
+ part.value = pattern.substring(prevPos, pos);
+ results.push(part);
+ prevPos = pos + 1;
+ inBlock = false;
+ }
+ } else {
+ if (braceStack.length == 0) {
+ inBlock = true;
+ var substring = pattern.substring(prevPos, pos);
+ if (substring != '') {
+ results.push({
+ type: goog.i18n.MessageFormat.Element_.STRING,
+ value: substring
+ });
+ }
+ prevPos = pos + 1;
+ }
+ braceStack.push('{');
+ }
+ }
+
+ // Take care of the final string, and check if the braceStack is empty.
+ goog.asserts.assert(braceStack.length == 0,
+ 'There are mismatched { or } in the pattern.');
+
+ var substring = pattern.substring(prevPos);
+ if (substring != '') {
+ results.push({
+ type: goog.i18n.MessageFormat.Element_.STRING,
+ value: substring
+ });
+ }
+
+ return results;
+};
+
+
+/**
+ * A regular expression to parse the plural block, extracting the argument
+ * index and offset (if any).
+ * @type {RegExp}
+ * @private
+ */
+goog.i18n.MessageFormat.PLURAL_BLOCK_RE_ =
+ /^\s*(\w+)\s*,\s*plural\s*,(?:\s*offset:(\d+))?/;
+
+
+/**
+ * A regular expression to parse the ordinal block, extracting the argument
+ * index.
+ * @type {RegExp}
+ * @private
+ */
+goog.i18n.MessageFormat.ORDINAL_BLOCK_RE_ = /^\s*(\w+)\s*,\s*selectordinal\s*,/;
+
+
+/**
+ * A regular expression to parse the select block, extracting the argument
+ * index.
+ * @type {RegExp}
+ * @private
+ */
+goog.i18n.MessageFormat.SELECT_BLOCK_RE_ = /^\s*(\w+)\s*,\s*select\s*,/;
+
+
+/**
+ * Detects which type of a block is the pattern.
+ * @param {string} pattern Content of the block.
+ * @return {goog.i18n.MessageFormat.BlockType_} One of the block types.
+ * @private
+ */
+goog.i18n.MessageFormat.prototype.parseBlockType_ = function(pattern) {
+ if (goog.i18n.MessageFormat.PLURAL_BLOCK_RE_.test(pattern)) {
+ return goog.i18n.MessageFormat.BlockType_.PLURAL;
+ }
+
+ if (goog.i18n.MessageFormat.ORDINAL_BLOCK_RE_.test(pattern)) {
+ return goog.i18n.MessageFormat.BlockType_.ORDINAL;
+ }
+
+ if (goog.i18n.MessageFormat.SELECT_BLOCK_RE_.test(pattern)) {
+ return goog.i18n.MessageFormat.BlockType_.SELECT;
+ }
+
+ if (/^\s*\w+\s*/.test(pattern)) {
+ return goog.i18n.MessageFormat.BlockType_.SIMPLE;
+ }
+
+ return goog.i18n.MessageFormat.BlockType_.UNKNOWN;
+};
+
+
+/**
+ * Parses generic block.
+ * @param {string} pattern Content of the block to parse.
+ * @return {!Array.<!Object>} Subblocks marked as strings, select...
+ * @private
+ */
+goog.i18n.MessageFormat.prototype.parseBlock_ = function(pattern) {
+ var result = [];
+ var parts = this.extractParts_(pattern);
+ for (var i = 0; i < parts.length; i++) {
+ var block = {};
+ if (goog.i18n.MessageFormat.Element_.STRING == parts[i].type) {
+ block.type = goog.i18n.MessageFormat.BlockType_.STRING;
+ block.value = parts[i].value;
+ } else if (goog.i18n.MessageFormat.Element_.BLOCK == parts[i].type) {
+ var blockType = this.parseBlockType_(parts[i].value);
+
+ switch (blockType) {
+ case goog.i18n.MessageFormat.BlockType_.SELECT:
+ block.type = goog.i18n.MessageFormat.BlockType_.SELECT;
+ block.value = this.parseSelectBlock_(parts[i].value);
+ break;
+ case goog.i18n.MessageFormat.BlockType_.PLURAL:
+ block.type = goog.i18n.MessageFormat.BlockType_.PLURAL;
+ block.value = this.parsePluralBlock_(parts[i].value);
+ break;
+ case goog.i18n.MessageFormat.BlockType_.ORDINAL:
+ block.type = goog.i18n.MessageFormat.BlockType_.ORDINAL;
+ block.value = this.parseOrdinalBlock_(parts[i].value);
+ break;
+ case goog.i18n.MessageFormat.BlockType_.SIMPLE:
+ block.type = goog.i18n.MessageFormat.BlockType_.SIMPLE;
+ block.value = parts[i].value;
+ break;
+ default:
+ goog.asserts.fail('Unknown block type.');
+ }
+ } else {
+ goog.asserts.fail('Unknown part of the pattern.');
+ }
+ result.push(block);
+ }
+
+ return result;
+};
+
+
+/**
+ * Parses a select type of a block and produces JSON object for it.
+ * @param {string} pattern Subpattern that needs to be parsed as select pattern.
+ * @return {!Object} Object with select block info.
+ * @private
+ */
+goog.i18n.MessageFormat.prototype.parseSelectBlock_ = function(pattern) {
+ var argumentIndex = '';
+ var replaceRegex = goog.i18n.MessageFormat.SELECT_BLOCK_RE_;
+ pattern = pattern.replace(replaceRegex, function(string, name) {
+ argumentIndex = name;
+ return '';
+ });
+ var result = {};
+ result.argumentIndex = argumentIndex;
+
+ var parts = this.extractParts_(pattern);
+ // Looking for (key block)+ sequence. One of the keys has to be "other".
+ var pos = 0;
+ while (pos < parts.length) {
+ var key = parts[pos].value;
+ goog.asserts.assertString(key, 'Missing select key element.');
+
+ pos++;
+ goog.asserts.assert(pos < parts.length,
+ 'Missing or invalid select value element.');
+
+ if (goog.i18n.MessageFormat.Element_.BLOCK == parts[pos].type) {
+ var value = this.parseBlock_(parts[pos].value);
+ } else {
+ goog.asserts.fail('Expected block type.');
+ }
+ result[key.replace(/\s/g, '')] = value;
+ pos++;
+ }
+
+ goog.asserts.assertArray(result[goog.i18n.MessageFormat.OTHER_],
+ 'Missing other key in select statement.');
+ return result;
+};
+
+
+/**
+ * Parses a plural type of a block and produces JSON object for it.
+ * @param {string} pattern Subpattern that needs to be parsed as plural pattern.
+ * @return {!Object} Object with select block info.
+ * @private
+ */
+goog.i18n.MessageFormat.prototype.parsePluralBlock_ = function(pattern) {
+ var argumentIndex = '';
+ var argumentOffset = 0;
+ var replaceRegex = goog.i18n.MessageFormat.PLURAL_BLOCK_RE_;
+ pattern = pattern.replace(replaceRegex, function(string, name, offset) {
+ argumentIndex = name;
+ if (offset) {
+ argumentOffset = parseInt(offset, 10);
+ }
+ return '';
+ });
+
+ var result = {};
+ result.argumentIndex = argumentIndex;
+ result.argumentOffset = argumentOffset;
+
+ var parts = this.extractParts_(pattern);
+ // Looking for (key block)+ sequence.
+ var pos = 0;
+ while (pos < parts.length) {
+ var key = parts[pos].value;
+ goog.asserts.assertString(key, 'Missing plural key element.');
+
+ pos++;
+ goog.asserts.assert(pos < parts.length,
+ 'Missing or invalid plural value element.');
+
+ if (goog.i18n.MessageFormat.Element_.BLOCK == parts[pos].type) {
+ var value = this.parseBlock_(parts[pos].value);
+ } else {
+ goog.asserts.fail('Expected block type.');
+ }
+ result[key.replace(/\s*(?:=)?(\w+)\s*/, '$1')] = value;
+ pos++;
+ }
+
+ goog.asserts.assertArray(result[goog.i18n.MessageFormat.OTHER_],
+ 'Missing other key in plural statement.');
+
+ return result;
+};
+
+
+/**
+ * Parses an ordinal type of a block and produces JSON object for it.
+ * For example the input string:
+ * '{FOO, selectordinal, one {Message A}other {Message B}}'
+ * Should result in the output object:
+ * {
+ * argumentIndex: 'FOO',
+ * argumentOffest: 0,
+ * one: [ { type: 4, value: 'Message A' } ],
+ * other: [ { type: 4, value: 'Message B' } ]
+ * }
+ * @param {string} pattern Subpattern that needs to be parsed as plural pattern.
+ * @return {!Object} Object with select block info.
+ * @private
+ */
+goog.i18n.MessageFormat.prototype.parseOrdinalBlock_ = function(pattern) {
+ var argumentIndex = '';
+ var replaceRegex = goog.i18n.MessageFormat.ORDINAL_BLOCK_RE_;
+ pattern = pattern.replace(replaceRegex, function(string, name) {
+ argumentIndex = name;
+ return '';
+ });
+
+ var result = {};
+ result.argumentIndex = argumentIndex;
+ result.argumentOffset = 0;
+
+ var parts = this.extractParts_(pattern);
+ // Looking for (key block)+ sequence.
+ var pos = 0;
+ while (pos < parts.length) {
+ var key = parts[pos].value;
+ goog.asserts.assertString(key, 'Missing ordinal key element.');
+
+ pos++;
+ goog.asserts.assert(pos < parts.length,
+ 'Missing or invalid ordinal value element.');
+
+ if (goog.i18n.MessageFormat.Element_.BLOCK == parts[pos].type) {
+ var value = this.parseBlock_(parts[pos].value);
+ } else {
+ goog.asserts.fail('Expected block type.');
+ }
+ result[key.replace(/\s*(?:=)?(\w+)\s*/, '$1')] = value;
+ pos++;
+ }
+
+ goog.asserts.assertArray(result[goog.i18n.MessageFormat.OTHER_],
+ 'Missing other key in selectordinal statement.');
+
+ return result;
+};
+
+
+/**
+ * Builds a placeholder from the last index of the array.
+ * @param {!Array} literals All literals encountered during parse.
+ * @return {string} \uFDDF_ + last index + _.
+ * @private
+ */
+goog.i18n.MessageFormat.prototype.buildPlaceholder_ = function(literals) {
+ goog.asserts.assert(literals.length > 0, 'Literal array is empty.');
+
+ var index = (literals.length - 1).toString(10);
+ return goog.i18n.MessageFormat.LITERAL_PLACEHOLDER_ + index + '_';
+};