From 30d53a8942f633d92f6c75d9b892ac2e527d989d Mon Sep 17 00:00:00 2001 From: Olivier Combe Date: Thu, 31 Aug 2017 02:38:39 +0200 Subject: [PATCH] fix(common): use v4 plurals when importing `DeprecatedI18NPipesModule` (#18955) --- packages/common/src/common_module.ts | 9 +- packages/common/src/i18n/localization.ts | 334 +++++++++++++++++- packages/common/src/pipes/deprecated/index.ts | 2 +- packages/common/src/pipes/i18n_plural_pipe.ts | 4 +- packages/common/test/common_module_spec.ts | 21 ++ .../common/test/i18n/localization_spec.ts | 210 ++++++----- tools/public_api_guard/common/common.d.ts | 3 +- 7 files changed, 487 insertions(+), 96 deletions(-) create mode 100644 packages/common/test/common_module_spec.ts diff --git a/packages/common/src/common_module.ts b/packages/common/src/common_module.ts index dd7750f86fa68..35cc06e750ac7 100644 --- a/packages/common/src/common_module.ts +++ b/packages/common/src/common_module.ts @@ -7,9 +7,8 @@ */ import {NgModule} from '@angular/core'; - import {COMMON_DIRECTIVES} from './directives/index'; -import {NgLocaleLocalization, NgLocalization} from './i18n/localization'; +import {NgLocaleLocalization, NgLocalization, USE_V4_PLURALS} from './i18n/localization'; import {COMMON_DEPRECATED_I18N_PIPES} from './pipes/deprecated/index'; import {COMMON_PIPES} from './pipes/index'; @@ -36,6 +35,10 @@ export class CommonModule { * * @deprecated from v5 */ -@NgModule({declarations: [COMMON_DEPRECATED_I18N_PIPES], exports: [COMMON_DEPRECATED_I18N_PIPES]}) +@NgModule({ + declarations: [COMMON_DEPRECATED_I18N_PIPES], + exports: [COMMON_DEPRECATED_I18N_PIPES], + providers: [{provide: USE_V4_PLURALS, useValue: true}], +}) export class DeprecatedI18NPipesModule { } diff --git a/packages/common/src/i18n/localization.ts b/packages/common/src/i18n/localization.ts index 7b1ba6e943574..2f80540714139 100644 --- a/packages/common/src/i18n/localization.ts +++ b/packages/common/src/i18n/localization.ts @@ -6,9 +6,15 @@ * found in the LICENSE file at https://angular.io/license */ -import {Inject, Injectable, LOCALE_ID} from '@angular/core'; +import {Inject, Injectable, InjectionToken, LOCALE_ID, Optional} from '@angular/core'; import {Plural, getLocalePluralCase} from './locale_data_api'; + +/** + * @deprecated from v5 + */ +export const USE_V4_PLURALS = new InjectionToken('UseV4Plurals'); + /** * @experimental */ @@ -52,10 +58,15 @@ export function getPluralCategory( */ @Injectable() export class NgLocaleLocalization extends NgLocalization { - constructor(@Inject(LOCALE_ID) protected locale: string) { super(); } + constructor( + @Inject(LOCALE_ID) protected locale: string, + @Optional() @Inject(USE_V4_PLURALS) protected useV4Plurals?: boolean) { + super(); + } getPluralCategory(value: any, locale?: string): string { - const plural = getLocalePluralCase(locale || this.locale)(value); + const plural = this.useV4Plurals ? getPluralCase(locale || this.locale, value) : + getLocalePluralCase(locale || this.locale)(value); switch (plural) { case Plural.Zero: @@ -73,3 +84,320 @@ export class NgLocaleLocalization extends NgLocalization { } } } + +/** + * Returns the plural case based on the locale + * + * @deprecated from v5 the plural case function is in locale data files common/locales/*.ts + * @experimental + */ +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; + + 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 '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': + 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; + // When there is no specification, the default is always "other" + // Spec: 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; + } +} diff --git a/packages/common/src/pipes/deprecated/index.ts b/packages/common/src/pipes/deprecated/index.ts index 935583234bcbc..2d2bbd7c900e9 100644 --- a/packages/common/src/pipes/deprecated/index.ts +++ b/packages/common/src/pipes/deprecated/index.ts @@ -21,7 +21,7 @@ export { /** * A collection of deprecated i18n pipes that require intl api * - * @deprecated + * @deprecated from v5 */ export const COMMON_DEPRECATED_I18N_PIPES: Provider[] = [DeprecatedDecimalPipe, DeprecatedPercentPipe, DeprecatedCurrencyPipe, DeprecatedDatePipe]; diff --git a/packages/common/src/pipes/i18n_plural_pipe.ts b/packages/common/src/pipes/i18n_plural_pipe.ts index 0d3ca1b65ea06..7f2dd640ad0e0 100644 --- a/packages/common/src/pipes/i18n_plural_pipe.ts +++ b/packages/common/src/pipes/i18n_plural_pipe.ts @@ -6,10 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {Inject, LOCALE_ID, Pipe, PipeTransform} from '@angular/core'; - +import {LOCALE_ID, Pipe, PipeTransform} from '@angular/core'; import {NgLocalization, getPluralCategory} from '../i18n/localization'; - import {invalidPipeArgumentError} from './invalid_pipe_argument_error'; const _INTERPOLATION_REGEXP: RegExp = /#/g; diff --git a/packages/common/test/common_module_spec.ts b/packages/common/test/common_module_spec.ts new file mode 100644 index 0000000000000..1af7fa87b4c89 --- /dev/null +++ b/packages/common/test/common_module_spec.ts @@ -0,0 +1,21 @@ +/** + * @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 {TestBed, inject} from '@angular/core/testing'; +import {DeprecatedI18NPipesModule} from '../src/common_module'; +import {USE_V4_PLURALS} from '../src/i18n/localization'; + +export function main() { + describe('DeprecatedI18NPipesModule', () => { + beforeEach(() => { TestBed.configureTestingModule({imports: [DeprecatedI18NPipesModule]}); }); + + it('should define the token USE_V4_PLURALS to true', + inject([USE_V4_PLURALS], (useV4Plurals: true) => { expect(useV4Plurals).toEqual(true); })); + }); +} diff --git a/packages/common/test/i18n/localization_spec.ts b/packages/common/test/i18n/localization_spec.ts index dde85eb6c11c6..6607bd3541d8a 100644 --- a/packages/common/test/i18n/localization_spec.ts +++ b/packages/common/test/i18n/localization_spec.ts @@ -12,7 +12,7 @@ import localeZgh from '../../locales/zgh'; import localeFr from '../../locales/fr'; import {LOCALE_ID} from '@angular/core'; import {TestBed, inject} from '@angular/core/testing'; -import {NgLocaleLocalization, NgLocalization, getPluralCategory} from '../../src/i18n/localization'; +import {NgLocaleLocalization, NgLocalization, getPluralCategory, USE_V4_PLURALS} from '../../src/i18n/localization'; import {registerLocaleData} from '../../src/i18n/locale_data'; export function main() { @@ -25,13 +25,7 @@ export function main() { }); describe('NgLocalization', () => { - describe('ro', () => { - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [{provide: LOCALE_ID, useValue: 'ro'}], - }); - }); - + function roTests() { it('should return plural cases for the provided locale', inject([NgLocalization], (l10n: NgLocalization) => { expect(l10n.getPluralCategory(0)).toEqual('few'); @@ -39,15 +33,30 @@ export function main() { expect(l10n.getPluralCategory(1212)).toEqual('few'); expect(l10n.getPluralCategory(1223)).toEqual('other'); })); + } + + describe('ro', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [{provide: LOCALE_ID, useValue: 'ro'}], + }); + }); + + roTests(); }); - describe('sr', () => { + describe('ro with v4 plurals', () => { beforeEach(() => { TestBed.configureTestingModule({ - providers: [{provide: LOCALE_ID, useValue: 'sr'}], + providers: + [{provide: LOCALE_ID, useValue: 'ro'}, {provide: USE_V4_PLURALS, useValue: true}], }); }); + roTests(); + }); + + function srTests() { it('should return plural cases for the provided locale', inject([NgLocalization], (l10n: NgLocalization) => { expect(l10n.getPluralCategory(1)).toEqual('one'); @@ -59,91 +68,117 @@ export function main() { expect(l10n.getPluralCategory(2.11)).toEqual('other'); expect(l10n.getPluralCategory(2.12)).toEqual('other'); })); + } + + describe('sr', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [{provide: LOCALE_ID, useValue: 'sr'}], + }); + }); + + srTests(); + }); + + describe('sr with v4 plurals', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: + [{provide: LOCALE_ID, useValue: 'sr'}, {provide: USE_V4_PLURALS, useValue: true}], + }); + }); + + srTests(); }); }); describe('NgLocaleLocalization', () => { - it('should return the correct values for the "en" locale', () => { - const l10n = new NgLocaleLocalization('en-US'); + function ngLocaleLocalizationTests(useV4Plurals: boolean) { + it('should return the correct values for the "en" locale', () => { + const l10n = new NgLocaleLocalization('en-US', useV4Plurals); - expect(l10n.getPluralCategory(0)).toEqual('other'); - expect(l10n.getPluralCategory(1)).toEqual('one'); - expect(l10n.getPluralCategory(2)).toEqual('other'); - }); + expect(l10n.getPluralCategory(0)).toEqual('other'); + expect(l10n.getPluralCategory(1)).toEqual('one'); + expect(l10n.getPluralCategory(2)).toEqual('other'); + }); - it('should return the correct values for the "ro" locale', () => { - const l10n = new NgLocaleLocalization('ro'); + it('should return the correct values for the "ro" locale', () => { + const l10n = new NgLocaleLocalization('ro', useV4Plurals); - expect(l10n.getPluralCategory(0)).toEqual('few'); - expect(l10n.getPluralCategory(1)).toEqual('one'); - expect(l10n.getPluralCategory(2)).toEqual('few'); - expect(l10n.getPluralCategory(12)).toEqual('few'); - expect(l10n.getPluralCategory(23)).toEqual('other'); - expect(l10n.getPluralCategory(1212)).toEqual('few'); - expect(l10n.getPluralCategory(1223)).toEqual('other'); - }); + expect(l10n.getPluralCategory(0)).toEqual('few'); + expect(l10n.getPluralCategory(1)).toEqual('one'); + expect(l10n.getPluralCategory(2)).toEqual('few'); + expect(l10n.getPluralCategory(12)).toEqual('few'); + expect(l10n.getPluralCategory(23)).toEqual('other'); + expect(l10n.getPluralCategory(1212)).toEqual('few'); + expect(l10n.getPluralCategory(1223)).toEqual('other'); + }); - it('should return the correct values for the "sr" locale', () => { - const l10n = new NgLocaleLocalization('sr'); - - expect(l10n.getPluralCategory(1)).toEqual('one'); - expect(l10n.getPluralCategory(31)).toEqual('one'); - expect(l10n.getPluralCategory(0.1)).toEqual('one'); - expect(l10n.getPluralCategory(1.1)).toEqual('one'); - expect(l10n.getPluralCategory(2.1)).toEqual('one'); - - expect(l10n.getPluralCategory(3)).toEqual('few'); - expect(l10n.getPluralCategory(33)).toEqual('few'); - expect(l10n.getPluralCategory(0.2)).toEqual('few'); - expect(l10n.getPluralCategory(0.3)).toEqual('few'); - expect(l10n.getPluralCategory(0.4)).toEqual('few'); - expect(l10n.getPluralCategory(2.2)).toEqual('few'); - - expect(l10n.getPluralCategory(2.11)).toEqual('other'); - expect(l10n.getPluralCategory(2.12)).toEqual('other'); - expect(l10n.getPluralCategory(2.13)).toEqual('other'); - expect(l10n.getPluralCategory(2.14)).toEqual('other'); - expect(l10n.getPluralCategory(2.15)).toEqual('other'); - - expect(l10n.getPluralCategory(0)).toEqual('other'); - expect(l10n.getPluralCategory(5)).toEqual('other'); - expect(l10n.getPluralCategory(10)).toEqual('other'); - expect(l10n.getPluralCategory(35)).toEqual('other'); - expect(l10n.getPluralCategory(37)).toEqual('other'); - expect(l10n.getPluralCategory(40)).toEqual('other'); - expect(l10n.getPluralCategory(0.0)).toEqual('other'); - expect(l10n.getPluralCategory(0.5)).toEqual('other'); - expect(l10n.getPluralCategory(0.6)).toEqual('other'); - - expect(l10n.getPluralCategory(2)).toEqual('few'); - expect(l10n.getPluralCategory(2.1)).toEqual('one'); - expect(l10n.getPluralCategory(2.2)).toEqual('few'); - expect(l10n.getPluralCategory(2.3)).toEqual('few'); - expect(l10n.getPluralCategory(2.4)).toEqual('few'); - expect(l10n.getPluralCategory(2.5)).toEqual('other'); - - expect(l10n.getPluralCategory(20)).toEqual('other'); - expect(l10n.getPluralCategory(21)).toEqual('one'); - expect(l10n.getPluralCategory(22)).toEqual('few'); - expect(l10n.getPluralCategory(23)).toEqual('few'); - expect(l10n.getPluralCategory(24)).toEqual('few'); - expect(l10n.getPluralCategory(25)).toEqual('other'); - }); + it('should return the correct values for the "sr" locale', () => { + const l10n = new NgLocaleLocalization('sr', useV4Plurals); + + expect(l10n.getPluralCategory(1)).toEqual('one'); + expect(l10n.getPluralCategory(31)).toEqual('one'); + expect(l10n.getPluralCategory(0.1)).toEqual('one'); + expect(l10n.getPluralCategory(1.1)).toEqual('one'); + expect(l10n.getPluralCategory(2.1)).toEqual('one'); + + expect(l10n.getPluralCategory(3)).toEqual('few'); + expect(l10n.getPluralCategory(33)).toEqual('few'); + expect(l10n.getPluralCategory(0.2)).toEqual('few'); + expect(l10n.getPluralCategory(0.3)).toEqual('few'); + expect(l10n.getPluralCategory(0.4)).toEqual('few'); + expect(l10n.getPluralCategory(2.2)).toEqual('few'); + + expect(l10n.getPluralCategory(2.11)).toEqual('other'); + expect(l10n.getPluralCategory(2.12)).toEqual('other'); + expect(l10n.getPluralCategory(2.13)).toEqual('other'); + expect(l10n.getPluralCategory(2.14)).toEqual('other'); + expect(l10n.getPluralCategory(2.15)).toEqual('other'); + + expect(l10n.getPluralCategory(0)).toEqual('other'); + expect(l10n.getPluralCategory(5)).toEqual('other'); + expect(l10n.getPluralCategory(10)).toEqual('other'); + expect(l10n.getPluralCategory(35)).toEqual('other'); + expect(l10n.getPluralCategory(37)).toEqual('other'); + expect(l10n.getPluralCategory(40)).toEqual('other'); + expect(l10n.getPluralCategory(0.0)).toEqual('other'); + expect(l10n.getPluralCategory(0.5)).toEqual('other'); + expect(l10n.getPluralCategory(0.6)).toEqual('other'); + + expect(l10n.getPluralCategory(2)).toEqual('few'); + expect(l10n.getPluralCategory(2.1)).toEqual('one'); + expect(l10n.getPluralCategory(2.2)).toEqual('few'); + expect(l10n.getPluralCategory(2.3)).toEqual('few'); + expect(l10n.getPluralCategory(2.4)).toEqual('few'); + expect(l10n.getPluralCategory(2.5)).toEqual('other'); + + expect(l10n.getPluralCategory(20)).toEqual('other'); + expect(l10n.getPluralCategory(21)).toEqual('one'); + expect(l10n.getPluralCategory(22)).toEqual('few'); + expect(l10n.getPluralCategory(23)).toEqual('few'); + expect(l10n.getPluralCategory(24)).toEqual('few'); + expect(l10n.getPluralCategory(25)).toEqual('other'); + }); - it('should return the default value for a locale with no rule', () => { - const l10n = new NgLocaleLocalization('zgh'); + it('should return the default value for a locale with no rule', () => { + const l10n = new NgLocaleLocalization('zgh', useV4Plurals); - expect(l10n.getPluralCategory(0)).toEqual('other'); - expect(l10n.getPluralCategory(1)).toEqual('other'); - expect(l10n.getPluralCategory(3)).toEqual('other'); - expect(l10n.getPluralCategory(5)).toEqual('other'); - expect(l10n.getPluralCategory(10)).toEqual('other'); - }); + expect(l10n.getPluralCategory(0)).toEqual('other'); + expect(l10n.getPluralCategory(1)).toEqual('other'); + expect(l10n.getPluralCategory(3)).toEqual('other'); + expect(l10n.getPluralCategory(5)).toEqual('other'); + expect(l10n.getPluralCategory(10)).toEqual('other'); + }); + } + + ngLocaleLocalizationTests(true); + ngLocaleLocalizationTests(false); }); - describe('getPluralCategory', () => { + function pluralCategoryTests(useV4Plurals: boolean) { it('should return plural category', () => { - const l10n = new NgLocaleLocalization('fr'); + const l10n = new NgLocaleLocalization('fr', useV4Plurals); expect(getPluralCategory(0, ['one', 'other'], l10n)).toEqual('one'); expect(getPluralCategory(1, ['one', 'other'], l10n)).toEqual('one'); @@ -151,7 +186,7 @@ export function main() { }); it('should return discrete cases', () => { - const l10n = new NgLocaleLocalization('fr'); + const l10n = new NgLocaleLocalization('fr', useV4Plurals); expect(getPluralCategory(0, ['one', 'other', '=0'], l10n)).toEqual('=0'); expect(getPluralCategory(1, ['one', 'other'], l10n)).toEqual('one'); @@ -160,7 +195,7 @@ export function main() { }); it('should fallback to other when the case is not present', () => { - const l10n = new NgLocaleLocalization('ro'); + const l10n = new NgLocaleLocalization('ro', useV4Plurals); expect(getPluralCategory(1, ['one', 'other'], l10n)).toEqual('one'); // 2 -> 'few' expect(getPluralCategory(2, ['one', 'other'], l10n)).toEqual('other'); @@ -169,12 +204,17 @@ export function main() { describe('errors', () => { it('should report an error when the "other" category is not present', () => { expect(() => { - const l10n = new NgLocaleLocalization('ro'); + const l10n = new NgLocaleLocalization('ro', useV4Plurals); // 2 -> 'few' getPluralCategory(2, ['one'], l10n); }).toThrowError('No plural message found for value "2"'); }); }); + } + + describe('getPluralCategory', () => { + pluralCategoryTests(false); + pluralCategoryTests(true); }); }); } diff --git a/tools/public_api_guard/common/common.d.ts b/tools/public_api_guard/common/common.d.ts index 05cadf276a0fe..48ebbfa4bad54 100644 --- a/tools/public_api_guard/common/common.d.ts +++ b/tools/public_api_guard/common/common.d.ts @@ -287,7 +287,8 @@ export declare class NgIfContext { /** @experimental */ export declare class NgLocaleLocalization extends NgLocalization { protected locale: string; - constructor(locale: string); + protected useV4Plurals: boolean; + constructor(locale: string, useV4Plurals?: boolean); getPluralCategory(value: any, locale?: string): string; }