feat(i18n): Add NgLocaleLocalization which returns plural cases given a locale (#10744)

This commit is contained in:
Victor Berchet 2016-08-12 14:46:06 -07:00 committed by vikerman
parent 6580d67875
commit 161a4dd15f
8 changed files with 841 additions and 5 deletions

View File

@ -14,7 +14,7 @@ export * from './src/pipes';
export * from './src/directives';
export * from './src/common_directives';
export * from './src/location';
export {NgLocalization} from './src/localization';
export {NgLocalization, NgLocaleLocalization, Plural, getPluralCase} from './src/localization';
// Note: This does not contain the location providers,
// as they need some platform specific implementations to work.

View File

@ -6,11 +6,13 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Injectable} from '@angular/core';
/**
* @experimental
*/
export abstract class NgLocalization { abstract getPluralCategory(value: any): string; }
/**
* Returns the plural category for a given value.
* - "=value" when the case exists,
@ -24,3 +26,400 @@ export function getPluralCategory(
return cases.indexOf(nbCase) > -1 ? nbCase : ngLocalization.getPluralCategory(value);
}
/**
* Returns the plural case based on the locale
*
* @experimental
*/
@Injectable()
export class NgLocaleLocalization extends NgLocalization {
constructor(private _locale: string) { super(); }
getPluralCategory(value: any): string {
const plural = getPluralCase(this._locale, value);
switch (plural) {
case Plural.Zero:
return 'zero';
case Plural.One:
return 'one';
case Plural.Two:
return 'two';
case Plural.Few:
return 'few';
case Plural.Many:
return 'many';
default:
return 'other';
}
}
}
// This is generated code DO NOT MODIFY
// see angular2/script/cldr/gen_plural_rules.js
/** @experimental */
export enum Plural {
Zero,
One,
Two,
Few,
Many,
Other
}
/**
* 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(<string>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;
const lang = locale.split('_')[0].toLowerCase();
switch (lang) {
case 'af':
case 'asa':
case 'az':
case 'bem':
case 'bez':
case 'bg':
case 'brx':
case 'ce':
case 'cgg':
case 'chr':
case 'ckb':
case 'ee':
case 'el':
case 'eo':
case 'es':
case 'eu':
case 'fo':
case 'fur':
case 'gsw':
case 'ha':
case 'haw':
case 'hu':
case 'jgo':
case 'jmc':
case 'ka':
case 'kk':
case 'kkj':
case 'kl':
case 'ks':
case 'ksb':
case 'ky':
case 'lb':
case 'lg':
case 'mas':
case 'mgo':
case 'ml':
case 'mn':
case 'nb':
case 'nd':
case 'ne':
case 'nn':
case 'nnh':
case 'nyn':
case 'om':
case 'or':
case 'os':
case 'ps':
case 'rm':
case 'rof':
case 'rwk':
case 'saq':
case 'seh':
case 'sn':
case 'so':
case 'sq':
case 'ta':
case 'te':
case 'teo':
case 'tk':
case 'tr':
case 'ug':
case 'uz':
case 'vo':
case 'vun':
case 'wae':
case 'xog':
if (n === 1) return Plural.One;
return Plural.Other;
case 'agq':
case 'bas':
case 'cu':
case 'dav':
case 'dje':
case 'dua':
case 'dyo':
case 'ebu':
case 'ewo':
case 'guz':
case 'kam':
case 'khq':
case 'ki':
case 'kln':
case 'kok':
case 'ksf':
case 'lrc':
case 'lu':
case 'luo':
case 'luy':
case 'mer':
case 'mfe':
case 'mgh':
case 'mua':
case 'mzn':
case 'nmg':
case 'nus':
case 'qu':
case 'rn':
case 'rw':
case 'sbp':
case 'twq':
case 'vai':
case 'yav':
case 'yue':
case 'zgh':
case 'ak':
case 'ln':
case 'mg':
case 'pa':
case 'ti':
if (n === Math.floor(n) && n >= 0 && n <= 1) return Plural.One;
return Plural.Other;
case 'am':
case 'as':
case 'bn':
case 'fa':
case 'gu':
case 'hi':
case 'kn':
case 'mr':
case 'zu':
if (i === 0 || n === 1) return Plural.One;
return Plural.Other;
case 'ar':
if (n === 0) return Plural.Zero;
if (n === 1) return Plural.One;
if (n === 2) return Plural.Two;
if (n % 100 === Math.floor(n % 100) && n % 100 >= 3 && n % 100 <= 10) return Plural.Few;
if (n % 100 === Math.floor(n % 100) && n % 100 >= 11 && n % 100 <= 99) return Plural.Many;
return Plural.Other;
case 'ast':
case 'ca':
case 'de':
case 'en':
case 'et':
case 'fi':
case 'fy':
case 'gl':
case 'it':
case 'nl':
case 'sv':
case 'sw':
case 'ur':
case 'yi':
if (i === 1 && v === 0) return Plural.One;
return Plural.Other;
case 'be':
if (n % 10 === 1 && !(n % 100 === 11)) return Plural.One;
if (n % 10 === Math.floor(n % 10) && n % 10 >= 2 && n % 10 <= 4 &&
!(n % 100 >= 12 && n % 100 <= 14))
return Plural.Few;
if (n % 10 === 0 || n % 10 === Math.floor(n % 10) && n % 10 >= 5 && n % 10 <= 9 ||
n % 100 === Math.floor(n % 100) && n % 100 >= 11 && n % 100 <= 14)
return Plural.Many;
return Plural.Other;
case 'br':
if (n % 10 === 1 && !(n % 100 === 11 || n % 100 === 71 || n % 100 === 91)) return Plural.One;
if (n % 10 === 2 && !(n % 100 === 12 || n % 100 === 72 || n % 100 === 92)) return Plural.Two;
if (n % 10 === Math.floor(n % 10) && (n % 10 >= 3 && n % 10 <= 4 || n % 10 === 9) &&
!(n % 100 >= 10 && n % 100 <= 19 || n % 100 >= 70 && n % 100 <= 79 ||
n % 100 >= 90 && n % 100 <= 99))
return Plural.Few;
if (!(n === 0) && n % 1e6 === 0) return Plural.Many;
return Plural.Other;
case 'bs':
case 'hr':
case 'sr':
// https://github.com/papandreou/node-cldr/issues/31
if (v === 0 && i % 10 === 1 && (!(i % 100 === 11)) || f % 10 === 1 && !(f % 100 === 11))
return Plural.One;
if (v === 0 && i % 10 === Math.floor(i % 10) && i % 10 >= 2 && i % 10 <= 4 &&
(!(i % 100 >= 12 && i % 100 <= 14)) ||
f % 10 === Math.floor(f % 10) && f % 10 >= 2 && f % 10 <= 4 &&
!(f % 100 >= 12 && f % 100 <= 14))
return Plural.Few;
return Plural.Other;
case 'cs':
case 'sk':
if (i === 1 && v === 0) return Plural.One;
if (i === Math.floor(i) && i >= 2 && i <= 4 && v === 0) return Plural.Few;
if (!(v === 0)) return Plural.Many;
return Plural.Other;
case 'cy':
if (n === 0) return Plural.Zero;
if (n === 1) return Plural.One;
if (n === 2) return Plural.Two;
if (n === 3) return Plural.Few;
if (n === 6) return Plural.Many;
return Plural.Other;
case 'da':
if (n === 1 || !(t === 0) && (i === 0 || i === 1)) return Plural.One;
return Plural.Other;
case 'dsb':
case 'hsb':
if (v === 0 && (i % 100 === 1 || f % 100 === 1)) return Plural.One;
if (v === 0 && (i % 100 === 2 || f % 100 === 2)) return Plural.Two;
if (v === 0 && (i % 100 === Math.floor(i % 100) && i % 100 >= 3 && i % 100 <= 4 ||
f % 100 === Math.floor(f % 100) && f % 100 >= 3 && f % 100 <= 4))
return Plural.Few;
return Plural.Other;
case 'ff':
case 'fr':
case 'hy':
case 'kab':
if (i === 0 || i === 1) return Plural.One;
return Plural.Other;
case 'fil':
if (v === 0 && (i === 1 || i === 2 || i === 3 ||
v === 0 && (!(i % 10 === 4 || i % 10 === 6 || i % 10 === 9) ||
!(v === 0) && !(f % 10 === 4 || f % 10 === 6 || f % 10 === 9))))
return Plural.One;
return Plural.Other;
case 'ga':
if (n === 1) return Plural.One;
if (n === 2) return Plural.Two;
if (n === Math.floor(n) && n >= 3 && n <= 6) return Plural.Few;
if (n === Math.floor(n) && n >= 7 && n <= 10) return Plural.Many;
return Plural.Other;
case 'gd':
if (n === 1 || n === 11) return Plural.One;
if (n === 2 || n === 12) return Plural.Two;
if (n === Math.floor(n) && (n >= 3 && n <= 10 || n >= 13 && n <= 19)) return Plural.Few;
return Plural.Other;
case 'gv':
if (v === 0 && i % 10 === 1) return Plural.One;
if (v === 0 && i % 10 === 2) return Plural.Two;
if (v === 0 &&
(i % 100 === 0 || i % 100 === 20 || i % 100 === 40 || i % 100 === 60 || i % 100 === 80))
return Plural.Few;
if (!(v === 0)) return Plural.Many;
return Plural.Other;
case 'he':
if (i === 1 && v === 0) return Plural.One;
if (i === 2 && v === 0) return Plural.Two;
if (v === 0 && !(n >= 0 && n <= 10) && n % 10 === 0) return Plural.Many;
return Plural.Other;
case 'is':
if (t === 0 && i % 10 === 1 && (!(i % 100 === 11) || !(t === 0))) return Plural.One;
return Plural.Other;
case 'ksh':
if (n === 0) return Plural.Zero;
if (n === 1) return Plural.One;
return Plural.Other;
case 'kw':
case 'naq':
case 'se':
case 'smn':
if (n === 1) return Plural.One;
if (n === 2) return Plural.Two;
return Plural.Other;
case 'lag':
if (n === 0) return Plural.Zero;
if ((i === 0 || i === 1) && !(n === 0)) return Plural.One;
return Plural.Other;
case 'lt':
if (n % 10 === 1 && !(n % 100 >= 11 && n % 100 <= 19)) return Plural.One;
if (n % 10 === Math.floor(n % 10) && n % 10 >= 2 && n % 10 <= 9 &&
!(n % 100 >= 11 && n % 100 <= 19))
return Plural.Few;
if (!(f === 0)) return Plural.Many;
return Plural.Other;
case 'lv':
case 'prg':
if (n % 10 === 0 || n % 100 === Math.floor(n % 100) && n % 100 >= 11 && n % 100 <= 19 ||
v === 2 && f % 100 === Math.floor(f % 100) && f % 100 >= 11 && f % 100 <= 19)
return Plural.Zero;
if (n % 10 === 1 &&
(!(n % 100 === 11) ||
v === 2 && f % 10 === 1 && (!(f % 100 === 11) || !(v === 2) && f % 10 === 1)))
return Plural.One;
return Plural.Other;
case 'mk':
if (v === 0 && (i % 10 === 1 || f % 10 === 1)) return Plural.One;
return Plural.Other;
case 'mt':
if (n === 1) return Plural.One;
if (n === 0 || n % 100 === Math.floor(n % 100) && n % 100 >= 2 && n % 100 <= 10)
return Plural.Few;
if (n % 100 === Math.floor(n % 100) && n % 100 >= 11 && n % 100 <= 19) return Plural.Many;
return Plural.Other;
case 'pl':
if (i === 1 && v === 0) return Plural.One;
if (v === 0 && i % 10 === Math.floor(i % 10) && i % 10 >= 2 && i % 10 <= 4 &&
!(i % 100 >= 12 && i % 100 <= 14))
return Plural.Few;
if (v === 0 && !(i === 1) &&
(i % 10 === Math.floor(i % 10) && i % 10 >= 0 && i % 10 <= 1 ||
v === 0 &&
(i % 10 === Math.floor(i % 10) && i % 10 >= 5 && i % 10 <= 9 ||
v === 0 && i % 100 === Math.floor(i % 100) && i % 100 >= 12 && i % 100 <= 14)))
return Plural.Many;
return Plural.Other;
case 'pt':
if (n === Math.floor(n) && n >= 0 && n <= 2 && !(n === 2)) return Plural.One;
return Plural.Other;
case 'ro':
if (i === 1 && v === 0) return Plural.One;
if (!(v === 0) || n === 0 ||
!(n === 1) && n % 100 === Math.floor(n % 100) && n % 100 >= 1 && n % 100 <= 19)
return Plural.Few;
return Plural.Other;
case 'ru':
case 'uk':
if (v === 0 && i % 10 === 1 && !(i % 100 === 11)) return Plural.One;
if (v === 0 && i % 10 === Math.floor(i % 10) && i % 10 >= 2 && i % 10 <= 4 &&
!(i % 100 >= 12 && i % 100 <= 14))
return Plural.Few;
if (v === 0 && (i % 10 === 0 ||
v === 0 && (i % 10 === Math.floor(i % 10) && i % 10 >= 5 && i % 10 <= 9 ||
v === 0 && i % 100 === Math.floor(i % 100) && i % 100 >= 11 &&
i % 100 <= 14)))
return Plural.Many;
return Plural.Other;
case 'shi':
if (i === 0 || n === 1) return Plural.One;
if (n === Math.floor(n) && n >= 2 && n <= 10) return Plural.Few;
return Plural.Other;
case 'si':
if (n === 0 || n === 1 || i === 0 && f === 1) return Plural.One;
return Plural.Other;
case 'sl':
if (v === 0 && i % 100 === 1) return Plural.One;
if (v === 0 && i % 100 === 2) return Plural.Two;
if (v === 0 &&
(i % 100 === Math.floor(i % 100) && i % 100 >= 3 && i % 100 <= 4 || !(v === 0)))
return Plural.Few;
return Plural.Other;
case 'tzm':
if (n === Math.floor(n) && n >= 0 && n <= 1 || n === Math.floor(n) && n >= 11 && n <= 99)
return Plural.One;
return Plural.Other;
default:
return Plural.Other;
}
}

View File

@ -0,0 +1,117 @@
/**
* @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
*/
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
import {expect} from '@angular/platform-browser/testing/matchers';
import {NgLocaleLocalization, NgLocalization, getPluralCategory} from '../src/localization';
export function main() {
describe('localization', () => {
describe('NgLocaleLocalization', () => {
it('should return the correct values for the "en" locale', () => {
const localization = new NgLocaleLocalization('en_US');
expect(localization.getPluralCategory(0)).toEqual('other');
expect(localization.getPluralCategory(1)).toEqual('one');
expect(localization.getPluralCategory(2)).toEqual('other');
});
it('should return the correct values for the "ro" locale', () => {
const localization = new NgLocaleLocalization('ro');
expect(localization.getPluralCategory(0)).toEqual('few');
expect(localization.getPluralCategory(1)).toEqual('one');
expect(localization.getPluralCategory(2)).toEqual('few');
expect(localization.getPluralCategory(12)).toEqual('few');
expect(localization.getPluralCategory(23)).toEqual('other');
expect(localization.getPluralCategory(1212)).toEqual('few');
expect(localization.getPluralCategory(1223)).toEqual('other');
});
it('should return the correct values for the "sr" locale', () => {
const localization = new NgLocaleLocalization('sr');
expect(localization.getPluralCategory(1)).toEqual('one');
expect(localization.getPluralCategory(31)).toEqual('one');
expect(localization.getPluralCategory(0.1)).toEqual('one');
expect(localization.getPluralCategory(1.1)).toEqual('one');
expect(localization.getPluralCategory(2.1)).toEqual('one');
expect(localization.getPluralCategory(3)).toEqual('few');
expect(localization.getPluralCategory(33)).toEqual('few');
expect(localization.getPluralCategory(0.2)).toEqual('few');
expect(localization.getPluralCategory(0.3)).toEqual('few');
expect(localization.getPluralCategory(0.4)).toEqual('few');
expect(localization.getPluralCategory(2.2)).toEqual('few');
expect(localization.getPluralCategory(2.11)).toEqual('other');
expect(localization.getPluralCategory(2.12)).toEqual('other');
expect(localization.getPluralCategory(2.13)).toEqual('other');
expect(localization.getPluralCategory(2.14)).toEqual('other');
expect(localization.getPluralCategory(2.15)).toEqual('other');
expect(localization.getPluralCategory(0)).toEqual('other');
expect(localization.getPluralCategory(5)).toEqual('other');
expect(localization.getPluralCategory(10)).toEqual('other');
expect(localization.getPluralCategory(35)).toEqual('other');
expect(localization.getPluralCategory(37)).toEqual('other');
expect(localization.getPluralCategory(40)).toEqual('other');
expect(localization.getPluralCategory(0.0)).toEqual('other');
expect(localization.getPluralCategory(0.5)).toEqual('other');
expect(localization.getPluralCategory(0.6)).toEqual('other');
expect(localization.getPluralCategory(2)).toEqual('few');
expect(localization.getPluralCategory(2.1)).toEqual('one');
expect(localization.getPluralCategory(2.2)).toEqual('few');
expect(localization.getPluralCategory(2.3)).toEqual('few');
expect(localization.getPluralCategory(2.4)).toEqual('few');
expect(localization.getPluralCategory(2.5)).toEqual('other');
expect(localization.getPluralCategory(20)).toEqual('other');
expect(localization.getPluralCategory(21)).toEqual('one');
expect(localization.getPluralCategory(22)).toEqual('few');
expect(localization.getPluralCategory(23)).toEqual('few');
expect(localization.getPluralCategory(24)).toEqual('few');
expect(localization.getPluralCategory(25)).toEqual('other');
});
});
describe('getPluralCategory', () => {
it('should return plural category', () => {
const localization = new FrLocalization();
expect(getPluralCategory(0, ['one', 'other'], localization)).toEqual('one');
expect(getPluralCategory(1, ['one', 'other'], localization)).toEqual('one');
expect(getPluralCategory(5, ['one', 'other'], localization)).toEqual('other');
});
it('should return discrete cases', () => {
const localization = new FrLocalization();
expect(getPluralCategory(0, ['one', 'other', '=0'], localization)).toEqual('=0');
expect(getPluralCategory(1, ['one', 'other'], localization)).toEqual('one');
expect(getPluralCategory(5, ['one', 'other', '=5'], localization)).toEqual('=5');
expect(getPluralCategory(6, ['one', 'other', '=5'], localization)).toEqual('other');
});
});
});
}
class FrLocalization extends NgLocalization {
getPluralCategory(value: number): string {
switch (value) {
case 0:
case 1:
return 'one';
default:
return 'other';
}
}
}

View File

@ -1660,6 +1660,17 @@
}
}
},
"cldr": {
"version": "3.5.0",
"dependencies": {
"uglify-js": {
"version": "1.3.3"
},
"underscore": {
"version": "1.3.3"
}
}
},
"cli-color": {
"version": "1.1.0",
"dependencies": {
@ -3283,6 +3294,9 @@
"hash.js": {
"version": "1.0.3"
},
"hashish": {
"version": "0.0.4"
},
"hawk": {
"version": "3.1.2"
},
@ -3990,6 +4004,17 @@
"media-typer": {
"version": "0.3.0"
},
"memoizeasync": {
"version": "0.8.0",
"dependencies": {
"lru-cache": {
"version": "2.5.0"
},
"passerror": {
"version": "0.0.2"
}
}
},
"memoizee": {
"version": "0.3.10"
},
@ -4341,6 +4366,9 @@
"parseurl": {
"version": "1.3.0"
},
"passerror": {
"version": "0.0.1"
},
"path-array": {
"version": "1.0.0"
},
@ -4379,6 +4407,9 @@
"pbkdf2-compat": {
"version": "2.0.1"
},
"pegjs": {
"version": "0.7.0"
},
"pify": {
"version": "2.3.0"
},
@ -4844,6 +4875,14 @@
"send": {
"version": "0.13.0"
},
"seq": {
"version": "0.3.5",
"dependencies": {
"chainsaw": {
"version": "0.0.9"
}
}
},
"sequencify": {
"version": "0.0.7"
},
@ -5420,6 +5459,9 @@
"underscore.string": {
"version": "2.3.3"
},
"unicoderegexp": {
"version": "0.4.1"
},
"unique-stream": {
"version": "1.0.0"
},
@ -5696,9 +5738,15 @@
}
}
},
"xmldom": {
"version": "0.1.19"
},
"xmlhttprequest-ssl": {
"version": "1.5.1"
},
"xpath": {
"version": "0.0.7"
},
"xtend": {
"version": "4.0.1"
},
@ -5730,5 +5778,5 @@
}
},
"name": "angular-srcs",
"version": "2.0.0-rc.4"
"version": "2.0.0-rc.5"
}

84
npm-shrinkwrap.json generated
View File

@ -1,6 +1,6 @@
{
"name": "angular-srcs",
"version": "2.0.0-rc.4",
"version": "2.0.0-rc.5",
"dependencies": {
"@types/angularjs": {
"version": "1.5.13-alpha",
@ -2598,6 +2598,23 @@
}
}
},
"cldr": {
"version": "3.5.0",
"from": "cldr@latest",
"resolved": "https://registry.npmjs.org/cldr/-/cldr-3.5.0.tgz",
"dependencies": {
"uglify-js": {
"version": "1.3.3",
"from": "uglify-js@1.3.3",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-1.3.3.tgz"
},
"underscore": {
"version": "1.3.3",
"from": "underscore@1.3.3",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.3.3.tgz"
}
}
},
"cli-color": {
"version": "1.1.0",
"from": "cli-color@>=1.0.0 <2.0.0",
@ -2878,7 +2895,8 @@
},
"through2": {
"version": "2.0.1",
"from": "through2@>=2.0.0 <3.0.0"
"from": "through2@2.0.1",
"resolved": "https://registry.npmjs.org/through2/-/through2-2.0.1.tgz"
}
}
},
@ -2899,7 +2917,8 @@
},
"lodash": {
"version": "4.14.2",
"from": "lodash@>=4.2.1 <5.0.0"
"from": "lodash@4.14.2",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.14.2.tgz"
},
"readable-stream": {
"version": "2.0.6",
@ -5204,6 +5223,11 @@
"from": "hash.js@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.0.3.tgz"
},
"hashish": {
"version": "0.0.4",
"from": "hashish@>=0.0.2 <0.1.0",
"resolved": "https://registry.npmjs.org/hashish/-/hashish-0.0.4.tgz"
},
"hawk": {
"version": "3.1.2",
"from": "hawk@>=3.1.0 <3.2.0",
@ -6341,6 +6365,23 @@
"from": "media-typer@0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz"
},
"memoizeasync": {
"version": "0.8.0",
"from": "memoizeasync@0.8.0",
"resolved": "https://registry.npmjs.org/memoizeasync/-/memoizeasync-0.8.0.tgz",
"dependencies": {
"lru-cache": {
"version": "2.5.0",
"from": "lru-cache@2.5.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz"
},
"passerror": {
"version": "0.0.2",
"from": "passerror@0.0.2",
"resolved": "https://registry.npmjs.org/passerror/-/passerror-0.0.2.tgz"
}
}
},
"memoizee": {
"version": "0.3.10",
"from": "memoizee@>=0.3.9 <0.4.0",
@ -6910,6 +6951,11 @@
"from": "parseurl@>=1.3.0 <1.4.0",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.0.tgz"
},
"passerror": {
"version": "0.0.1",
"from": "passerror@0.0.1",
"resolved": "https://registry.npmjs.org/passerror/-/passerror-0.0.1.tgz"
},
"path-array": {
"version": "1.0.0",
"from": "path-array@>=1.0.0 <2.0.0",
@ -6972,6 +7018,11 @@
"from": "pbkdf2-compat@2.0.1",
"resolved": "https://registry.npmjs.org/pbkdf2-compat/-/pbkdf2-compat-2.0.1.tgz"
},
"pegjs": {
"version": "0.7.0",
"from": "pegjs@0.7.0",
"resolved": "https://registry.npmjs.org/pegjs/-/pegjs-0.7.0.tgz"
},
"pify": {
"version": "2.3.0",
"from": "pify@>=2.0.0 <3.0.0",
@ -7727,6 +7778,18 @@
"from": "send@0.13.0",
"resolved": "https://registry.npmjs.org/send/-/send-0.13.0.tgz"
},
"seq": {
"version": "0.3.5",
"from": "seq@0.3.5",
"resolved": "https://registry.npmjs.org/seq/-/seq-0.3.5.tgz",
"dependencies": {
"chainsaw": {
"version": "0.0.9",
"from": "chainsaw@>=0.0.7 <0.1.0",
"resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.0.9.tgz"
}
}
},
"sequencify": {
"version": "0.0.7",
"from": "sequencify@>=0.0.7 <0.1.0",
@ -8643,6 +8706,11 @@
"from": "underscore.string@>=2.3.3 <2.4.0",
"resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz"
},
"unicoderegexp": {
"version": "0.4.1",
"from": "unicoderegexp@0.4.1",
"resolved": "https://registry.npmjs.org/unicoderegexp/-/unicoderegexp-0.4.1.tgz"
},
"unique-stream": {
"version": "1.0.0",
"from": "unique-stream@>=1.0.0 <2.0.0",
@ -9087,11 +9155,21 @@
}
}
},
"xmldom": {
"version": "0.1.19",
"from": "xmldom@0.1.19",
"resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.19.tgz"
},
"xmlhttprequest-ssl": {
"version": "1.5.1",
"from": "xmlhttprequest-ssl@1.5.1",
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.1.tgz"
},
"xpath": {
"version": "0.0.7",
"from": "xpath@0.0.7",
"resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.7.tgz"
},
"xtend": {
"version": "4.0.1",
"from": "xtend@>=4.0.0 <5.0.0",

View File

@ -47,6 +47,7 @@
"canonical-path": "0.0.2",
"chokidar": "^1.1.0",
"clang-format": "^1.0.32",
"cldr": "^3.5.0",
"conventional-changelog": "^1.1.0",
"cors": "^2.7.1",
"firefox-profile": "^0.3.4",

View File

@ -0,0 +1,174 @@
/**
* @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}`;
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);
});
var nextVariantCode = 'a'.charCodeAt(0);
variants.forEach(locale => {
const rule = normalizeRule(cldr.extractPluralRuleFunction(locale).toString());
if (!rule) {
return;
}
var 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 angular2/script/cldr/gen_plural_rules.js
enum Plural {
Zero,
One,
Two,
Few,
Many,
Other
}
function getPluralCase(locale: string, n: number|string): Plural {
` + 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(` default:
return Plural.Other;
}`);
return codeParts.join('\n');
}
function generateVars(){
return `
function getPluralCase(locale: string, nLike: number | string): Plural {
// TODO(vicb): lazy compute
if (typeof nLike === 'string') {
nLike = parseInt(<string>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) {
var 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) 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');
}

View File

@ -36,6 +36,9 @@ export declare class DecimalPipe implements PipeTransform {
transform(value: any, digits?: string): string;
}
/** @experimental */
export declare function getPluralCase(locale: string, nLike: number | string): Plural;
/** @stable */
export declare class HashLocationStrategy extends LocationStrategy {
constructor(_platformLocation: PlatformLocation, _baseHref?: string);
@ -129,6 +132,12 @@ export declare class NgIf {
constructor(_viewContainer: ViewContainerRef, _templateRef: TemplateRef<Object>);
}
/** @experimental */
export declare class NgLocaleLocalization extends NgLocalization {
constructor(_locale: string);
getPluralCategory(value: any): string;
}
/** @experimental */
export declare abstract class NgLocalization {
abstract getPluralCategory(value: any): string;
@ -213,6 +222,16 @@ export declare abstract class PlatformLocation {
abstract replaceState(state: any, title: string, url: string): void;
}
/** @experimental */
export declare enum Plural {
Zero = 0,
One = 1,
Two = 2,
Few = 3,
Many = 4,
Other = 5,
}
/** @deprecated */
export declare class ReplacePipe implements PipeTransform {
transform(value: any, pattern: string | RegExp, replacement: Function | string): any;