iegik
10/11/2016 - 7:22 AM

Plural forms for l10n

Plural forms

// _n = l10n(dicts, 'ru')

// ;[
// _n`today`,
// _n`day`(5),
// _n`${5} day`(5),
// _n`${1} page of ${10}`(1),
// _n`${parseFloat(1/3).toFixed(2)} second`(1/3),
// _n`${1}th`(1),
// _n`${`${4}`.padStart(4, '0')}th`(4),
// _n`${`${125}`.padStart(4, '0')}th`(125),
// ]
const l10n = lang => {
    const form = forms[lang]
    const dict = dicts[lang]

    return (parts, ...values) => {
        const replacer = k => values.shift()
        const str = dict[parts.join('%s')]
        return (n) => str[form(n)]
            .replaceAll(/%s/g, replacer)
    }
}
{
  "ru": {
    "day": "день",
    "%s days": ["%s день", "%s дня", "%s дней"]
  }
}
plural.prototype.ru = function (n){
  return n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;
}
plural.prototype.lv = function(n){
  return n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2;
}
function createText(lang) {
  return function () {
    return plural.bind(plural, dict, lang, null).apply(plural, arguments);
  }
}

function createPluralText(lang) {
  return function () {
    return plural.bind(plural, dict, lang).apply(plural, arguments);
  }
}

// Aliases
/**
 * @returns {String} text translated to russian language
 */
var rutext = createText('ru');
// rutext('day') - 'день'

/**
 * @param {String} slug/text for translation
 * @returns {String} plural formatted text translated to russian language, based on dictionary
 */
var nrutext = createPluralText('ru');
// nrutext(2, '%s day', '%s days') - '%s дня'

// Example without dictionary
/**
 * @param {String} formatted text
 * @returns {String} plural formatted text in latvian form
 */
function nlvtext() {
    return plural.bind(plural, null, 'lv').apply(plural, arguments);
}
// nlvtext(2, '%s diena', '%s dienas') - '%s dienas'
/**
 * @param {Object} dict Dictionary: 
 * {
 *   en: {
 *     'day': 'day',
 *     '%s days': ['%s day', '%s days']
 *   }
 * }
 * @param {String} lang Language of the word
 * @param {Number} n Base number of form
 * @param {String} ... #n form of text
 * @return {String} translated plural text
 */
function plural(dict, lang, n){
  var form = this.prototype[lang](n),
      x = 3;
  return dict ? (typeof n === 'number' ? dict[lang][arguments[x + 1]][form] : dict[lang][arguments[x]]) : arguments[form + x];
}
// http://rextester.com/OMF52875
public class Program
    {
        public static string N(Match m) {
            return "{" + m.Captures.Count + "}";
        }
        public static string Get(string str) {
            Dictionary<string, string> obj = new Dictionary<string, string>();
            obj.Add("%n day", "%n день");
            obj.Add("%n days0", "%n дня");
            obj.Add("%n days1", "%n дней");
            obj.Add("%n day in %s", "%n день в %s");
            obj.Add("%n days in %s0", "%n дня в %s");
            obj.Add("%n days in %s1", "%n дней в %s");
            return obj[str];
        }
        public static string ToSharp(string str) {
            string pattern = @"%[ns]";
            Regex rgx = new Regex(pattern);
            int i = 0;
            str = rgx.Replace(str, m => "{" + (i++) + "}");
            return str;
        }
        public static string Get(string str, params object[] list) {
            str = Get(str);
            str = String.Format(ToSharp(str), list);
            return str;
        }
        public static int pluralRU(int n) {
            return n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;
        }
        public static string NGet(string str, string strs, int count) {
            int form = pluralRU(count);
            str = form == 0 ? Get(str) : Get(strs + (form - 1));
            return str;
        }
        public static string NGet(string str, string strs, int count, params object[] list) {
            int form = pluralRU(count);
            str = form == 0 ? Get(str) : Get(strs + (form - 1));
            str = String.Format(ToSharp(str), list);
            return str;
        }
        public static void Main(string[] args)
        {
            String str;
            int n;

            n = 1;
            str = NGet("%n day", "%n days", n, n);
            Console.WriteLine(str);


            n = 2;
            str = NGet("%n day", "%n days", n, n);
            Console.WriteLine(str);

            n = 5;
            str = NGet("%n day", "%n days", n, n);
            Console.WriteLine(str);


            n = 7;
            str = NGet("%n day in %s", "%n days in %s", n, n, "неделе");
            Console.WriteLine(str);
        }
    }
// _n = l10n('ru')

// ;[
// _n('today')
// _n('day', 5),
// _n('%d day', 5, 5),
// _n('%d page of %d', 1, 1, 10),
// _n('%0.2f second', 1/3, 1/3),
// _n('%4dth', 1, 1),
// _n('%4dth', 4, 4),
// _n('%4dth', 125, 125),
// ]

const l10n = lang => {
    form = forms[lang]
    dict = dicts[lang]
    return (str, n, ...values) => dict[str][form(n)].replaceAll(/(%d|%0.2f|%4d)/g, (k) => {
        const val = values.shift()
        switch (k) {
            case '%d': return parseInt(val)
            case '%0.2f': return parseFloat(val).toFixed(2)
            case '%4d': return `${val}`.padStart(4, '0')
            default: return val
        }
    })
}