kopia lustrzana https://gitlab.com/gridtracker.org/gridtracker
added jquery.i18n
rodzic
9d31645823
commit
aa65ad9936
|
@ -0,0 +1,684 @@
|
||||||
|
/**
|
||||||
|
* cldrpluralparser.js
|
||||||
|
* A parser engine for CLDR plural rules.
|
||||||
|
*
|
||||||
|
* Copyright 2012-2014 Santhosh Thottingal and other contributors
|
||||||
|
* Released under the MIT license
|
||||||
|
* http://opensource.org/licenses/MIT
|
||||||
|
*
|
||||||
|
* @source https://github.com/santhoshtr/CLDRPluralRuleParser
|
||||||
|
* @author Santhosh Thottingal <santhosh.thottingal@gmail.com>
|
||||||
|
* @author Timo Tijhof
|
||||||
|
* @author Amir Aharoni
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluates a plural rule in CLDR syntax for a number
|
||||||
|
* @param {string} rule
|
||||||
|
* @param {integer} number
|
||||||
|
* @return {boolean} true if evaluation passed, false if evaluation failed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// UMD returnExports https://github.com/umdjs/umd/blob/master/returnExports.js
|
||||||
|
(function(root, factory)
|
||||||
|
{
|
||||||
|
if (typeof define === "function" && define.amd)
|
||||||
|
{
|
||||||
|
// AMD. Register as an anonymous module.
|
||||||
|
define(factory);
|
||||||
|
}
|
||||||
|
else if (typeof exports === "object")
|
||||||
|
{
|
||||||
|
// Node. Does not work with strict CommonJS, but
|
||||||
|
// only CommonJS-like environments that support module.exports,
|
||||||
|
// like Node.
|
||||||
|
module.exports = factory();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Browser globals (root is window)
|
||||||
|
root.pluralRuleParser = factory();
|
||||||
|
}
|
||||||
|
}(this, function()
|
||||||
|
{
|
||||||
|
function pluralRuleParser(rule, number)
|
||||||
|
{
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/*
|
||||||
|
Syntax: see http://unicode.org/reports/tr35/#Language_Plural_Rules
|
||||||
|
-----------------------------------------------------------------
|
||||||
|
condition = and_condition ('or' and_condition)*
|
||||||
|
('@integer' samples)?
|
||||||
|
('@decimal' samples)?
|
||||||
|
and_condition = relation ('and' relation)*
|
||||||
|
relation = is_relation | in_relation | within_relation
|
||||||
|
is_relation = expr 'is' ('not')? value
|
||||||
|
in_relation = expr (('not')? 'in' | '=' | '!=') range_list
|
||||||
|
within_relation = expr ('not')? 'within' range_list
|
||||||
|
expr = operand (('mod' | '%') value)?
|
||||||
|
operand = 'n' | 'i' | 'f' | 't' | 'v' | 'w'
|
||||||
|
range_list = (range | value) (',' range_list)*
|
||||||
|
value = digit+
|
||||||
|
digit = 0|1|2|3|4|5|6|7|8|9
|
||||||
|
range = value'..'value
|
||||||
|
samples = sampleRange (',' sampleRange)* (',' ('…'|'...'))?
|
||||||
|
sampleRange = decimalValue '~' decimalValue
|
||||||
|
decimalValue = value ('.' value)?
|
||||||
|
*/
|
||||||
|
|
||||||
|
// We don't evaluate the samples section of the rule. Ignore it.
|
||||||
|
rule = rule.split("@")[0].replace(/^\s*/, "").replace(/\s*$/, "");
|
||||||
|
|
||||||
|
if (!rule.length)
|
||||||
|
{
|
||||||
|
// Empty rule or 'other' rule.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Indicates the current position in the rule as we parse through it.
|
||||||
|
// Shared among all parsing functions below.
|
||||||
|
var pos = 0,
|
||||||
|
operand,
|
||||||
|
expression,
|
||||||
|
relation,
|
||||||
|
result,
|
||||||
|
whitespace = makeRegexParser(/^\s+/),
|
||||||
|
value = makeRegexParser(/^\d+/),
|
||||||
|
_n_ = makeStringParser("n"),
|
||||||
|
_i_ = makeStringParser("i"),
|
||||||
|
_f_ = makeStringParser("f"),
|
||||||
|
_t_ = makeStringParser("t"),
|
||||||
|
_v_ = makeStringParser("v"),
|
||||||
|
_w_ = makeStringParser("w"),
|
||||||
|
_is_ = makeStringParser("is"),
|
||||||
|
_isnot_ = makeStringParser("is not"),
|
||||||
|
_isnot_sign_ = makeStringParser("!="),
|
||||||
|
_equal_ = makeStringParser("="),
|
||||||
|
_mod_ = makeStringParser("mod"),
|
||||||
|
_percent_ = makeStringParser("%"),
|
||||||
|
_not_ = makeStringParser("not"),
|
||||||
|
_in_ = makeStringParser("in"),
|
||||||
|
_within_ = makeStringParser("within"),
|
||||||
|
_range_ = makeStringParser(".."),
|
||||||
|
_comma_ = makeStringParser(","),
|
||||||
|
_or_ = makeStringParser("or"),
|
||||||
|
_and_ = makeStringParser("and");
|
||||||
|
|
||||||
|
function debug()
|
||||||
|
{
|
||||||
|
// console.log.apply(console, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
debug("pluralRuleParser", rule, number);
|
||||||
|
|
||||||
|
// Try parsers until one works, if none work return null
|
||||||
|
function choice(parserSyntax)
|
||||||
|
{
|
||||||
|
return function()
|
||||||
|
{
|
||||||
|
var i, result;
|
||||||
|
|
||||||
|
for (i = 0; i < parserSyntax.length; i++)
|
||||||
|
{
|
||||||
|
result = parserSyntax[i]();
|
||||||
|
|
||||||
|
if (result !== null)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try several parserSyntax-es in a row.
|
||||||
|
// All must succeed; otherwise, return null.
|
||||||
|
// This is the only eager one.
|
||||||
|
function sequence(parserSyntax)
|
||||||
|
{
|
||||||
|
var i, parserRes,
|
||||||
|
originalPos = pos,
|
||||||
|
result = [];
|
||||||
|
|
||||||
|
for (i = 0; i < parserSyntax.length; i++)
|
||||||
|
{
|
||||||
|
parserRes = parserSyntax[i]();
|
||||||
|
|
||||||
|
if (parserRes === null)
|
||||||
|
{
|
||||||
|
pos = originalPos;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push(parserRes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the same parser over and over until it fails.
|
||||||
|
// Must succeed a minimum of n times; otherwise, return null.
|
||||||
|
function nOrMore(n, p)
|
||||||
|
{
|
||||||
|
return function()
|
||||||
|
{
|
||||||
|
var originalPos = pos,
|
||||||
|
result = [],
|
||||||
|
parsed = p();
|
||||||
|
|
||||||
|
while (parsed !== null)
|
||||||
|
{
|
||||||
|
result.push(parsed);
|
||||||
|
parsed = p();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.length < n)
|
||||||
|
{
|
||||||
|
pos = originalPos;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helpers - just make parserSyntax out of simpler JS builtin types
|
||||||
|
function makeStringParser(s)
|
||||||
|
{
|
||||||
|
var len = s.length;
|
||||||
|
|
||||||
|
return function()
|
||||||
|
{
|
||||||
|
var result = null;
|
||||||
|
|
||||||
|
if (rule.substr(pos, len) === s)
|
||||||
|
{
|
||||||
|
result = s;
|
||||||
|
pos += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeRegexParser(regex)
|
||||||
|
{
|
||||||
|
return function()
|
||||||
|
{
|
||||||
|
var matches = rule.substr(pos).match(regex);
|
||||||
|
|
||||||
|
if (matches === null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos += matches[0].length;
|
||||||
|
|
||||||
|
return matches[0];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integer digits of n.
|
||||||
|
*/
|
||||||
|
function i()
|
||||||
|
{
|
||||||
|
var result = _i_();
|
||||||
|
|
||||||
|
if (result === null)
|
||||||
|
{
|
||||||
|
debug(" -- failed i", parseInt(number, 10));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = parseInt(number, 10);
|
||||||
|
debug(" -- passed i ", result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Absolute value of the source number (integer and decimals).
|
||||||
|
*/
|
||||||
|
function n()
|
||||||
|
{
|
||||||
|
var result = _n_();
|
||||||
|
|
||||||
|
if (result === null)
|
||||||
|
{
|
||||||
|
debug(" -- failed n ", number);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = parseFloat(number, 10);
|
||||||
|
debug(" -- passed n ", result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visible fractional digits in n, with trailing zeros.
|
||||||
|
*/
|
||||||
|
function f()
|
||||||
|
{
|
||||||
|
var result = _f_();
|
||||||
|
|
||||||
|
if (result === null)
|
||||||
|
{
|
||||||
|
debug(" -- failed f ", number);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = (number + ".").split(".")[1] || 0;
|
||||||
|
debug(" -- passed f ", result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visible fractional digits in n, without trailing zeros.
|
||||||
|
*/
|
||||||
|
function t()
|
||||||
|
{
|
||||||
|
var result = _t_();
|
||||||
|
|
||||||
|
if (result === null)
|
||||||
|
{
|
||||||
|
debug(" -- failed t ", number);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = (number + ".").split(".")[1].replace(/0$/, "") || 0;
|
||||||
|
debug(" -- passed t ", result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of visible fraction digits in n, with trailing zeros.
|
||||||
|
*/
|
||||||
|
function v()
|
||||||
|
{
|
||||||
|
var result = _v_();
|
||||||
|
|
||||||
|
if (result === null)
|
||||||
|
{
|
||||||
|
debug(" -- failed v ", number);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = (number + ".").split(".")[1].length || 0;
|
||||||
|
debug(" -- passed v ", result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of visible fraction digits in n, without trailing zeros.
|
||||||
|
*/
|
||||||
|
function w()
|
||||||
|
{
|
||||||
|
var result = _w_();
|
||||||
|
|
||||||
|
if (result === null)
|
||||||
|
{
|
||||||
|
debug(" -- failed w ", number);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = (number + ".").split(".")[1].replace(/0$/, "").length || 0;
|
||||||
|
debug(" -- passed w ", result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// operand = 'n' | 'i' | 'f' | 't' | 'v' | 'w'
|
||||||
|
operand = choice([n, i, f, t, v, w]);
|
||||||
|
|
||||||
|
// expr = operand (('mod' | '%') value)?
|
||||||
|
expression = choice([mod, operand]);
|
||||||
|
|
||||||
|
function mod()
|
||||||
|
{
|
||||||
|
var result = sequence(
|
||||||
|
[operand, whitespace, choice([_mod_, _percent_]), whitespace, value]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result === null)
|
||||||
|
{
|
||||||
|
debug(" -- failed mod");
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
debug(" -- passed " + parseInt(result[0], 10) + " " + result[2] + " " + parseInt(result[4], 10));
|
||||||
|
|
||||||
|
return parseFloat(result[0]) % parseInt(result[4], 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
function not()
|
||||||
|
{
|
||||||
|
var result = sequence([whitespace, _not_]);
|
||||||
|
|
||||||
|
if (result === null)
|
||||||
|
{
|
||||||
|
debug(" -- failed not");
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// is_relation = expr 'is' ('not')? value
|
||||||
|
function is()
|
||||||
|
{
|
||||||
|
var result = sequence([expression, whitespace, choice([_is_]), whitespace, value]);
|
||||||
|
|
||||||
|
if (result !== null)
|
||||||
|
{
|
||||||
|
debug(" -- passed is : " + result[0] + " == " + parseInt(result[4], 10));
|
||||||
|
|
||||||
|
return result[0] === parseInt(result[4], 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
debug(" -- failed is");
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// is_relation = expr 'is' ('not')? value
|
||||||
|
function isnot()
|
||||||
|
{
|
||||||
|
var result = sequence(
|
||||||
|
[expression, whitespace, choice([_isnot_, _isnot_sign_]), whitespace, value]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result !== null)
|
||||||
|
{
|
||||||
|
debug(" -- passed isnot: " + result[0] + " != " + parseInt(result[4], 10));
|
||||||
|
|
||||||
|
return result[0] !== parseInt(result[4], 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
debug(" -- failed isnot");
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function not_in()
|
||||||
|
{
|
||||||
|
var i, range_list,
|
||||||
|
result = sequence([expression, whitespace, _isnot_sign_, whitespace, rangeList]);
|
||||||
|
|
||||||
|
if (result !== null)
|
||||||
|
{
|
||||||
|
debug(" -- passed not_in: " + result[0] + " != " + result[4]);
|
||||||
|
range_list = result[4];
|
||||||
|
|
||||||
|
for (i = 0; i < range_list.length; i++)
|
||||||
|
{
|
||||||
|
if (parseInt(range_list[i], 10) === parseInt(result[0], 10))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
debug(" -- failed not_in");
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// range_list = (range | value) (',' range_list)*
|
||||||
|
function rangeList()
|
||||||
|
{
|
||||||
|
var result = sequence([choice([range, value]), nOrMore(0, rangeTail)]),
|
||||||
|
resultList = [];
|
||||||
|
|
||||||
|
if (result !== null)
|
||||||
|
{
|
||||||
|
resultList = resultList.concat(result[0]);
|
||||||
|
|
||||||
|
if (result[1][0])
|
||||||
|
{
|
||||||
|
resultList = resultList.concat(result[1][0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultList;
|
||||||
|
}
|
||||||
|
|
||||||
|
debug(" -- failed rangeList");
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function rangeTail()
|
||||||
|
{
|
||||||
|
// ',' range_list
|
||||||
|
var result = sequence([_comma_, rangeList]);
|
||||||
|
|
||||||
|
if (result !== null)
|
||||||
|
{
|
||||||
|
return result[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
debug(" -- failed rangeTail");
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// range = value'..'value
|
||||||
|
function range()
|
||||||
|
{
|
||||||
|
var i, array, left, right,
|
||||||
|
result = sequence([value, _range_, value]);
|
||||||
|
|
||||||
|
if (result !== null)
|
||||||
|
{
|
||||||
|
debug(" -- passed range");
|
||||||
|
|
||||||
|
array = [];
|
||||||
|
left = parseInt(result[0], 10);
|
||||||
|
right = parseInt(result[2], 10);
|
||||||
|
|
||||||
|
for (i = left; i <= right; i++)
|
||||||
|
{
|
||||||
|
array.push(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
debug(" -- failed range");
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _in()
|
||||||
|
{
|
||||||
|
var result, range_list, i;
|
||||||
|
|
||||||
|
// in_relation = expr ('not')? 'in' range_list
|
||||||
|
result = sequence(
|
||||||
|
[expression, nOrMore(0, not), whitespace, choice([_in_, _equal_]), whitespace, rangeList]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result !== null)
|
||||||
|
{
|
||||||
|
debug(" -- passed _in:" + result);
|
||||||
|
|
||||||
|
range_list = result[5];
|
||||||
|
|
||||||
|
for (i = 0; i < range_list.length; i++)
|
||||||
|
{
|
||||||
|
if (parseInt(range_list[i], 10) === parseFloat(result[0]))
|
||||||
|
{
|
||||||
|
return (result[1][0] !== "not");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (result[1][0] === "not");
|
||||||
|
}
|
||||||
|
|
||||||
|
debug(" -- failed _in ");
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The difference between "in" and "within" is that
|
||||||
|
* "in" only includes integers in the specified range,
|
||||||
|
* while "within" includes all values.
|
||||||
|
*/
|
||||||
|
function within()
|
||||||
|
{
|
||||||
|
var range_list, result;
|
||||||
|
|
||||||
|
// within_relation = expr ('not')? 'within' range_list
|
||||||
|
result = sequence(
|
||||||
|
[expression, nOrMore(0, not), whitespace, _within_, whitespace, rangeList]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result !== null)
|
||||||
|
{
|
||||||
|
debug(" -- passed within");
|
||||||
|
|
||||||
|
range_list = result[5];
|
||||||
|
|
||||||
|
if ((result[0] >= parseInt(range_list[0], 10)) &&
|
||||||
|
(result[0] < parseInt(range_list[range_list.length - 1], 10)))
|
||||||
|
{
|
||||||
|
return (result[1][0] !== "not");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (result[1][0] === "not");
|
||||||
|
}
|
||||||
|
|
||||||
|
debug(" -- failed within ");
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// relation = is_relation | in_relation | within_relation
|
||||||
|
relation = choice([is, not_in, isnot, _in, within]);
|
||||||
|
|
||||||
|
// and_condition = relation ('and' relation)*
|
||||||
|
function and()
|
||||||
|
{
|
||||||
|
var i,
|
||||||
|
result = sequence([relation, nOrMore(0, andTail)]);
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
if (!result[0])
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < result[1].length; i++)
|
||||||
|
{
|
||||||
|
if (!result[1][i])
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
debug(" -- failed and");
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ('and' relation)*
|
||||||
|
function andTail()
|
||||||
|
{
|
||||||
|
var result = sequence([whitespace, _and_, whitespace, relation]);
|
||||||
|
|
||||||
|
if (result !== null)
|
||||||
|
{
|
||||||
|
debug(" -- passed andTail" + result);
|
||||||
|
|
||||||
|
return result[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
debug(" -- failed andTail");
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// ('or' and_condition)*
|
||||||
|
function orTail()
|
||||||
|
{
|
||||||
|
var result = sequence([whitespace, _or_, whitespace, and]);
|
||||||
|
|
||||||
|
if (result !== null)
|
||||||
|
{
|
||||||
|
debug(" -- passed orTail: " + result[3]);
|
||||||
|
|
||||||
|
return result[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
debug(" -- failed orTail");
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// condition = and_condition ('or' and_condition)*
|
||||||
|
function condition()
|
||||||
|
{
|
||||||
|
var i,
|
||||||
|
result = sequence([and, nOrMore(0, orTail)]);
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
for (i = 0; i < result[1].length; i++)
|
||||||
|
{
|
||||||
|
if (result[1][i])
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = condition();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For success, the pos must have gotten to the end of the rule
|
||||||
|
* and returned a non-null.
|
||||||
|
* n.b. This is part of language infrastructure,
|
||||||
|
* so we do not throw an internationalizable message.
|
||||||
|
*/
|
||||||
|
if (result === null)
|
||||||
|
{
|
||||||
|
throw new Error("Parse error at position " + pos.toString() + " for rule: " + rule);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos !== rule.length)
|
||||||
|
{
|
||||||
|
debug("Warning: Rule not parsed completely. Parser stopped at " + rule.substr(0, pos) + " for rule: " + rule);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pluralRuleParser;
|
||||||
|
}));
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,185 @@
|
||||||
|
/*!
|
||||||
|
* jQuery Internationalization library
|
||||||
|
*
|
||||||
|
* Copyright (C) 2011-2013 Santhosh Thottingal, Neil Kandalgaonkar
|
||||||
|
*
|
||||||
|
* jquery.i18n is dual licensed GPLv2 or later and MIT. You don't have to do
|
||||||
|
* anything special to choose one license or the other and you don't have to
|
||||||
|
* notify anyone which license you are using. You are free to use
|
||||||
|
* UniversalLanguageSelector in commercial projects as long as the copyright
|
||||||
|
* header is left intact. See files GPL-LICENSE and MIT-LICENSE for details.
|
||||||
|
*
|
||||||
|
* @licence GNU General Public Licence 2.0 or later
|
||||||
|
* @licence MIT License
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function ($)
|
||||||
|
{
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var MessageParserEmitter = function ()
|
||||||
|
{
|
||||||
|
this.language = $.i18n.languages[String.locale] || $.i18n.languages.default;
|
||||||
|
};
|
||||||
|
|
||||||
|
MessageParserEmitter.prototype = {
|
||||||
|
constructor: MessageParserEmitter,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (We put this method definition here, and not in prototype, to make
|
||||||
|
* sure it's not overwritten by any magic.) Walk entire node structure,
|
||||||
|
* applying replacements and template functions when appropriate
|
||||||
|
*
|
||||||
|
* @param {Mixed} node abstract syntax tree (top node or subnode)
|
||||||
|
* @param {Array} replacements for $1, $2, ... $n
|
||||||
|
* @return {Mixed} single-string node or array of nodes suitable for
|
||||||
|
* jQuery appending.
|
||||||
|
*/
|
||||||
|
emit: function (node, replacements)
|
||||||
|
{
|
||||||
|
var ret, subnodes, operation,
|
||||||
|
messageParserEmitter = this;
|
||||||
|
|
||||||
|
switch (typeof node)
|
||||||
|
{
|
||||||
|
case "string":
|
||||||
|
case "number":
|
||||||
|
ret = node;
|
||||||
|
break;
|
||||||
|
case "object":
|
||||||
|
// node is an array of nodes
|
||||||
|
subnodes = $.map(node.slice(1), function (n)
|
||||||
|
{
|
||||||
|
return messageParserEmitter.emit(n, replacements);
|
||||||
|
});
|
||||||
|
|
||||||
|
operation = node[0].toLowerCase();
|
||||||
|
|
||||||
|
if (typeof messageParserEmitter[operation] === "function")
|
||||||
|
{
|
||||||
|
ret = messageParserEmitter[operation](subnodes, replacements);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Error("unknown operation \"" + operation + "\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "undefined":
|
||||||
|
// Parsing the empty string (as an entire expression, or as a
|
||||||
|
// paramExpression in a template) results in undefined
|
||||||
|
// Perhaps a more clever parser can detect this, and return the
|
||||||
|
// empty string? Or is that useful information?
|
||||||
|
// The logical thing is probably to return the empty string here
|
||||||
|
// when we encounter undefined.
|
||||||
|
ret = "";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error("unexpected type in AST: " + typeof node);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parsing has been applied depth-first we can assume that all nodes
|
||||||
|
* here are single nodes Must return a single node to parents -- a
|
||||||
|
* jQuery with synthetic span However, unwrap any other synthetic spans
|
||||||
|
* in our children and pass them upwards
|
||||||
|
*
|
||||||
|
* @param {Array} nodes Mixed, some single nodes, some arrays of nodes.
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
concat: function (nodes)
|
||||||
|
{
|
||||||
|
var result = "";
|
||||||
|
|
||||||
|
$.each(nodes, function (i, node)
|
||||||
|
{
|
||||||
|
// strings, integers, anything else
|
||||||
|
result += node;
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return escaped replacement of correct index, or string if
|
||||||
|
* unavailable. Note that we expect the parsed parameter to be
|
||||||
|
* zero-based. i.e. $1 should have become [ 0 ]. if the specified
|
||||||
|
* parameter is not found return the same string (e.g. "$99" ->
|
||||||
|
* parameter 98 -> not found -> return "$99" ) TODO throw error if
|
||||||
|
* nodes.length > 1 ?
|
||||||
|
*
|
||||||
|
* @param {Array} nodes One element, integer, n >= 0
|
||||||
|
* @param {Array} replacements for $1, $2, ... $n
|
||||||
|
* @return {string} replacement
|
||||||
|
*/
|
||||||
|
replace: function (nodes, replacements)
|
||||||
|
{
|
||||||
|
var index = parseInt(nodes[0], 10);
|
||||||
|
|
||||||
|
if (index < replacements.length)
|
||||||
|
{
|
||||||
|
// replacement is not a string, don't touch!
|
||||||
|
return replacements[index];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// index not found, fallback to displaying variable
|
||||||
|
return "$" + (index + 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform parsed structure into pluralization n.b. The first node may
|
||||||
|
* be a non-integer (for instance, a string representing an Arabic
|
||||||
|
* number). So convert it back with the current language's
|
||||||
|
* convertNumber.
|
||||||
|
*
|
||||||
|
* @param {Array} nodes List [ {String|Number}, {String}, {String} ... ]
|
||||||
|
* @return {string} selected pluralized form according to current
|
||||||
|
* language.
|
||||||
|
*/
|
||||||
|
plural: function (nodes)
|
||||||
|
{
|
||||||
|
var count = parseFloat(this.language.convertNumber(nodes[0], 10)),
|
||||||
|
forms = nodes.slice(1);
|
||||||
|
|
||||||
|
return forms.length ? this.language.convertPlural(count, forms) : "";
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform parsed structure into gender Usage
|
||||||
|
* {{gender:gender|masculine|feminine|neutral}}.
|
||||||
|
*
|
||||||
|
* @param {Array} nodes List [ {String}, {String}, {String} , {String} ]
|
||||||
|
* @return {string} selected gender form according to current language
|
||||||
|
*/
|
||||||
|
gender: function (nodes)
|
||||||
|
{
|
||||||
|
var gender = nodes[0],
|
||||||
|
forms = nodes.slice(1);
|
||||||
|
|
||||||
|
return this.language.gender(gender, forms);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform parsed structure into grammar conversion. Invoked by
|
||||||
|
* putting {{grammar:form|word}} in a message
|
||||||
|
*
|
||||||
|
* @param {Array} nodes List [{Grammar case eg: genitive}, {String word}]
|
||||||
|
* @return {string} selected grammatical form according to current
|
||||||
|
* language.
|
||||||
|
*/
|
||||||
|
grammar: function (nodes)
|
||||||
|
{
|
||||||
|
var form = nodes[0],
|
||||||
|
word = nodes[1];
|
||||||
|
|
||||||
|
return word && form && this.language.convertGrammar(word, form);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$.extend($.i18n.parser.emitter, new MessageParserEmitter());
|
||||||
|
}(jQuery));
|
|
@ -0,0 +1,187 @@
|
||||||
|
/*!
|
||||||
|
* jQuery Internationalization library
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Santhosh Thottingal
|
||||||
|
*
|
||||||
|
* jquery.i18n is dual licensed GPLv2 or later and MIT. You don't have to do anything special to
|
||||||
|
* choose one license or the other and you don't have to notify anyone which license you are using.
|
||||||
|
* You are free to use UniversalLanguageSelector in commercial projects as long as the copyright
|
||||||
|
* header is left intact. See files GPL-LICENSE and MIT-LICENSE for details.
|
||||||
|
*
|
||||||
|
* @licence GNU General Public Licence 2.0 or later
|
||||||
|
* @licence MIT License
|
||||||
|
*/
|
||||||
|
(function ($)
|
||||||
|
{
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
$.i18n = $.i18n || {};
|
||||||
|
$.extend($.i18n.fallbacks, {
|
||||||
|
ab: ["ru"],
|
||||||
|
ace: ["id"],
|
||||||
|
aln: ["sq"],
|
||||||
|
// Not so standard - als is supposed to be Tosk Albanian,
|
||||||
|
// but in Wikipedia it's used for a Germanic language.
|
||||||
|
als: ["gsw", "de"],
|
||||||
|
an: ["es"],
|
||||||
|
anp: ["hi"],
|
||||||
|
arn: ["es"],
|
||||||
|
arz: ["ar"],
|
||||||
|
av: ["ru"],
|
||||||
|
ay: ["es"],
|
||||||
|
ba: ["ru"],
|
||||||
|
bar: ["de"],
|
||||||
|
"bat-smg": ["sgs", "lt"],
|
||||||
|
bcc: ["fa"],
|
||||||
|
"be-x-old": ["be-tarask"],
|
||||||
|
bh: ["bho"],
|
||||||
|
bjn: ["id"],
|
||||||
|
bm: ["fr"],
|
||||||
|
bpy: ["bn"],
|
||||||
|
bqi: ["fa"],
|
||||||
|
bug: ["id"],
|
||||||
|
"cbk-zam": ["es"],
|
||||||
|
ce: ["ru"],
|
||||||
|
crh: ["crh-latn"],
|
||||||
|
"crh-cyrl": ["ru"],
|
||||||
|
csb: ["pl"],
|
||||||
|
cv: ["ru"],
|
||||||
|
"de-at": ["de"],
|
||||||
|
"de-ch": ["de"],
|
||||||
|
"de-formal": ["de"],
|
||||||
|
dsb: ["de"],
|
||||||
|
dtp: ["ms"],
|
||||||
|
egl: ["it"],
|
||||||
|
eml: ["it"],
|
||||||
|
ff: ["fr"],
|
||||||
|
fit: ["fi"],
|
||||||
|
"fiu-vro": ["vro", "et"],
|
||||||
|
frc: ["fr"],
|
||||||
|
frp: ["fr"],
|
||||||
|
frr: ["de"],
|
||||||
|
fur: ["it"],
|
||||||
|
gag: ["tr"],
|
||||||
|
gan: ["gan-hant", "zh-hant", "zh-hans"],
|
||||||
|
"gan-hans": ["zh-hans"],
|
||||||
|
"gan-hant": ["zh-hant", "zh-hans"],
|
||||||
|
gl: ["pt"],
|
||||||
|
glk: ["fa"],
|
||||||
|
gn: ["es"],
|
||||||
|
gsw: ["de"],
|
||||||
|
hif: ["hif-latn"],
|
||||||
|
hsb: ["de"],
|
||||||
|
ht: ["fr"],
|
||||||
|
ii: ["zh-cn", "zh-hans"],
|
||||||
|
inh: ["ru"],
|
||||||
|
iu: ["ike-cans"],
|
||||||
|
jut: ["da"],
|
||||||
|
jv: ["id"],
|
||||||
|
kaa: ["kk-latn", "kk-cyrl"],
|
||||||
|
kbd: ["kbd-cyrl"],
|
||||||
|
khw: ["ur"],
|
||||||
|
kiu: ["tr"],
|
||||||
|
kk: ["kk-cyrl"],
|
||||||
|
"kk-arab": ["kk-cyrl"],
|
||||||
|
"kk-latn": ["kk-cyrl"],
|
||||||
|
"kk-cn": ["kk-arab", "kk-cyrl"],
|
||||||
|
"kk-kz": ["kk-cyrl"],
|
||||||
|
"kk-tr": ["kk-latn", "kk-cyrl"],
|
||||||
|
kl: ["da"],
|
||||||
|
"ko-kp": ["ko"],
|
||||||
|
koi: ["ru"],
|
||||||
|
krc: ["ru"],
|
||||||
|
ks: ["ks-arab"],
|
||||||
|
ksh: ["de"],
|
||||||
|
ku: ["ku-latn"],
|
||||||
|
"ku-arab": ["ckb"],
|
||||||
|
kv: ["ru"],
|
||||||
|
lad: ["es"],
|
||||||
|
lb: ["de"],
|
||||||
|
lbe: ["ru"],
|
||||||
|
lez: ["ru"],
|
||||||
|
li: ["nl"],
|
||||||
|
lij: ["it"],
|
||||||
|
liv: ["et"],
|
||||||
|
lmo: ["it"],
|
||||||
|
ln: ["fr"],
|
||||||
|
ltg: ["lv"],
|
||||||
|
lzz: ["tr"],
|
||||||
|
mai: ["hi"],
|
||||||
|
"map-bms": ["jv", "id"],
|
||||||
|
mg: ["fr"],
|
||||||
|
mhr: ["ru"],
|
||||||
|
min: ["id"],
|
||||||
|
mo: ["ro"],
|
||||||
|
mrj: ["ru"],
|
||||||
|
mwl: ["pt"],
|
||||||
|
myv: ["ru"],
|
||||||
|
mzn: ["fa"],
|
||||||
|
nah: ["es"],
|
||||||
|
nap: ["it"],
|
||||||
|
nds: ["de"],
|
||||||
|
"nds-nl": ["nl"],
|
||||||
|
"nl-informal": ["nl"],
|
||||||
|
no: ["nb"],
|
||||||
|
os: ["ru"],
|
||||||
|
pcd: ["fr"],
|
||||||
|
pdc: ["de"],
|
||||||
|
pdt: ["de"],
|
||||||
|
pfl: ["de"],
|
||||||
|
pms: ["it"],
|
||||||
|
pt: ["pt-br"],
|
||||||
|
"pt-br": ["pt"],
|
||||||
|
qu: ["es"],
|
||||||
|
qug: ["qu", "es"],
|
||||||
|
rgn: ["it"],
|
||||||
|
rmy: ["ro"],
|
||||||
|
"roa-rup": ["rup"],
|
||||||
|
rue: ["uk", "ru"],
|
||||||
|
ruq: ["ruq-latn", "ro"],
|
||||||
|
"ruq-cyrl": ["mk"],
|
||||||
|
"ruq-latn": ["ro"],
|
||||||
|
sa: ["hi"],
|
||||||
|
sah: ["ru"],
|
||||||
|
scn: ["it"],
|
||||||
|
sg: ["fr"],
|
||||||
|
sgs: ["lt"],
|
||||||
|
sli: ["de"],
|
||||||
|
sr: ["sr-ec"],
|
||||||
|
srn: ["nl"],
|
||||||
|
stq: ["de"],
|
||||||
|
su: ["id"],
|
||||||
|
szl: ["pl"],
|
||||||
|
tcy: ["kn"],
|
||||||
|
tg: ["tg-cyrl"],
|
||||||
|
tt: ["tt-cyrl", "ru"],
|
||||||
|
"tt-cyrl": ["ru"],
|
||||||
|
ty: ["fr"],
|
||||||
|
udm: ["ru"],
|
||||||
|
ug: ["ug-arab"],
|
||||||
|
uk: ["ru"],
|
||||||
|
vec: ["it"],
|
||||||
|
vep: ["et"],
|
||||||
|
vls: ["nl"],
|
||||||
|
vmf: ["de"],
|
||||||
|
vot: ["fi"],
|
||||||
|
vro: ["et"],
|
||||||
|
wa: ["fr"],
|
||||||
|
wo: ["fr"],
|
||||||
|
wuu: ["zh-hans"],
|
||||||
|
xal: ["ru"],
|
||||||
|
xmf: ["ka"],
|
||||||
|
yi: ["he"],
|
||||||
|
za: ["zh-hans"],
|
||||||
|
zea: ["nl"],
|
||||||
|
zh: ["zh-hans"],
|
||||||
|
"zh-classical": ["lzh"],
|
||||||
|
"zh-cn": ["zh-hans"],
|
||||||
|
"zh-hant": ["zh-hans"],
|
||||||
|
"zh-hk": ["zh-hant", "zh-hans"],
|
||||||
|
"zh-min-nan": ["nan"],
|
||||||
|
"zh-mo": ["zh-hk", "zh-hant", "zh-hans"],
|
||||||
|
"zh-my": ["zh-sg", "zh-hans"],
|
||||||
|
"zh-sg": ["zh-hans"],
|
||||||
|
"zh-tw": ["zh-hant", "zh-hans"],
|
||||||
|
"zh-yue": ["yue"]
|
||||||
|
});
|
||||||
|
}(jQuery));
|
|
@ -0,0 +1,332 @@
|
||||||
|
/*!
|
||||||
|
* jQuery Internationalization library
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Santhosh Thottingal
|
||||||
|
*
|
||||||
|
* jquery.i18n is dual licensed GPLv2 or later and MIT. You don't have to do
|
||||||
|
* anything special to choose one license or the other and you don't have to
|
||||||
|
* notify anyone which license you are using. You are free to use
|
||||||
|
* UniversalLanguageSelector in commercial projects as long as the copyright
|
||||||
|
* header is left intact. See files GPL-LICENSE and MIT-LICENSE for details.
|
||||||
|
*
|
||||||
|
* @licence GNU General Public Licence 2.0 or later
|
||||||
|
* @licence MIT License
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function ($)
|
||||||
|
{
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var I18N,
|
||||||
|
slice = Array.prototype.slice;
|
||||||
|
/**
|
||||||
|
* @constructor
|
||||||
|
* @param {Object} options
|
||||||
|
*/
|
||||||
|
I18N = function (options)
|
||||||
|
{
|
||||||
|
// Load defaults
|
||||||
|
this.options = $.extend({}, I18N.defaults, options);
|
||||||
|
|
||||||
|
this.parser = this.options.parser;
|
||||||
|
this.locale = this.options.locale;
|
||||||
|
this.messageStore = this.options.messageStore;
|
||||||
|
this.languages = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
I18N.prototype = {
|
||||||
|
/**
|
||||||
|
* Localize a given messageKey to a locale.
|
||||||
|
* @param {string} messageKey
|
||||||
|
* @return {string} Localized message
|
||||||
|
*/
|
||||||
|
localize: function (messageKey)
|
||||||
|
{
|
||||||
|
var localeParts, localePartIndex, locale, fallbackIndex,
|
||||||
|
tryingLocale, message;
|
||||||
|
|
||||||
|
locale = this.locale;
|
||||||
|
fallbackIndex = 0;
|
||||||
|
|
||||||
|
while (locale)
|
||||||
|
{
|
||||||
|
// Iterate through locales starting at most-specific until
|
||||||
|
// localization is found. As in fi-Latn-FI, fi-Latn and fi.
|
||||||
|
localeParts = locale.split("-");
|
||||||
|
localePartIndex = localeParts.length;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
tryingLocale = localeParts.slice(0, localePartIndex).join("-");
|
||||||
|
message = this.messageStore.get(tryingLocale, messageKey);
|
||||||
|
|
||||||
|
if (message)
|
||||||
|
{
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
localePartIndex--;
|
||||||
|
} while (localePartIndex);
|
||||||
|
|
||||||
|
if (locale === this.options.fallbackLocale)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
locale = ($.i18n.fallbacks[this.locale] &&
|
||||||
|
$.i18n.fallbacks[this.locale][fallbackIndex]) ||
|
||||||
|
this.options.fallbackLocale;
|
||||||
|
$.i18n.log("Trying fallback locale for " + this.locale + ": " + locale + " (" + messageKey + ")");
|
||||||
|
|
||||||
|
fallbackIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// key not found
|
||||||
|
return "";
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Destroy the i18n instance.
|
||||||
|
*/
|
||||||
|
destroy: function ()
|
||||||
|
{
|
||||||
|
$.removeData(document, "i18n");
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* General message loading API This can take a URL string for
|
||||||
|
* the json formatted messages. Example:
|
||||||
|
* <code>load('path/to/all_localizations.json');</code>
|
||||||
|
*
|
||||||
|
* To load a localization file for a locale:
|
||||||
|
* <code>
|
||||||
|
* load('path/to/de-messages.json', 'de' );
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* To load a localization file from a directory:
|
||||||
|
* <code>
|
||||||
|
* load('path/to/i18n/directory', 'de' );
|
||||||
|
* </code>
|
||||||
|
* The above method has the advantage of fallback resolution.
|
||||||
|
* ie, it will automatically load the fallback locales for de.
|
||||||
|
* For most usecases, this is the recommended method.
|
||||||
|
* It is optional to have trailing slash at end.
|
||||||
|
*
|
||||||
|
* A data object containing message key- message translation mappings
|
||||||
|
* can also be passed. Example:
|
||||||
|
* <code>
|
||||||
|
* load( { 'hello' : 'Hello' }, optionalLocale );
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* A source map containing key-value pair of languagename and locations
|
||||||
|
* can also be passed. Example:
|
||||||
|
* <code>
|
||||||
|
* load( {
|
||||||
|
* bn: 'i18n/bn.json',
|
||||||
|
* he: 'i18n/he.json',
|
||||||
|
* en: 'i18n/en.json'
|
||||||
|
* } )
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* If the data argument is null/undefined/false,
|
||||||
|
* all cached messages for the i18n instance will get reset.
|
||||||
|
*
|
||||||
|
* @param {string|Object} source
|
||||||
|
* @param {string} locale Language tag
|
||||||
|
* @return {jQuery.Promise}
|
||||||
|
*/
|
||||||
|
load: function (source, locale)
|
||||||
|
{
|
||||||
|
var fallbackLocales, locIndex, fallbackLocale, sourceMap = {};
|
||||||
|
if (!source && !locale)
|
||||||
|
{
|
||||||
|
source = "i18n/" + $.i18n().locale + ".json";
|
||||||
|
locale = $.i18n().locale;
|
||||||
|
}
|
||||||
|
if (typeof source === "string" &&
|
||||||
|
// source extension should be json, but can have query params after that.
|
||||||
|
source.split("?")[0].split(".").pop() !== "json"
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Load specified locale then check for fallbacks when directory is
|
||||||
|
// specified in load()
|
||||||
|
sourceMap[locale] = source + "/" + locale + ".json";
|
||||||
|
fallbackLocales = ($.i18n.fallbacks[locale] || [])
|
||||||
|
.concat(this.options.fallbackLocale);
|
||||||
|
for (locIndex = 0; locIndex < fallbackLocales.length; locIndex++)
|
||||||
|
{
|
||||||
|
fallbackLocale = fallbackLocales[locIndex];
|
||||||
|
sourceMap[fallbackLocale] = source + "/" + fallbackLocale + ".json";
|
||||||
|
}
|
||||||
|
return this.load(sourceMap);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return this.messageStore.load(source, locale);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does parameter and magic word substitution.
|
||||||
|
*
|
||||||
|
* @param {string} key Message key
|
||||||
|
* @param {Array} parameters Message parameters
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
parse: function (key, parameters)
|
||||||
|
{
|
||||||
|
var message = this.localize(key);
|
||||||
|
// FIXME: This changes the state of the I18N object,
|
||||||
|
// should probably not change the 'this.parser' but just
|
||||||
|
// pass it to the parser.
|
||||||
|
this.parser.language = $.i18n.languages[$.i18n().locale] || $.i18n.languages.default;
|
||||||
|
if (message === "")
|
||||||
|
{
|
||||||
|
message = key;
|
||||||
|
}
|
||||||
|
return this.parser.parse(message, parameters);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process a message from the $.I18N instance
|
||||||
|
* for the current document, stored in jQuery.data(document).
|
||||||
|
*
|
||||||
|
* @param {string} key Key of the message.
|
||||||
|
* @param {string} param1 [param...] Variadic list of parameters for {key}.
|
||||||
|
* @return {string|$.I18N} Parsed message, or if no key was given
|
||||||
|
* the instance of $.I18N is returned.
|
||||||
|
*/
|
||||||
|
$.i18n = function (key, param1)
|
||||||
|
{
|
||||||
|
var parameters,
|
||||||
|
i18n = $.data(document, "i18n"),
|
||||||
|
options = typeof key === "object" && key;
|
||||||
|
|
||||||
|
// If the locale option for this call is different then the setup so far,
|
||||||
|
// update it automatically. This doesn't just change the context for this
|
||||||
|
// call but for all future call as well.
|
||||||
|
// If there is no i18n setup yet, don't do this. It will be taken care of
|
||||||
|
// by the `new I18N` construction below.
|
||||||
|
// NOTE: It should only change language for this one call.
|
||||||
|
// Then cache instances of I18N somewhere.
|
||||||
|
if (options && options.locale && i18n && i18n.locale !== options.locale)
|
||||||
|
{
|
||||||
|
i18n.locale = options.locale;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!i18n)
|
||||||
|
{
|
||||||
|
i18n = new I18N(options);
|
||||||
|
$.data(document, "i18n", i18n);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof key === "string")
|
||||||
|
{
|
||||||
|
if (param1 !== undefined)
|
||||||
|
{
|
||||||
|
parameters = slice.call(arguments, 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
parameters = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return i18n.parse(key, parameters);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// FIXME: remove this feature/bug.
|
||||||
|
return i18n;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$.fn.i18n = function ()
|
||||||
|
{
|
||||||
|
var i18n = $.data(document, "i18n");
|
||||||
|
|
||||||
|
if (!i18n)
|
||||||
|
{
|
||||||
|
i18n = new I18N();
|
||||||
|
$.data(document, "i18n", i18n);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.each(function ()
|
||||||
|
{
|
||||||
|
var $this = $(this),
|
||||||
|
messageKey = $this.data("i18n"),
|
||||||
|
lBracket, rBracket, type, key;
|
||||||
|
|
||||||
|
if (messageKey)
|
||||||
|
{
|
||||||
|
lBracket = messageKey.indexOf("[");
|
||||||
|
rBracket = messageKey.indexOf("]");
|
||||||
|
if (lBracket !== -1 && rBracket !== -1 && lBracket < rBracket)
|
||||||
|
{
|
||||||
|
type = messageKey.slice(lBracket + 1, rBracket);
|
||||||
|
key = messageKey.slice(rBracket + 1);
|
||||||
|
if (type === "html")
|
||||||
|
{
|
||||||
|
$this.html(i18n.parse(key));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$this.attr(type, i18n.parse(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$this.text(i18n.parse(messageKey));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$this.find("[data-i18n]").i18n();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function getDefaultLocale()
|
||||||
|
{
|
||||||
|
var locale = $("html").attr("lang");
|
||||||
|
if (!locale)
|
||||||
|
{
|
||||||
|
locale = navigator.language || navigator.userLanguage || "";
|
||||||
|
}
|
||||||
|
return locale;
|
||||||
|
}
|
||||||
|
|
||||||
|
$.i18n.languages = {};
|
||||||
|
$.i18n.messageStore = $.i18n.messageStore || {};
|
||||||
|
$.i18n.parser = {
|
||||||
|
// The default parser only handles variable substitution
|
||||||
|
parse: function (message, parameters)
|
||||||
|
{
|
||||||
|
return message.replace(/\$(\d+)/g, function (str, match)
|
||||||
|
{
|
||||||
|
var index = parseInt(match, 10) - 1;
|
||||||
|
return parameters[index] !== undefined ? parameters[index] : "$" + match;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
emitter: {}
|
||||||
|
};
|
||||||
|
$.i18n.fallbacks = {};
|
||||||
|
$.i18n.debug = false;
|
||||||
|
$.i18n.log = function (/* arguments */)
|
||||||
|
{
|
||||||
|
if (window.console && $.i18n.debug)
|
||||||
|
{
|
||||||
|
window.console.log.apply(window.console, arguments);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/* Static members */
|
||||||
|
I18N.defaults = {
|
||||||
|
locale: getDefaultLocale(),
|
||||||
|
fallbackLocale: "en",
|
||||||
|
parser: $.i18n.parser,
|
||||||
|
messageStore: $.i18n.messageStore
|
||||||
|
};
|
||||||
|
|
||||||
|
// Expose constructor
|
||||||
|
$.i18n.constructor = I18N;
|
||||||
|
}(jQuery));
|
|
@ -0,0 +1,529 @@
|
||||||
|
/* global pluralRuleParser */
|
||||||
|
(function ($)
|
||||||
|
{
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// jscs:disable
|
||||||
|
var language = {
|
||||||
|
// CLDR plural rules generated using
|
||||||
|
// libs/CLDRPluralRuleParser/tools/PluralXML2JSON.html
|
||||||
|
pluralRules: {
|
||||||
|
ak: {
|
||||||
|
one: "n = 0..1"
|
||||||
|
},
|
||||||
|
am: {
|
||||||
|
one: "i = 0 or n = 1"
|
||||||
|
},
|
||||||
|
ar: {
|
||||||
|
zero: "n = 0",
|
||||||
|
one: "n = 1",
|
||||||
|
two: "n = 2",
|
||||||
|
few: "n % 100 = 3..10",
|
||||||
|
many: "n % 100 = 11..99"
|
||||||
|
},
|
||||||
|
ars: {
|
||||||
|
zero: "n = 0",
|
||||||
|
one: "n = 1",
|
||||||
|
two: "n = 2",
|
||||||
|
few: "n % 100 = 3..10",
|
||||||
|
many: "n % 100 = 11..99"
|
||||||
|
},
|
||||||
|
as: {
|
||||||
|
one: "i = 0 or n = 1"
|
||||||
|
},
|
||||||
|
be: {
|
||||||
|
one: "n % 10 = 1 and n % 100 != 11",
|
||||||
|
few: "n % 10 = 2..4 and n % 100 != 12..14",
|
||||||
|
many: "n % 10 = 0 or n % 10 = 5..9 or n % 100 = 11..14"
|
||||||
|
},
|
||||||
|
bh: {
|
||||||
|
one: "n = 0..1"
|
||||||
|
},
|
||||||
|
bn: {
|
||||||
|
one: "i = 0 or n = 1"
|
||||||
|
},
|
||||||
|
br: {
|
||||||
|
one: "n % 10 = 1 and n % 100 != 11,71,91",
|
||||||
|
two: "n % 10 = 2 and n % 100 != 12,72,92",
|
||||||
|
few: "n % 10 = 3..4,9 and n % 100 != 10..19,70..79,90..99",
|
||||||
|
many: "n != 0 and n % 1000000 = 0"
|
||||||
|
},
|
||||||
|
bs: {
|
||||||
|
one: "v = 0 and i % 10 = 1 and i % 100 != 11 or f % 10 = 1 and f % 100 != 11",
|
||||||
|
few: "v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14"
|
||||||
|
},
|
||||||
|
cs: {
|
||||||
|
one: "i = 1 and v = 0",
|
||||||
|
few: "i = 2..4 and v = 0",
|
||||||
|
many: "v != 0"
|
||||||
|
},
|
||||||
|
cy: {
|
||||||
|
zero: "n = 0",
|
||||||
|
one: "n = 1",
|
||||||
|
two: "n = 2",
|
||||||
|
few: "n = 3",
|
||||||
|
many: "n = 6"
|
||||||
|
},
|
||||||
|
da: {
|
||||||
|
one: "n = 1 or t != 0 and i = 0,1"
|
||||||
|
},
|
||||||
|
dsb: {
|
||||||
|
one: "v = 0 and i % 100 = 1 or f % 100 = 1",
|
||||||
|
two: "v = 0 and i % 100 = 2 or f % 100 = 2",
|
||||||
|
few: "v = 0 and i % 100 = 3..4 or f % 100 = 3..4"
|
||||||
|
},
|
||||||
|
fa: {
|
||||||
|
one: "i = 0 or n = 1"
|
||||||
|
},
|
||||||
|
ff: {
|
||||||
|
one: "i = 0,1"
|
||||||
|
},
|
||||||
|
fil: {
|
||||||
|
one: "v = 0 and i = 1,2,3 or v = 0 and i % 10 != 4,6,9 or v != 0 and f % 10 != 4,6,9"
|
||||||
|
},
|
||||||
|
fr: {
|
||||||
|
one: "i = 0,1"
|
||||||
|
},
|
||||||
|
ga: {
|
||||||
|
one: "n = 1",
|
||||||
|
two: "n = 2",
|
||||||
|
few: "n = 3..6",
|
||||||
|
many: "n = 7..10"
|
||||||
|
},
|
||||||
|
gd: {
|
||||||
|
one: "n = 1,11",
|
||||||
|
two: "n = 2,12",
|
||||||
|
few: "n = 3..10,13..19"
|
||||||
|
},
|
||||||
|
gu: {
|
||||||
|
one: "i = 0 or n = 1"
|
||||||
|
},
|
||||||
|
guw: {
|
||||||
|
one: "n = 0..1"
|
||||||
|
},
|
||||||
|
gv: {
|
||||||
|
one: "v = 0 and i % 10 = 1",
|
||||||
|
two: "v = 0 and i % 10 = 2",
|
||||||
|
few: "v = 0 and i % 100 = 0,20,40,60,80",
|
||||||
|
many: "v != 0"
|
||||||
|
},
|
||||||
|
he: {
|
||||||
|
one: "i = 1 and v = 0",
|
||||||
|
two: "i = 2 and v = 0",
|
||||||
|
many: "v = 0 and n != 0..10 and n % 10 = 0"
|
||||||
|
},
|
||||||
|
hi: {
|
||||||
|
one: "i = 0 or n = 1"
|
||||||
|
},
|
||||||
|
hr: {
|
||||||
|
one: "v = 0 and i % 10 = 1 and i % 100 != 11 or f % 10 = 1 and f % 100 != 11",
|
||||||
|
few: "v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14"
|
||||||
|
},
|
||||||
|
hsb: {
|
||||||
|
one: "v = 0 and i % 100 = 1 or f % 100 = 1",
|
||||||
|
two: "v = 0 and i % 100 = 2 or f % 100 = 2",
|
||||||
|
few: "v = 0 and i % 100 = 3..4 or f % 100 = 3..4"
|
||||||
|
},
|
||||||
|
hy: {
|
||||||
|
one: "i = 0,1"
|
||||||
|
},
|
||||||
|
is: {
|
||||||
|
one: "t = 0 and i % 10 = 1 and i % 100 != 11 or t != 0"
|
||||||
|
},
|
||||||
|
iu: {
|
||||||
|
one: "n = 1",
|
||||||
|
two: "n = 2"
|
||||||
|
},
|
||||||
|
iw: {
|
||||||
|
one: "i = 1 and v = 0",
|
||||||
|
two: "i = 2 and v = 0",
|
||||||
|
many: "v = 0 and n != 0..10 and n % 10 = 0"
|
||||||
|
},
|
||||||
|
kab: {
|
||||||
|
one: "i = 0,1"
|
||||||
|
},
|
||||||
|
kn: {
|
||||||
|
one: "i = 0 or n = 1"
|
||||||
|
},
|
||||||
|
kw: {
|
||||||
|
one: "n = 1",
|
||||||
|
two: "n = 2"
|
||||||
|
},
|
||||||
|
lag: {
|
||||||
|
zero: "n = 0",
|
||||||
|
one: "i = 0,1 and n != 0"
|
||||||
|
},
|
||||||
|
ln: {
|
||||||
|
one: "n = 0..1"
|
||||||
|
},
|
||||||
|
lt: {
|
||||||
|
one: "n % 10 = 1 and n % 100 != 11..19",
|
||||||
|
few: "n % 10 = 2..9 and n % 100 != 11..19",
|
||||||
|
many: "f != 0"
|
||||||
|
},
|
||||||
|
lv: {
|
||||||
|
zero: "n % 10 = 0 or n % 100 = 11..19 or v = 2 and f % 100 = 11..19",
|
||||||
|
one: "n % 10 = 1 and n % 100 != 11 or v = 2 and f % 10 = 1 and f % 100 != 11 or v != 2 and f % 10 = 1"
|
||||||
|
},
|
||||||
|
mg: {
|
||||||
|
one: "n = 0..1"
|
||||||
|
},
|
||||||
|
mk: {
|
||||||
|
one: "v = 0 and i % 10 = 1 or f % 10 = 1"
|
||||||
|
},
|
||||||
|
mo: {
|
||||||
|
one: "i = 1 and v = 0",
|
||||||
|
few: "v != 0 or n = 0 or n != 1 and n % 100 = 1..19"
|
||||||
|
},
|
||||||
|
mr: {
|
||||||
|
one: "i = 0 or n = 1"
|
||||||
|
},
|
||||||
|
mt: {
|
||||||
|
one: "n = 1",
|
||||||
|
few: "n = 0 or n % 100 = 2..10",
|
||||||
|
many: "n % 100 = 11..19"
|
||||||
|
},
|
||||||
|
naq: {
|
||||||
|
one: "n = 1",
|
||||||
|
two: "n = 2"
|
||||||
|
},
|
||||||
|
nso: {
|
||||||
|
one: "n = 0..1"
|
||||||
|
},
|
||||||
|
pa: {
|
||||||
|
one: "n = 0..1"
|
||||||
|
},
|
||||||
|
pl: {
|
||||||
|
one: "i = 1 and v = 0",
|
||||||
|
few: "v = 0 and i % 10 = 2..4 and i % 100 != 12..14",
|
||||||
|
many: "v = 0 and i != 1 and i % 10 = 0..1 or v = 0 and i % 10 = 5..9 or v = 0 and i % 100 = 12..14"
|
||||||
|
},
|
||||||
|
prg: {
|
||||||
|
zero: "n % 10 = 0 or n % 100 = 11..19 or v = 2 and f % 100 = 11..19",
|
||||||
|
one: "n % 10 = 1 and n % 100 != 11 or v = 2 and f % 10 = 1 and f % 100 != 11 or v != 2 and f % 10 = 1"
|
||||||
|
},
|
||||||
|
pt: {
|
||||||
|
one: "i = 0..1"
|
||||||
|
},
|
||||||
|
ro: {
|
||||||
|
one: "i = 1 and v = 0",
|
||||||
|
few: "v != 0 or n = 0 or n != 1 and n % 100 = 1..19"
|
||||||
|
},
|
||||||
|
ru: {
|
||||||
|
one: "v = 0 and i % 10 = 1 and i % 100 != 11",
|
||||||
|
few: "v = 0 and i % 10 = 2..4 and i % 100 != 12..14",
|
||||||
|
many: "v = 0 and i % 10 = 0 or v = 0 and i % 10 = 5..9 or v = 0 and i % 100 = 11..14"
|
||||||
|
},
|
||||||
|
se: {
|
||||||
|
one: "n = 1",
|
||||||
|
two: "n = 2"
|
||||||
|
},
|
||||||
|
sh: {
|
||||||
|
one: "v = 0 and i % 10 = 1 and i % 100 != 11 or f % 10 = 1 and f % 100 != 11",
|
||||||
|
few: "v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14"
|
||||||
|
},
|
||||||
|
shi: {
|
||||||
|
one: "i = 0 or n = 1",
|
||||||
|
few: "n = 2..10"
|
||||||
|
},
|
||||||
|
si: {
|
||||||
|
one: "n = 0,1 or i = 0 and f = 1"
|
||||||
|
},
|
||||||
|
sk: {
|
||||||
|
one: "i = 1 and v = 0",
|
||||||
|
few: "i = 2..4 and v = 0",
|
||||||
|
many: "v != 0"
|
||||||
|
},
|
||||||
|
sl: {
|
||||||
|
one: "v = 0 and i % 100 = 1",
|
||||||
|
two: "v = 0 and i % 100 = 2",
|
||||||
|
few: "v = 0 and i % 100 = 3..4 or v != 0"
|
||||||
|
},
|
||||||
|
sma: {
|
||||||
|
one: "n = 1",
|
||||||
|
two: "n = 2"
|
||||||
|
},
|
||||||
|
smi: {
|
||||||
|
one: "n = 1",
|
||||||
|
two: "n = 2"
|
||||||
|
},
|
||||||
|
smj: {
|
||||||
|
one: "n = 1",
|
||||||
|
two: "n = 2"
|
||||||
|
},
|
||||||
|
smn: {
|
||||||
|
one: "n = 1",
|
||||||
|
two: "n = 2"
|
||||||
|
},
|
||||||
|
sms: {
|
||||||
|
one: "n = 1",
|
||||||
|
two: "n = 2"
|
||||||
|
},
|
||||||
|
sr: {
|
||||||
|
one: "v = 0 and i % 10 = 1 and i % 100 != 11 or f % 10 = 1 and f % 100 != 11",
|
||||||
|
few: "v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14"
|
||||||
|
},
|
||||||
|
ti: {
|
||||||
|
one: "n = 0..1"
|
||||||
|
},
|
||||||
|
tl: {
|
||||||
|
one: "v = 0 and i = 1,2,3 or v = 0 and i % 10 != 4,6,9 or v != 0 and f % 10 != 4,6,9"
|
||||||
|
},
|
||||||
|
tzm: {
|
||||||
|
one: "n = 0..1 or n = 11..99"
|
||||||
|
},
|
||||||
|
uk: {
|
||||||
|
one: "v = 0 and i % 10 = 1 and i % 100 != 11",
|
||||||
|
few: "v = 0 and i % 10 = 2..4 and i % 100 != 12..14",
|
||||||
|
many: "v = 0 and i % 10 = 0 or v = 0 and i % 10 = 5..9 or v = 0 and i % 100 = 11..14"
|
||||||
|
},
|
||||||
|
wa: {
|
||||||
|
one: "n = 0..1"
|
||||||
|
},
|
||||||
|
zu: {
|
||||||
|
one: "i = 0 or n = 1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// jscs:enable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plural form transformations, needed for some languages.
|
||||||
|
*
|
||||||
|
* @param {integer} count
|
||||||
|
* Non-localized quantifier
|
||||||
|
* @param {Array} forms
|
||||||
|
* List of plural forms
|
||||||
|
* @return {string} Correct form for quantifier in this language
|
||||||
|
*/
|
||||||
|
convertPlural: function (count, forms)
|
||||||
|
{
|
||||||
|
var pluralRules,
|
||||||
|
pluralFormIndex,
|
||||||
|
index,
|
||||||
|
explicitPluralPattern = /\d+=/i,
|
||||||
|
formCount,
|
||||||
|
form;
|
||||||
|
|
||||||
|
if (!forms || forms.length === 0)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle for Explicit 0= & 1= values
|
||||||
|
for (index = 0; index < forms.length; index++)
|
||||||
|
{
|
||||||
|
form = forms[index];
|
||||||
|
if (explicitPluralPattern.test(form))
|
||||||
|
{
|
||||||
|
formCount = parseInt(form.slice(0, form.indexOf("=")), 10);
|
||||||
|
if (formCount === count)
|
||||||
|
{
|
||||||
|
return (form.slice(form.indexOf("=") + 1));
|
||||||
|
}
|
||||||
|
forms[index] = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
forms = $.map(forms, function (form)
|
||||||
|
{
|
||||||
|
if (form !== undefined)
|
||||||
|
{
|
||||||
|
return form;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
pluralRules = this.pluralRules[$.i18n().locale];
|
||||||
|
|
||||||
|
if (!pluralRules)
|
||||||
|
{
|
||||||
|
// default fallback.
|
||||||
|
return (count === 1) ? forms[0] : forms[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
pluralFormIndex = this.getPluralForm(count, pluralRules);
|
||||||
|
pluralFormIndex = Math.min(pluralFormIndex, forms.length - 1);
|
||||||
|
|
||||||
|
return forms[pluralFormIndex];
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For the number, get the plural for index
|
||||||
|
*
|
||||||
|
* @param {integer} number
|
||||||
|
* @param {Object} pluralRules
|
||||||
|
* @return {integer} plural form index
|
||||||
|
*/
|
||||||
|
getPluralForm: function (number, pluralRules)
|
||||||
|
{
|
||||||
|
var i,
|
||||||
|
pluralForms = ["zero", "one", "two", "few", "many", "other"],
|
||||||
|
pluralFormIndex = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < pluralForms.length; i++)
|
||||||
|
{
|
||||||
|
if (pluralRules[pluralForms[i]])
|
||||||
|
{
|
||||||
|
if (pluralRuleParser(pluralRules[pluralForms[i]], number))
|
||||||
|
{
|
||||||
|
return pluralFormIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
pluralFormIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pluralFormIndex;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a number using digitTransformTable.
|
||||||
|
*
|
||||||
|
* @param {number} num Value to be converted
|
||||||
|
* @param {boolean} integer Convert the return value to an integer
|
||||||
|
* @return {string} The number converted into a String.
|
||||||
|
*/
|
||||||
|
convertNumber: function (num, integer)
|
||||||
|
{
|
||||||
|
var tmp, item, i,
|
||||||
|
transformTable, numberString, convertedNumber;
|
||||||
|
|
||||||
|
// Set the target Transform table:
|
||||||
|
transformTable = this.digitTransformTable($.i18n().locale);
|
||||||
|
numberString = String(num);
|
||||||
|
convertedNumber = "";
|
||||||
|
|
||||||
|
if (!transformTable)
|
||||||
|
{
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the restore to Latin number flag is set:
|
||||||
|
if (integer)
|
||||||
|
{
|
||||||
|
if (parseFloat(num, 10) === num)
|
||||||
|
{
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp = [];
|
||||||
|
|
||||||
|
for (item in transformTable)
|
||||||
|
{
|
||||||
|
tmp[transformTable[item]] = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
transformTable = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < numberString.length; i++)
|
||||||
|
{
|
||||||
|
if (transformTable[numberString[i]])
|
||||||
|
{
|
||||||
|
convertedNumber += transformTable[numberString[i]];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
convertedNumber += numberString[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return integer ? parseFloat(convertedNumber, 10) : convertedNumber;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Grammatical transformations, needed for inflected languages.
|
||||||
|
* Invoked by putting {{grammar:form|word}} in a message.
|
||||||
|
* Override this method for languages that need special grammar rules
|
||||||
|
* applied dynamically.
|
||||||
|
*
|
||||||
|
* @param {string} word
|
||||||
|
* @param {string} form
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
convertGrammar: function (word, form)
|
||||||
|
{
|
||||||
|
return word;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides an alternative text depending on specified gender. Usage
|
||||||
|
* {{gender:[gender|user object]|masculine|feminine|neutral}}. If second
|
||||||
|
* or third parameter are not specified, masculine is used.
|
||||||
|
*
|
||||||
|
* These details may be overriden per language.
|
||||||
|
*
|
||||||
|
* @param {string} gender
|
||||||
|
* male, female, or anything else for neutral.
|
||||||
|
* @param {Array} forms
|
||||||
|
* List of gender forms
|
||||||
|
*
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
gender: function (gender, forms)
|
||||||
|
{
|
||||||
|
if (!forms || forms.length === 0)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
while (forms.length < 2)
|
||||||
|
{
|
||||||
|
forms.push(forms[forms.length - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gender === "male")
|
||||||
|
{
|
||||||
|
return forms[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gender === "female")
|
||||||
|
{
|
||||||
|
return forms[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return (forms.length === 3) ? forms[2] : forms[0];
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the digit transform table for the given language
|
||||||
|
* See http://cldr.unicode.org/translation/numbering-systems
|
||||||
|
*
|
||||||
|
* @param {string} language
|
||||||
|
* @return {Array|boolean} List of digits in the passed language or false
|
||||||
|
* representation, or boolean false if there is no information.
|
||||||
|
*/
|
||||||
|
digitTransformTable: function (language)
|
||||||
|
{
|
||||||
|
var tables = {
|
||||||
|
ar: "٠١٢٣٤٥٦٧٨٩",
|
||||||
|
fa: "۰۱۲۳۴۵۶۷۸۹",
|
||||||
|
ml: "൦൧൨൩൪൫൬൭൮൯",
|
||||||
|
kn: "೦೧೨೩೪೫೬೭೮೯",
|
||||||
|
lo: "໐໑໒໓໔໕໖໗໘໙",
|
||||||
|
or: "୦୧୨୩୪୫୬୭୮୯",
|
||||||
|
kh: "០១២៣៤៥៦៧៨៩",
|
||||||
|
nqo: "߀߁߂߃߄߅߆߇߈߉", // Note that the digits go right to left
|
||||||
|
pa: "੦੧੨੩੪੫੬੭੮੯",
|
||||||
|
gu: "૦૧૨૩૪૫૬૭૮૯",
|
||||||
|
hi: "०१२३४५६७८९",
|
||||||
|
my: "၀၁၂၃၄၅၆၇၈၉",
|
||||||
|
ta: "௦௧௨௩௪௫௬௭௮௯",
|
||||||
|
te: "౦౧౨౩౪౫౬౭౮౯",
|
||||||
|
th: "๐๑๒๓๔๕๖๗๘๙", // FIXME use iso 639 codes
|
||||||
|
bo: "༠༡༢༣༤༥༦༧༨༩" // FIXME use iso 639 codes
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!tables[language])
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tables[language].split("");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$.extend($.i18n.languages, {
|
||||||
|
default: language
|
||||||
|
});
|
||||||
|
}(jQuery));
|
|
@ -0,0 +1,139 @@
|
||||||
|
/*!
|
||||||
|
* jQuery Internationalization library - Message Store
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Santhosh Thottingal
|
||||||
|
*
|
||||||
|
* jquery.i18n is dual licensed GPLv2 or later and MIT. You don't have to do anything special to
|
||||||
|
* choose one license or the other and you don't have to notify anyone which license you are using.
|
||||||
|
* You are free to use UniversalLanguageSelector in commercial projects as long as the copyright
|
||||||
|
* header is left intact. See files GPL-LICENSE and MIT-LICENSE for details.
|
||||||
|
*
|
||||||
|
* @licence GNU General Public Licence 2.0 or later
|
||||||
|
* @licence MIT License
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function ($)
|
||||||
|
{
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var MessageStore = function ()
|
||||||
|
{
|
||||||
|
this.messages = {};
|
||||||
|
this.sources = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
function jsonMessageLoader(url)
|
||||||
|
{
|
||||||
|
var deferred = $.Deferred();
|
||||||
|
|
||||||
|
$.getJSON(url)
|
||||||
|
.done(deferred.resolve)
|
||||||
|
.fail(function (jqxhr, settings, exception)
|
||||||
|
{
|
||||||
|
$.i18n.log("Error in loading messages from " + url + " Exception: " + exception);
|
||||||
|
// Ignore 404 exception, because we are handling fallabacks explicitly
|
||||||
|
deferred.resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
return deferred.promise();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See https://github.com/wikimedia/jquery.i18n/wiki/Specification#wiki-Message_File_Loading
|
||||||
|
*/
|
||||||
|
MessageStore.prototype = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* General message loading API This can take a URL string for
|
||||||
|
* the json formatted messages.
|
||||||
|
* <code>load('path/to/all_localizations.json');</code>
|
||||||
|
*
|
||||||
|
* This can also load a localization file for a locale <code>
|
||||||
|
* load( 'path/to/de-messages.json', 'de' );
|
||||||
|
* </code>
|
||||||
|
* A data object containing message key- message translation mappings
|
||||||
|
* can also be passed Eg:
|
||||||
|
* <code>
|
||||||
|
* load( { 'hello' : 'Hello' }, optionalLocale );
|
||||||
|
* </code> If the data argument is
|
||||||
|
* null/undefined/false,
|
||||||
|
* all cached messages for the i18n instance will get reset.
|
||||||
|
*
|
||||||
|
* @param {string|Object} source
|
||||||
|
* @param {string} locale Language tag
|
||||||
|
* @return {jQuery.Promise}
|
||||||
|
*/
|
||||||
|
load: function (source, locale)
|
||||||
|
{
|
||||||
|
var key = null,
|
||||||
|
deferreds = [],
|
||||||
|
messageStore = this;
|
||||||
|
|
||||||
|
if (typeof source === "string")
|
||||||
|
{
|
||||||
|
// This is a URL to the messages file.
|
||||||
|
$.i18n.log("Loading messages from: " + source);
|
||||||
|
return jsonMessageLoader(source)
|
||||||
|
.then(function (localization)
|
||||||
|
{
|
||||||
|
return messageStore.load(localization, locale);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (locale)
|
||||||
|
{
|
||||||
|
// source is an key-value pair of messages for given locale
|
||||||
|
messageStore.set(locale, source);
|
||||||
|
|
||||||
|
return $.Deferred().resolve();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// source is a key-value pair of locales and their source
|
||||||
|
for (key in source)
|
||||||
|
{
|
||||||
|
if (Object.prototype.hasOwnProperty.call(source, key))
|
||||||
|
{
|
||||||
|
locale = key;
|
||||||
|
// No {locale} given, assume data is a group of languages,
|
||||||
|
// call this function again for each language.
|
||||||
|
deferreds.push(messageStore.load(source[key], locale));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $.when.apply($, deferreds);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set messages to the given locale.
|
||||||
|
* If locale exists, add messages to the locale.
|
||||||
|
*
|
||||||
|
* @param {string} locale
|
||||||
|
* @param {Object} messages
|
||||||
|
*/
|
||||||
|
set: function (locale, messages)
|
||||||
|
{
|
||||||
|
if (!this.messages[locale])
|
||||||
|
{
|
||||||
|
this.messages[locale] = messages;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.messages[locale] = $.extend(this.messages[locale], messages);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {string} locale
|
||||||
|
* @param {string} messageKey
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
get: function (locale, messageKey)
|
||||||
|
{
|
||||||
|
return this.messages[locale] && this.messages[locale][messageKey];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$.extend($.i18n.messageStore, new MessageStore());
|
||||||
|
}(jQuery));
|
|
@ -0,0 +1,353 @@
|
||||||
|
/*!
|
||||||
|
* jQuery Internationalization library
|
||||||
|
*
|
||||||
|
* Copyright (C) 2011-2013 Santhosh Thottingal, Neil Kandalgaonkar
|
||||||
|
*
|
||||||
|
* jquery.i18n is dual licensed GPLv2 or later and MIT. You don't have to do
|
||||||
|
* anything special to choose one license or the other and you don't have to
|
||||||
|
* notify anyone which license you are using. You are free to use
|
||||||
|
* UniversalLanguageSelector in commercial projects as long as the copyright
|
||||||
|
* header is left intact. See files GPL-LICENSE and MIT-LICENSE for details.
|
||||||
|
*
|
||||||
|
* @licence GNU General Public Licence 2.0 or later
|
||||||
|
* @licence MIT License
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function ($)
|
||||||
|
{
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var MessageParser = function (options)
|
||||||
|
{
|
||||||
|
this.options = $.extend({}, $.i18n.parser.defaults, options);
|
||||||
|
this.language = $.i18n.languages[String.locale] || $.i18n.languages.default;
|
||||||
|
this.emitter = $.i18n.parser.emitter;
|
||||||
|
};
|
||||||
|
|
||||||
|
MessageParser.prototype = {
|
||||||
|
|
||||||
|
constructor: MessageParser,
|
||||||
|
|
||||||
|
simpleParse: function (message, parameters)
|
||||||
|
{
|
||||||
|
return message.replace(/\$(\d+)/g, function (str, match)
|
||||||
|
{
|
||||||
|
var index = parseInt(match, 10) - 1;
|
||||||
|
|
||||||
|
return parameters[index] !== undefined ? parameters[index] : "$" + match;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
parse: function (message, replacements)
|
||||||
|
{
|
||||||
|
if (message.indexOf("{{") < 0)
|
||||||
|
{
|
||||||
|
return this.simpleParse(message, replacements);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.emitter.language = $.i18n.languages[$.i18n().locale] ||
|
||||||
|
$.i18n.languages.default;
|
||||||
|
|
||||||
|
return this.emitter.emit(this.ast(message), replacements);
|
||||||
|
},
|
||||||
|
|
||||||
|
ast: function (message)
|
||||||
|
{
|
||||||
|
var pipe, colon, backslash, anyCharacter, dollar, digits, regularLiteral,
|
||||||
|
regularLiteralWithoutBar, regularLiteralWithoutSpace, escapedOrLiteralWithoutBar,
|
||||||
|
escapedOrRegularLiteral, templateContents, templateName, openTemplate,
|
||||||
|
closeTemplate, expression, paramExpression, result,
|
||||||
|
pos = 0;
|
||||||
|
|
||||||
|
// Try parsers until one works, if none work return null
|
||||||
|
function choice(parserSyntax)
|
||||||
|
{
|
||||||
|
return function ()
|
||||||
|
{
|
||||||
|
var i, result;
|
||||||
|
|
||||||
|
for (i = 0; i < parserSyntax.length; i++)
|
||||||
|
{
|
||||||
|
result = parserSyntax[i]();
|
||||||
|
|
||||||
|
if (result !== null)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try several parserSyntax-es in a row.
|
||||||
|
// All must succeed; otherwise, return null.
|
||||||
|
// This is the only eager one.
|
||||||
|
function sequence(parserSyntax)
|
||||||
|
{
|
||||||
|
var i, res,
|
||||||
|
originalPos = pos,
|
||||||
|
result = [];
|
||||||
|
|
||||||
|
for (i = 0; i < parserSyntax.length; i++)
|
||||||
|
{
|
||||||
|
res = parserSyntax[i]();
|
||||||
|
|
||||||
|
if (res === null)
|
||||||
|
{
|
||||||
|
pos = originalPos;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the same parser over and over until it fails.
|
||||||
|
// Must succeed a minimum of n times; otherwise, return null.
|
||||||
|
function nOrMore(n, p)
|
||||||
|
{
|
||||||
|
return function ()
|
||||||
|
{
|
||||||
|
var originalPos = pos,
|
||||||
|
result = [],
|
||||||
|
parsed = p();
|
||||||
|
|
||||||
|
while (parsed !== null)
|
||||||
|
{
|
||||||
|
result.push(parsed);
|
||||||
|
parsed = p();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.length < n)
|
||||||
|
{
|
||||||
|
pos = originalPos;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helpers -- just make parserSyntax out of simpler JS builtin types
|
||||||
|
|
||||||
|
function makeStringParser(s)
|
||||||
|
{
|
||||||
|
var len = s.length;
|
||||||
|
|
||||||
|
return function ()
|
||||||
|
{
|
||||||
|
var result = null;
|
||||||
|
|
||||||
|
if (message.slice(pos, pos + len) === s)
|
||||||
|
{
|
||||||
|
result = s;
|
||||||
|
pos += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeRegexParser(regex)
|
||||||
|
{
|
||||||
|
return function ()
|
||||||
|
{
|
||||||
|
var matches = message.slice(pos).match(regex);
|
||||||
|
|
||||||
|
if (matches === null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos += matches[0].length;
|
||||||
|
|
||||||
|
return matches[0];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pipe = makeStringParser("|");
|
||||||
|
colon = makeStringParser(":");
|
||||||
|
backslash = makeStringParser("\\");
|
||||||
|
anyCharacter = makeRegexParser(/^./);
|
||||||
|
dollar = makeStringParser("$");
|
||||||
|
digits = makeRegexParser(/^\d+/);
|
||||||
|
regularLiteral = makeRegexParser(/^[^{}[\]$\\]/);
|
||||||
|
regularLiteralWithoutBar = makeRegexParser(/^[^{}[\]$\\|]/);
|
||||||
|
regularLiteralWithoutSpace = makeRegexParser(/^[^{}[\]$\s]/);
|
||||||
|
|
||||||
|
// There is a general pattern:
|
||||||
|
// parse a thing;
|
||||||
|
// if it worked, apply transform,
|
||||||
|
// otherwise return null.
|
||||||
|
// But using this as a combinator seems to cause problems
|
||||||
|
// when combined with nOrMore().
|
||||||
|
// May be some scoping issue.
|
||||||
|
function transform(p, fn)
|
||||||
|
{
|
||||||
|
return function ()
|
||||||
|
{
|
||||||
|
var result = p();
|
||||||
|
|
||||||
|
return result === null ? null : fn(result);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used to define "literals" within template parameters. The pipe
|
||||||
|
// character is the parameter delimeter, so by default
|
||||||
|
// it is not a literal in the parameter
|
||||||
|
function literalWithoutBar()
|
||||||
|
{
|
||||||
|
var result = nOrMore(1, escapedOrLiteralWithoutBar)();
|
||||||
|
|
||||||
|
return result === null ? null : result.join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
function literal()
|
||||||
|
{
|
||||||
|
var result = nOrMore(1, escapedOrRegularLiteral)();
|
||||||
|
|
||||||
|
return result === null ? null : result.join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapedLiteral()
|
||||||
|
{
|
||||||
|
var result = sequence([backslash, anyCharacter]);
|
||||||
|
|
||||||
|
return result === null ? null : result[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
choice([escapedLiteral, regularLiteralWithoutSpace]);
|
||||||
|
escapedOrLiteralWithoutBar = choice([escapedLiteral, regularLiteralWithoutBar]);
|
||||||
|
escapedOrRegularLiteral = choice([escapedLiteral, regularLiteral]);
|
||||||
|
|
||||||
|
function replacement()
|
||||||
|
{
|
||||||
|
var result = sequence([dollar, digits]);
|
||||||
|
|
||||||
|
if (result === null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ["REPLACE", parseInt(result[1], 10) - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
templateName = transform(
|
||||||
|
// see $wgLegalTitleChars
|
||||||
|
// not allowing : due to the need to catch "PLURAL:$1"
|
||||||
|
makeRegexParser(/^[ !"$&'()*,./0-9;=?@A-Z^_`a-z~\x80-\xFF+-]+/),
|
||||||
|
|
||||||
|
function (result)
|
||||||
|
{
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
function templateParam()
|
||||||
|
{
|
||||||
|
var expr,
|
||||||
|
result = sequence([pipe, nOrMore(0, paramExpression)]);
|
||||||
|
|
||||||
|
if (result === null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
expr = result[1];
|
||||||
|
|
||||||
|
// use a "CONCAT" operator if there are multiple nodes,
|
||||||
|
// otherwise return the first node, raw.
|
||||||
|
return expr.length > 1 ? ["CONCAT"].concat(expr) : expr[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
function templateWithReplacement()
|
||||||
|
{
|
||||||
|
var result = sequence([templateName, colon, replacement]);
|
||||||
|
|
||||||
|
return result === null ? null : [result[0], result[2]];
|
||||||
|
}
|
||||||
|
|
||||||
|
function templateWithOutReplacement()
|
||||||
|
{
|
||||||
|
var result = sequence([templateName, colon, paramExpression]);
|
||||||
|
|
||||||
|
return result === null ? null : [result[0], result[2]];
|
||||||
|
}
|
||||||
|
|
||||||
|
templateContents = choice([
|
||||||
|
function ()
|
||||||
|
{
|
||||||
|
var res = sequence([
|
||||||
|
// templates can have placeholders for dynamic
|
||||||
|
// replacement eg: {{PLURAL:$1|one car|$1 cars}}
|
||||||
|
// or no placeholders eg:
|
||||||
|
// {{GRAMMAR:genitive|{{SITENAME}}}
|
||||||
|
choice([templateWithReplacement, templateWithOutReplacement]),
|
||||||
|
nOrMore(0, templateParam)
|
||||||
|
]);
|
||||||
|
|
||||||
|
return res === null ? null : res[0].concat(res[1]);
|
||||||
|
},
|
||||||
|
function ()
|
||||||
|
{
|
||||||
|
var res = sequence([templateName, nOrMore(0, templateParam)]);
|
||||||
|
|
||||||
|
if (res === null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [res[0]].concat(res[1]);
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
openTemplate = makeStringParser("{{");
|
||||||
|
closeTemplate = makeStringParser("}}");
|
||||||
|
|
||||||
|
function template()
|
||||||
|
{
|
||||||
|
var result = sequence([openTemplate, templateContents, closeTemplate]);
|
||||||
|
|
||||||
|
return result === null ? null : result[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
expression = choice([template, replacement, literal]);
|
||||||
|
paramExpression = choice([template, replacement, literalWithoutBar]);
|
||||||
|
|
||||||
|
function start()
|
||||||
|
{
|
||||||
|
var result = nOrMore(0, expression)();
|
||||||
|
|
||||||
|
if (result === null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ["CONCAT"].concat(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
result = start();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For success, the pos must have gotten to the end of the input
|
||||||
|
* and returned a non-null.
|
||||||
|
* n.b. This is part of language infrastructure, so we do not throw an
|
||||||
|
* internationalizable message.
|
||||||
|
*/
|
||||||
|
if (result === null || pos !== message.length)
|
||||||
|
{
|
||||||
|
throw new Error("Parse error at position " + pos.toString() + " in input: " + message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
$.extend($.i18n.parser, new MessageParser());
|
||||||
|
}(jQuery));
|
Ładowanie…
Reference in New Issue