/** * @license * Copyright Google Inc. All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ const cldr = require('cldr'); // locale list const locales = cldr.localeIds; const langToRule = {}; const ruleToLang = {}; const variants = []; const localeToVariant = {}; const DEFAULT_RULE = `function anonymous(n\n/**/) {\nreturn"other"\n}`; const EMPTY_RULE = `function anonymous(n\n/**/) {\n\n}`; locales.forEach(locale => { const rule = normalizeRule(cldr.extractPluralRuleFunction(locale).toString()); const lang = getVariantLang(locale, rule); if (!lang || !rule) { return; } if (!ruleToLang[rule]) { ruleToLang[rule] = []; } else if (ruleToLang[rule].indexOf(lang) > -1) { return; } ruleToLang[rule].push(lang); }); let nextVariantCode = 'a'.charCodeAt(0); variants.forEach(locale => { const rule = normalizeRule(cldr.extractPluralRuleFunction(locale).toString()); if (!rule) { return; } let mapTo = null; if (ruleToLang[rule]) { mapTo = ruleToLang[rule][0]; localeToVariant[locale] = mapTo; return; } if (!mapTo) { mapTo = '_' + String.fromCharCode(nextVariantCode++); langToRule[mapTo] = rule; ruleToLang[rule] = [mapTo]; localeToVariant[locale] = mapTo; } }); console.log(generateCode()); function generateCode() { checkMapping(); return ` // This is generated code DO NOT MODIFY // see angular/script/cldr/gen_plural_rules.js /** @experimental */ export enum Plural { Zero, One, Two, Few, Many, Other, } ` + generateVars() + generateRules() + ` }`; } function generateRules() { const codeParts = [` const lang = locale.split('-')[0].toLowerCase(); switch (lang) {`]; Object.keys(ruleToLang).forEach(rule => { const langs = ruleToLang[rule]; codeParts.push(...langs.map(l => ` case '${l}': `)); codeParts.push(` ${rule}`); }); codeParts.push(` // When there is no specification, the default is always other // see http://cldr.unicode.org/index/cldr-spec/plural-rules // "other (required—general plural form — also used if the language only has a single form)" default: return Plural.Other; }`); return codeParts.join('\n'); } function generateVars(){ return ` /** * Returns the plural case based on the locale * * @experimental */ export function getPluralCase(locale: string, nLike: number | string): Plural { // TODO(vicb): lazy compute if (typeof nLike === 'string') { nLike = parseInt(nLike, 10); } const n: number = nLike as number; const nDecimal = n.toString().replace(/^[^.]*\\.?/, ''); const i = Math.floor(Math.abs(n)); const v = nDecimal.length; const f = parseInt(nDecimal, 10); const t = parseInt(n.toString().replace(/^[^.]*\\.?|0+$/g,''), 10) || 0; `; } function checkMapping() { if (localeToVariant.length) { console.log(`Mapping required:`); console.log(localeToVariant); throw new Error('not implemented'); } } /** * If the language rule do not match an existing language rule, flag it as variant and handle it at the end */ function getVariantLang(locale, rule) { let lang = locale.split('_')[0]; if (!langToRule[lang]) { langToRule[lang] = rule; return lang; } if (langToRule[lang] === rule) { return lang; } variants.push(locale); return null; } function normalizeRule(fn) { if (fn === DEFAULT_RULE || fn === EMPTY_RULE) return; return fn .replace(toRegExp('function anonymous(n\n/**/) {\n'), '') .replace(toRegExp('var'), 'let') .replace(toRegExp('"zero"'), ' Plural.Zero') .replace(toRegExp('"one"'), ' Plural.One') .replace(toRegExp('"two"'), ' Plural.Two') .replace(toRegExp('"few"'), ' Plural.Few') .replace(toRegExp('"many"'), ' Plural.Many') .replace(toRegExp('"other"'), ' Plural.Other') .replace(toRegExp('\n}'), '') .replace(toRegExp('let'), '') .replace(toRegExp('if(typeof n==="string")n=parseInt(n,10);'), '') .replace(toRegExp('i=Math.floor(Math.abs(n))'), '') .replace(/v=n.toString.*?.length/g, '') .replace(/f=parseInt.*?\|\|0/g, '') .replace(/t=parseInt.*?\|\|0/g, '') .replace(/^[ ,;]*/, '') + ';'; } function toRegExp(s) { return new RegExp(s.replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1'), 'g'); }