| 
									
										
										
										
											2016-08-12 14:46:06 -07:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @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}`; | 
					
						
							| 
									
										
										
										
											2017-05-30 19:46:36 +02:00
										 |  |  | const EMPTY_RULE = `function anonymous(n\n/**/) {\n\n}`; | 
					
						
							| 
									
										
										
										
											2016-08-12 14:46:06 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 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); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-30 19:46:36 +02:00
										 |  |  | let nextVariantCode = 'a'.charCodeAt(0); | 
					
						
							| 
									
										
										
										
											2016-08-12 14:46:06 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | variants.forEach(locale => { | 
					
						
							|  |  |  |   const rule = normalizeRule(cldr.extractPluralRuleFunction(locale).toString()); | 
					
						
							|  |  |  |   if (!rule) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-30 19:46:36 +02:00
										 |  |  |   let mapTo = null; | 
					
						
							| 
									
										
										
										
											2016-08-12 14:46:06 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   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
 | 
					
						
							| 
									
										
										
										
											2017-05-30 19:46:36 +02:00
										 |  |  | // see angular/script/cldr/gen_plural_rules.js
 | 
					
						
							| 
									
										
										
										
											2016-08-12 14:46:06 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-30 19:46:36 +02:00
										 |  |  | /** @experimental */ | 
					
						
							|  |  |  | export enum Plural { | 
					
						
							| 
									
										
										
										
											2016-08-12 14:46:06 -07:00
										 |  |  |   Zero, | 
					
						
							|  |  |  |   One, | 
					
						
							|  |  |  |   Two, | 
					
						
							|  |  |  |   Few, | 
					
						
							|  |  |  |   Many, | 
					
						
							| 
									
										
										
										
											2017-05-30 19:46:36 +02:00
										 |  |  |   Other, | 
					
						
							| 
									
										
										
										
											2016-08-12 14:46:06 -07:00
										 |  |  | } | 
					
						
							|  |  |  | ` + generateVars() +
 | 
					
						
							|  |  |  |     generateRules() + `
 | 
					
						
							|  |  |  | }`;
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function generateRules() { | 
					
						
							|  |  |  |   const codeParts = [`
 | 
					
						
							| 
									
										
										
										
											2017-05-30 19:46:36 +02:00
										 |  |  | const lang = locale.split('-')[0].toLowerCase(); | 
					
						
							| 
									
										
										
										
											2016-08-12 14:46:06 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | switch (lang) {`];
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Object.keys(ruleToLang).forEach(rule => { | 
					
						
							|  |  |  |     const langs = ruleToLang[rule]; | 
					
						
							|  |  |  |     codeParts.push(...langs.map(l => `  case '${l}': `)); | 
					
						
							|  |  |  |     codeParts.push(`    ${rule}`); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-30 19:46:36 +02:00
										 |  |  |   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; | 
					
						
							| 
									
										
										
										
											2016-08-12 14:46:06 -07:00
										 |  |  | }`);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return codeParts.join('\n'); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function generateVars(){ | 
					
						
							|  |  |  |   return `
 | 
					
						
							| 
									
										
										
										
											2017-05-30 19:46:36 +02:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Returns the plural case based on the locale | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @experimental | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | export function getPluralCase(locale: string, nLike: number | string): Plural { | 
					
						
							| 
									
										
										
										
											2016-08-12 14:46:06 -07:00
										 |  |  | // TODO(vicb): lazy compute
 | 
					
						
							|  |  |  | if (typeof nLike === 'string') { | 
					
						
							|  |  |  |   nLike = parseInt(<string>nLike, 10); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | const n: number = nLike as number; | 
					
						
							| 
									
										
										
										
											2017-05-30 19:46:36 +02:00
										 |  |  | const nDecimal = n.toString().replace(/^[^.]*\\.?/, '');  | 
					
						
							| 
									
										
										
										
											2016-08-12 14:46:06 -07:00
										 |  |  | const i = Math.floor(Math.abs(n)); | 
					
						
							|  |  |  | const v = nDecimal.length; | 
					
						
							|  |  |  | const f = parseInt(nDecimal, 10);  | 
					
						
							| 
									
										
										
										
											2017-05-30 19:46:36 +02:00
										 |  |  | const t = parseInt(n.toString().replace(/^[^.]*\\.?|0+$/g,''), 10) || 0; | 
					
						
							| 
									
										
										
										
											2016-08-12 14:46:06 -07:00
										 |  |  | `;
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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) { | 
					
						
							| 
									
										
										
										
											2017-05-30 19:46:36 +02:00
										 |  |  |   let lang = locale.split('_')[0]; | 
					
						
							| 
									
										
										
										
											2016-08-12 14:46:06 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   if (!langToRule[lang]) { | 
					
						
							|  |  |  |     langToRule[lang] = rule; | 
					
						
							|  |  |  |     return lang; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (langToRule[lang] === rule) { | 
					
						
							|  |  |  |     return lang; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   variants.push(locale); | 
					
						
							|  |  |  |   return null; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function normalizeRule(fn) { | 
					
						
							| 
									
										
										
										
											2017-05-30 19:46:36 +02:00
										 |  |  |   if (fn === DEFAULT_RULE || fn === EMPTY_RULE) return; | 
					
						
							| 
									
										
										
										
											2016-08-12 14:46:06 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   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'); | 
					
						
							| 
									
										
										
										
											2017-05-30 19:46:36 +02:00
										 |  |  | } |