diff --git a/modules/@angular/common/index.ts b/modules/@angular/common/index.ts index 3ed795c92f..abd6a5c977 100644 --- a/modules/@angular/common/index.ts +++ b/modules/@angular/common/index.ts @@ -6,24 +6,9 @@ * found in the LICENSE file at https://angular.io/license */ -import {NgModule} from '@angular/core'; -import {COMMON_DIRECTIVES} from './src/common_directives'; -import {COMMON_PIPES} from './src/pipes'; - export * from './src/pipes'; export * from './src/directives'; export * from './src/common_directives'; export * from './src/location'; -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. -/** - * The module that includes all the basic Angular directives like {@link NgIf}, ${link NgFor}, ... - * - * @experimental - */ -@NgModule( - {declarations: [COMMON_DIRECTIVES, COMMON_PIPES], exports: [COMMON_DIRECTIVES, COMMON_PIPES]}) -export class CommonModule { -} +export {NgLocalization} from './src/localization'; +export {CommonModule} from './src/common_module'; diff --git a/modules/@angular/common/src/common_directives.ts b/modules/@angular/common/src/common_directives.ts index 3d35ff51d0..0539388de7 100644 --- a/modules/@angular/common/src/common_directives.ts +++ b/modules/@angular/common/src/common_directives.ts @@ -6,8 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -import {Provider} from '@angular/core'; - import {CORE_DIRECTIVES} from './directives'; /** diff --git a/modules/@angular/common/src/common_module.ts b/modules/@angular/common/src/common_module.ts new file mode 100644 index 0000000000..ad5311d31f --- /dev/null +++ b/modules/@angular/common/src/common_module.ts @@ -0,0 +1,30 @@ +/** + * @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 {NgModule} from '@angular/core'; + +import {COMMON_DIRECTIVES} from './common_directives'; +import {NgLocaleLocalization, NgLocalization} from './localization'; +import {COMMON_PIPES} from './pipes'; + +// Note: This does not contain the location providers, +// as they need some platform specific implementations to work. +/** + * The module that includes all the basic Angular directives like {@link NgIf}, ${link NgFor}, ... + * + * @experimental + */ +@NgModule({ + declarations: [COMMON_DIRECTIVES, COMMON_PIPES], + exports: [COMMON_DIRECTIVES, COMMON_PIPES], + providers: [ + {provide: NgLocalization, useClass: NgLocaleLocalization}, + ], +}) +export class CommonModule { +} diff --git a/modules/@angular/common/src/directives/ng_plural.ts b/modules/@angular/common/src/directives/ng_plural.ts index 476310f312..3401fb2061 100644 --- a/modules/@angular/common/src/directives/ng_plural.ts +++ b/modules/@angular/common/src/directives/ng_plural.ts @@ -18,29 +18,21 @@ import {SwitchView} from './ng_switch'; * `ngPlural` is an i18n directive that displays DOM sub-trees that match the switch expression * value, or failing that, DOM sub-trees that match the switch expression's pluralization category. * - * To use this directive, you must provide an extension of `NgLocalization` that maps values to - * category names. You then define a container element that sets the `[ngPlural]` attribute to a + * To use this directive you must provide a container element that sets the `[ngPlural]` attribute + * to a * switch expression. * - Inner elements defined with an `[ngPluralCase]` attribute will display based on their * expression. * - If `[ngPluralCase]` is set to a value starting with `=`, it will only display if the value * matches the switch expression exactly. * - Otherwise, the view will be treated as a "category match", and will only display if exact - * value matches aren't found and the value maps to its category using the `getPluralCategory` - * function provided. + * value matches aren't found and the value maps to its category for the defined locale. * * ```typescript - * class MyLocalization extends NgLocalization { - * getPluralCategory(value: any) { - * if(value < 5) { - * return 'few'; - * } - * } - * } - * * @Component({ * selector: 'app', - * providers: [{provide: NgLocalization, useClass: MyLocalization}] + * // best practice is to define the locale at the application level + * providers: [{provide: LOCALE_ID, useValue: 'en_US'}] * }) * @View({ * template: ` diff --git a/modules/@angular/common/src/localization.ts b/modules/@angular/common/src/localization.ts index 55d249223f..a2073249b1 100644 --- a/modules/@angular/common/src/localization.ts +++ b/modules/@angular/common/src/localization.ts @@ -6,7 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {Injectable} from '@angular/core'; +import {Inject, Injectable, LOCALE_ID} from '@angular/core'; + /** * @experimental */ @@ -34,7 +35,7 @@ export function getPluralCategory( */ @Injectable() export class NgLocaleLocalization extends NgLocalization { - constructor(private _locale: string) { super(); } + constructor(@Inject(LOCALE_ID) private _locale: string) { super(); } getPluralCategory(value: any): string { const plural = getPluralCase(this._locale, value); diff --git a/modules/@angular/common/src/pipes/i18n_plural_pipe.ts b/modules/@angular/common/src/pipes/i18n_plural_pipe.ts index f8b5e9843f..cd2064db5a 100644 --- a/modules/@angular/common/src/pipes/i18n_plural_pipe.ts +++ b/modules/@angular/common/src/pipes/i18n_plural_pipe.ts @@ -26,14 +26,6 @@ const _INTERPOLATION_REGEXP: RegExp = /#/g; * ## Example * * ``` - * class MyLocalization extends NgLocalization { - * getPluralCategory(value: any) { - * if(value > 1) { - * return 'other'; - * } - * } - * } - * * @Component({ * selector: 'app', * template: ` @@ -41,7 +33,8 @@ const _INTERPOLATION_REGEXP: RegExp = /#/g; * {{ messages.length | i18nPlural: messageMapping }} * * `, - * providers: [{provide: NgLocalization, useClass: MyLocalization}] + * // best practice is to define the locale at the application level + * providers: [{provide: LOCALE_ID, useValue: 'en_US'}] * }) * * class MyApp { diff --git a/modules/@angular/common/test/localization_spec.ts b/modules/@angular/common/test/localization_spec.ts index e3ea80bbcd..75b29d3b3e 100644 --- a/modules/@angular/common/test/localization_spec.ts +++ b/modules/@angular/common/test/localization_spec.ts @@ -6,6 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ +import {LOCALE_ID} from '@angular/core'; +import {TestBed} from '@angular/core/testing'; import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, iit, inject, it, xit} from '@angular/core/testing/testing_internal'; import {expect} from '@angular/platform-browser/testing/matchers'; @@ -13,92 +15,131 @@ import {NgLocaleLocalization, NgLocalization, getPluralCategory} from '../src/lo export function main() { - describe('localization', () => { + describe('l10n', () => { + + describe('NgLocalization', () => { + describe('ro', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [{provide: LOCALE_ID, useValue: 'ro'}], + }); + }); + + it('should return plural cases for the provided locale', + inject([NgLocalization], (l10n: NgLocalization) => { + expect(l10n.getPluralCategory(0)).toEqual('few'); + expect(l10n.getPluralCategory(1)).toEqual('one'); + expect(l10n.getPluralCategory(1212)).toEqual('few'); + expect(l10n.getPluralCategory(1223)).toEqual('other'); + })); + }); + + describe('sr', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [{provide: LOCALE_ID, useValue: 'sr'}], + }); + }); + + it('should return plural cases for the provided locale', + inject([NgLocalization], (l10n: NgLocalization) => { + expect(l10n.getPluralCategory(1)).toEqual('one'); + expect(l10n.getPluralCategory(2.1)).toEqual('one'); + + expect(l10n.getPluralCategory(3)).toEqual('few'); + expect(l10n.getPluralCategory(0.2)).toEqual('few'); + + expect(l10n.getPluralCategory(2.11)).toEqual('other'); + expect(l10n.getPluralCategory(2.12)).toEqual('other'); + })); + }); + }); + describe('NgLocaleLocalization', () => { it('should return the correct values for the "en" locale', () => { - const localization = new NgLocaleLocalization('en_US'); + const l10n = new NgLocaleLocalization('en_US'); - expect(localization.getPluralCategory(0)).toEqual('other'); - expect(localization.getPluralCategory(1)).toEqual('one'); - expect(localization.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 localization = new NgLocaleLocalization('ro'); + const l10n = 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'); + 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 localization = new NgLocaleLocalization('sr'); + const l10n = 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(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(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(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(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(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(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(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(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(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(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'); + 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'); }); }); describe('getPluralCategory', () => { it('should return plural category', () => { - const localization = new FrLocalization(); + const l10n = 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'); + expect(getPluralCategory(0, ['one', 'other'], l10n)).toEqual('one'); + expect(getPluralCategory(1, ['one', 'other'], l10n)).toEqual('one'); + expect(getPluralCategory(5, ['one', 'other'], l10n)).toEqual('other'); }); it('should return discrete cases', () => { - const localization = new FrLocalization(); + const l10n = 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'); + expect(getPluralCategory(0, ['one', 'other', '=0'], l10n)).toEqual('=0'); + expect(getPluralCategory(1, ['one', 'other'], l10n)).toEqual('one'); + expect(getPluralCategory(5, ['one', 'other', '=5'], l10n)).toEqual('=5'); + expect(getPluralCategory(6, ['one', 'other', '=5'], l10n)).toEqual('other'); }); }); }); diff --git a/modules/@angular/core/src/application_module.ts b/modules/@angular/core/src/application_module.ts index 3c2edb1770..f6e28af62c 100644 --- a/modules/@angular/core/src/application_module.ts +++ b/modules/@angular/core/src/application_module.ts @@ -10,6 +10,7 @@ import {ApplicationInitStatus} from './application_init'; import {ApplicationRef, ApplicationRef_} from './application_ref'; import {APP_ID_RANDOM_PROVIDER} from './application_tokens'; import {IterableDiffers, KeyValueDiffers, defaultIterableDiffers, defaultKeyValueDiffers} from './change_detection/change_detection'; +import {LOCALE_ID} from './i18n/tokens'; import {Compiler} from './linker/compiler'; import {ComponentResolver} from './linker/component_resolver'; import {DynamicComponentLoader, DynamicComponentLoader_} from './linker/dynamic_component_loader'; @@ -51,6 +52,7 @@ export const APPLICATION_COMMON_PROVIDERS: Array|{[k: string]: any}|an {provide: IterableDiffers, useFactory: _iterableDiffersFactory}, {provide: KeyValueDiffers, useFactory: _keyValueDiffersFactory}, {provide: DynamicComponentLoader, useClass: DynamicComponentLoader_}, + {provide: LOCALE_ID, useValue: 'en_US'}, ] }) export class ApplicationModule { diff --git a/modules/@angular/core/test/application_module_spec.ts b/modules/@angular/core/test/application_module_spec.ts new file mode 100644 index 0000000000..49ec8e1cf2 --- /dev/null +++ b/modules/@angular/core/test/application_module_spec.ts @@ -0,0 +1,17 @@ +/** + * @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 {LOCALE_ID} from '@angular/core'; +import {beforeEach, beforeEachProviders, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '../testing/testing_internal'; + +export function main() { + describe('Application module', () => { + it('should set the default locale to "en_US"', + inject([LOCALE_ID], (defaultLocale: string) => { expect(defaultLocale).toEqual('en_US'); })); + }); +} \ No newline at end of file diff --git a/tools/public_api_guard/common/index.d.ts b/tools/public_api_guard/common/index.d.ts index 3e22bf021b..17c97ff14d 100644 --- a/tools/public_api_guard/common/index.d.ts +++ b/tools/public_api_guard/common/index.d.ts @@ -36,9 +36,6 @@ 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); @@ -132,12 +129,6 @@ export declare class NgIf { constructor(_viewContainer: ViewContainerRef, _templateRef: TemplateRef); } -/** @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; @@ -222,16 +213,6 @@ 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, -} - /** @stable */ export declare class SlicePipe implements PipeTransform { transform(value: any, start: number, end?: number): any;