0

Symfony 的翻译包具有很好的多元化逻辑。但是,我想将此逻辑引入 JavaScript,因为我正在从 Ajax 调用更新字符串。

有没有这样的“图书馆”可以做这样的事情?

4

1 回答 1

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;
})();
于 2013-09-25T01:53:08.077 回答