Symfony与翻译包有很好的复数逻辑。但是,我想将此逻辑引入JavaScript,因为我正在从Ajax调用更新字符串。
是否有这样的“图书馆”可以做这样的事情?
答案 0 :(得分:2)
我自己写的因为找不到任何东西。版权所有Symfony
只需致电MessageSelector.choose(message, number, locale);
(function() {
var compiledRegex,
messageSelectorIntervalCompiledRegex,
messageSelectortandartCompiledRegex;
function getCompiledRegex() {
if (!compiledRegex) {
compiledRegex = new RegExp("^" + Interval.getIntervalRegexp() + "$");
}
return compiledRegex;
}
function getMessageSelectorIntervalCompiledRegex() {
if (!messageSelectorIntervalCompiledRegex) {
messageSelectorIntervalCompiledRegex = new RegExp("^(" + Interval.getIntervalRegexp() + ")\s*(.*?)$");
}
return messageSelectorIntervalCompiledRegex;
}
function getMessageSelectortandartCompiledRegex() {
if (!messageSelectortandartCompiledRegex) {
messageSelectortandartCompiledRegex = new RegExp("^\w+\:\s*(.*?)$");
}
return messageSelectortandartCompiledRegex;
}
function Interval() {
}
Interval.test = function (number, interval) {
interval = interval.trim();
var matches = getCompiledRegex().exec(interval);
var ret = false;
if (!matches) {
throw new Error("'" + interval + "' is not a valid interval.");
}
if (matches[1]) {
var ret;
matches[2].split(",").forEach(function (n) {
if (number == n) {
ret = true;
}
});
} else {
var leftNumber = Interval.convertNumber(matches[5]);
var rightNumber = Interval.convertNumber(matches[7]);
return ("[" === matches[4] ? number >= leftNumber : number > leftNumber)
&& ("]" === matches[9] ? number <= rightNumber : number < rightNumber);
}
return ret;
};
Interval.getIntervalRegexp = function () {
return "({\\s*" +
"(\\-?\\d+(\\.\\d+)?[\\s*,\\s*\\-?\\d+(\\.\\d+)?]*)" +
"\\s*})" +
"|" +
"([\\[\\]])" + // left_delimiter -> 4
"\\s*" +
"(-Inf|\\-?\\d+(\\.\\d+)?)" + // left -> 5
"\\s*,\\s*" +
"(\\+?Inf|\\-?\\d+(\\.\\d+)?)" + // right -> 7
"\\s*" +
"([\\[\\]])"; // right_delimiter -> 9
};
Interval.convertNumber = function (number) {
if ("-Inf" === number) {
return -Infinity;
} else if ("+Inf" === number || "Inf" === number) {
return Infinity;
}
return parseFloat(number);
};
function PluralizationRules() {
}
var rules = [];
PluralizationRules.get = function (number, locale) {
if ("pt_BR" == locale) {
// temporary set a locale for brazilian
locale = "xbr";
}
if (locale.length > 3) {
locale = locale.substr(0, locale.lastIndexOf("_"));
}
if (rules[locale] !== undefined) {
var ret = rules[locale](number);
if (isNaN(parseInt(ret, 10)) || ret < 0) {
return 0;
}
return ret;
}
/*
* The plural rules are derived from code of the Zend Framework (2010-09-25),
* which is subject to the new BSD license (http://framework.zend.com/license/new-bsd).
* Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
*/
switch (locale) {
case 'bo':
case 'dz':
case 'id':
case 'ja':
case 'jv':
case 'ka':
case 'km':
case 'kn':
case 'ko':
case 'ms':
case 'th':
case 'tr':
case 'vi':
case 'zh':
return 0;
break;
case 'af':
case 'az':
case 'bn':
case 'bg':
case 'ca':
case 'da':
case 'de':
case 'el':
case 'en':
case 'eo':
case 'es':
case 'et':
case 'eu':
case 'fa':
case 'fi':
case 'fo':
case 'fur':
case 'fy':
case 'gl':
case 'gu':
case 'ha':
case 'he':
case 'hu':
case 'is':
case 'it':
case 'ku':
case 'lb':
case 'ml':
case 'mn':
case 'mr':
case 'nah':
case 'nb':
case 'ne':
case 'nl':
case 'nn':
case 'no':
case 'om':
case 'or':
case 'pa':
case 'pap':
case 'ps':
case 'pt':
case 'so':
case 'sq':
case 'sv':
case 'sw':
case 'ta':
case 'te':
case 'tk':
case 'ur':
case 'zu':
return (number == 1) ? 0 : 1;
case 'am':
case 'bh':
case 'fil':
case 'fr':
case 'gun':
case 'hi':
case 'ln':
case 'mg':
case 'nso':
case 'xbr':
case 'ti':
case 'wa':
return ((number == 0) || (number == 1)) ? 0 : 1;
case 'be':
case 'bs':
case 'hr':
case 'ru':
case 'sr':
case 'uk':
return ((number % 10 == 1) && (number % 100 != 11)) ? 0 : (((number % 10 >= 2) && (number % 10 <= 4) && ((number % 100 < 10) || (number % 100 >= 20))) ? 1 : 2);
case 'cs':
case 'sk':
return (number == 1) ? 0 : (((number >= 2) && (number <= 4)) ? 1 : 2);
case 'ga':
return (number == 1) ? 0 : ((number == 2) ? 1 : 2);
case 'lt':
return ((number % 10 == 1) && (number % 100 != 11)) ? 0 : (((number % 10 >= 2) && ((number % 100 < 10) || (number % 100 >= 20))) ? 1 : 2);
case 'sl':
return (number % 100 == 1) ? 0 : ((number % 100 == 2) ? 1 : (((number % 100 == 3) || (number % 100 == 4)) ? 2 : 3));
case 'mk':
return (number % 10 == 1) ? 0 : 1;
case 'mt':
return (number == 1) ? 0 : (((number == 0) || ((number % 100 > 1) && (number % 100 < 11))) ? 1 : (((number % 100 > 10) && (number % 100 < 20)) ? 2 : 3));
case 'lv':
return (number == 0) ? 0 : (((number % 10 == 1) && (number % 100 != 11)) ? 1 : 2);
case 'pl':
return (number == 1) ? 0 : (((number % 10 >= 2) && (number % 10 <= 4) && ((number % 100 < 12) || (number % 100 > 14))) ? 1 : 2);
case 'cy':
return (number == 1) ? 0 : ((number == 2) ? 1 : (((number == 8) || (number == 11)) ? 2 : 3));
case 'ro':
return (number == 1) ? 0 : (((number == 0) || ((number % 100 > 0) && (number % 100 < 20))) ? 1 : 2);
case 'ar':
return (number == 0) ? 0 : ((number == 1) ? 1 : ((number == 2) ? 2 : (((number >= 3) && (number <= 10)) ? 3 : (((number >= 11) && (number <= 99)) ? 4 : 5))));
default:
return 0;
}
};
PluralizationRules.set = function (rule, locale) {
if ("pt_BR" == locale) {
// temporary set a locale for brazilian
$locale = "xbr";
}
if (locale.length > 3) {
locale = locale.substr(0, locale.lastIndexOf("_"));
}
if (typeof rule !== "function") {
throw new Error('The given rule can not be called');
}
rules[locale] = rule;
};
function MessageSelector() {
}
MessageSelector.choose = function (message, number, locale) {
var parts = message.split("|"),
explicitRules = {},
standardRules = [];
parts.forEach(function(part) {
part = part.trim();
var matches = getMessageSelectorIntervalCompiledRegex().exec(part);
if (matches) {
explicitRules[matches[1]] = matches[11];
} else {
matches = getMessageSelectortandartCompiledRegex().exec(part);
if (matches) {
standardRules.push(matches[1]);
} else {
standardRules.push(part);
}
}
});
// try to match an explicit rule, then fallback to the standard ones
for (var interval in explicitRules) {
if (explicitRules.hasOwnProperty(interval)) {
var m = explicitRules[interval];
if (Interval.test(number, interval)) {
return m;
}
}
}
var position = PluralizationRules.get(number, locale);
if (standardRules[position] === undefined) {
// when there's exactly one rule given, and that rule is a standard
// rule, use this rule
if (1 === parts.length && standardRules[0] !== undefined) {
return standardRules[0];
}
throw new Error("Unable to choose a translation for '" + message + "' with locale '" + locale + "'. Double check that this translation has the correct plural options (e.g. \"There is one apple|There are %%count%% apples\").");
}
return standardRules[position];
};
window.MessageSelector = MessageSelector;
})();