feat(common): support loading locales from a global (#33523)

To support compile time localization, we need to be
able to provide the locales via a well known global property.

This commit changes `getLocaleData()` so that it will attempt
to read the local from the global `ng.common.locales` if the
locale has not already been registered via `registerLocaleData()`.

PR Close #33523
This commit is contained in:
Pete Bacon Darwin 2019-10-31 12:55:54 +00:00 committed by atscott
parent 7e8eec57f0
commit c5894e08bc
4 changed files with 82 additions and 61 deletions

View File

@ -33,6 +33,5 @@ export {makeDecorator as ɵmakeDecorator} from './util/decorators';
export {isObservable as ɵisObservable, isPromise as ɵisPromise} from './util/lang'; export {isObservable as ɵisObservable, isPromise as ɵisPromise} from './util/lang';
export {clearOverrides as ɵclearOverrides, initServicesIfNeeded as ɵinitServicesIfNeeded, overrideComponentView as ɵoverrideComponentView, overrideProvider as ɵoverrideProvider} from './view/index'; export {clearOverrides as ɵclearOverrides, initServicesIfNeeded as ɵinitServicesIfNeeded, overrideComponentView as ɵoverrideComponentView, overrideProvider as ɵoverrideProvider} from './view/index';
export {NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR as ɵNOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR} from './view/provider'; export {NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR as ɵNOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR} from './view/provider';
export {unregisterLocaleData as ɵunregisterLocaleData, getLocalePluralCase as ɵgetLocalePluralCase, findLocaleData as ɵfindLocaleData, registerLocaleData as ɵregisterLocaleData} from './i18n/locale_data_api'; export {LocaleDataIndex as ɵLocaleDataIndex, CurrencyIndex as ɵCurrencyIndex, ExtraLocaleDataIndex as ɵExtraLocaleDataIndex, getLocalePluralCase as ɵgetLocalePluralCase, findLocaleData as ɵfindLocaleData, registerLocaleData as ɵregisterLocaleData, unregisterAllLocaleData as ɵunregisterLocaleData} from './i18n/locale_data_api';
export {LocaleDataIndex as ɵLocaleDataIndex, CurrencyIndex as ɵCurrencyIndex, ExtraLocaleDataIndex as ɵExtraLocaleDataIndex} from './i18n/locale_data';
export {allowSanitizationBypassAndThrow as ɵallowSanitizationBypassAndThrow, getSanitizationBypassType as ɵgetSanitizationBypassType, BypassType as ɵBypassType, unwrapSafeValue as ɵunwrapSafeValue, SafeHtml as ɵSafeHtml, SafeResourceUrl as ɵSafeResourceUrl, SafeScript as ɵSafeScript, SafeStyle as ɵSafeStyle, SafeUrl as ɵSafeUrl, SafeValue as ɵSafeValue} from './sanitization/bypass'; export {allowSanitizationBypassAndThrow as ɵallowSanitizationBypassAndThrow, getSanitizationBypassType as ɵgetSanitizationBypassType, BypassType as ɵBypassType, unwrapSafeValue as ɵunwrapSafeValue, SafeHtml as ɵSafeHtml, SafeResourceUrl as ɵSafeResourceUrl, SafeScript as ɵSafeScript, SafeStyle as ɵSafeStyle, SafeUrl as ɵSafeUrl, SafeValue as ɵSafeValue} from './sanitization/bypass';

View File

@ -1,52 +0,0 @@
/**
* @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
*/
/**
* This const is used to store the locale data registered with `registerLocaleData`
*/
export const LOCALE_DATA: {[localeId: string]: any} = {};
/**
* Index of each type of locale data from the locale data array
*/
export enum LocaleDataIndex {
LocaleId = 0,
DayPeriodsFormat,
DayPeriodsStandalone,
DaysFormat,
DaysStandalone,
MonthsFormat,
MonthsStandalone,
Eras,
FirstDayOfWeek,
WeekendRange,
DateFormat,
TimeFormat,
DateTimeFormat,
NumberSymbols,
NumberFormats,
CurrencySymbol,
CurrencyName,
Currencies,
PluralCase,
ExtraData
}
/**
* Index of each type of locale data from the extra locale data array
*/
export const enum ExtraLocaleDataIndex {
ExtraDayPeriodFormats = 0,
ExtraDayPeriodStandalone,
ExtraDayPeriodsRules
}
/**
* Index of each value in currency data (used to describe CURRENCIES_EN in currencies.ts)
*/
export const enum CurrencyIndex {Symbol = 0, SymbolNarrow, NbOfDigits}

View File

@ -5,9 +5,13 @@
* Use of this source code is governed by an MIT-style license that can be * 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 * found in the LICENSE file at https://angular.io/license
*/ */
import {LOCALE_DATA, LocaleDataIndex} from './locale_data';
import localeEn from './locale_en'; import localeEn from './locale_en';
import {global} from '../util/global';
/**
* This const is used to store the locale data registered with `registerLocaleData`
*/
let LOCALE_DATA: {[localeId: string]: any} = {};
/** /**
* Register locale data to be used internally by Angular. See the * Register locale data to be used internally by Angular. See the
@ -75,19 +79,64 @@ export function getLocalePluralCase(locale: string): (value: number) => number {
/** /**
* Helper function to get the given `normalizedLocale` from `LOCALE_DATA`. * Helper function to get the given `normalizedLocale` from `LOCALE_DATA`
* or from the global `ng.common.locale`.
*/ */
export function getLocaleData(normalizedLocale: string): any { export function getLocaleData(normalizedLocale: string): any {
if (!(normalizedLocale in LOCALE_DATA)) {
LOCALE_DATA[normalizedLocale] = global.ng && global.ng.common && global.ng.common.locales &&
global.ng.common.locales[normalizedLocale];
}
return LOCALE_DATA[normalizedLocale]; return LOCALE_DATA[normalizedLocale];
} }
/** /**
* Helper function to remove all the locale data from `LOCALE_DATA`. * Helper function to remove all the locale data from `LOCALE_DATA`.
*/ */
export function unregisterLocaleData() { export function unregisterAllLocaleData() {
Object.keys(LOCALE_DATA).forEach(key => delete LOCALE_DATA[key]); LOCALE_DATA = {};
} }
/**
* Index of each type of locale data from the locale data array
*/
export enum LocaleDataIndex {
LocaleId = 0,
DayPeriodsFormat,
DayPeriodsStandalone,
DaysFormat,
DaysStandalone,
MonthsFormat,
MonthsStandalone,
Eras,
FirstDayOfWeek,
WeekendRange,
DateFormat,
TimeFormat,
DateTimeFormat,
NumberSymbols,
NumberFormats,
CurrencySymbol,
CurrencyName,
Currencies,
PluralCase,
ExtraData
}
/**
* Index of each type of locale data from the extra locale data array
*/
export const enum ExtraLocaleDataIndex {
ExtraDayPeriodFormats = 0,
ExtraDayPeriodStandalone,
ExtraDayPeriodsRules
}
/**
* Index of each value in currency data (used to describe CURRENCIES_EN in currencies.ts)
*/
export const enum CurrencyIndex {Symbol = 0, SymbolNarrow, NbOfDigits}
/** /**
* Returns the canonical form of a locale name - lowercase with `_` replaced with `-`. * Returns the canonical form of a locale name - lowercase with `_` replaced with `-`.
*/ */

View File

@ -5,18 +5,23 @@
* Use of this source code is governed by an MIT-style license that can be * 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 * found in the LICENSE file at https://angular.io/license
*/ */
import {findLocaleData, registerLocaleData, unregisterLocaleData} from '../../src/i18n/locale_data_api'; import {findLocaleData, registerLocaleData, unregisterAllLocaleData} from '../../src/i18n/locale_data_api';
import {global} from '../../src/util/global';
{ {
describe('locale data api', () => { describe('locale data api', () => {
const localeCaESVALENCIA: any[] = ['ca-ES-VALENCIA']; const localeCaESVALENCIA: any[] = ['ca-ES-VALENCIA'];
const localeDe: any[] = ['de'];
const localeDeCH: any[] = ['de-CH'];
const localeEn: any[] = ['en']; const localeEn: any[] = ['en'];
const localeFr: any[] = ['fr']; const localeFr: any[] = ['fr'];
const localeFrCA: any[] = ['fr-CA']; const localeFrCA: any[] = ['fr-CA'];
const localeZh: any[] = ['zh']; const localeZh: any[] = ['zh'];
const localeEnAU: any[] = ['en-AU']; const localeEnAU: any[] = ['en-AU'];
const fakeGlobalFr: any[] = ['fr'];
beforeAll(() => { beforeAll(() => {
// Sumulate manually registering some locale data
registerLocaleData(localeCaESVALENCIA); registerLocaleData(localeCaESVALENCIA);
registerLocaleData(localeEn); registerLocaleData(localeEn);
registerLocaleData(localeFr); registerLocaleData(localeFr);
@ -25,9 +30,20 @@ import {findLocaleData, registerLocaleData, unregisterLocaleData} from '../../sr
registerLocaleData(localeFrCA, 'fake_Id2'); registerLocaleData(localeFrCA, 'fake_Id2');
registerLocaleData(localeZh); registerLocaleData(localeZh);
registerLocaleData(localeEnAU); registerLocaleData(localeEnAU);
// Simulate some locale data existing on the global already
global.ng = global.ng || {};
global.ng.common = global.ng.common || {locales: {}};
global.ng.common.locales = global.ng.common.locales || {};
global.ng.common.locales['fr'] = fakeGlobalFr;
global.ng.common.locales['de'] = localeDe;
global.ng.common.locales['de-ch'] = localeDeCH;
}); });
afterAll(() => unregisterLocaleData()); afterAll(() => {
unregisterAllLocaleData();
global.ng.common.locales = undefined;
});
describe('findLocaleData', () => { describe('findLocaleData', () => {
it('should throw if the LOCALE_DATA for the chosen locale or its parent locale is not available', it('should throw if the LOCALE_DATA for the chosen locale or its parent locale is not available',
@ -55,6 +71,15 @@ import {findLocaleData, registerLocaleData, unregisterLocaleData} from '../../sr
expect(findLocaleData('fake_iD')).toEqual(localeFr); expect(findLocaleData('fake_iD')).toEqual(localeFr);
expect(findLocaleData('fake-id2')).toEqual(localeFrCA); expect(findLocaleData('fake-id2')).toEqual(localeFrCA);
}); });
it('should find the exact LOCALE_DATA if the locale is on the global object',
() => { expect(findLocaleData('de-CH')).toEqual(localeDeCH); });
it('should find the parent LOCALE_DATA if the exact locale is not available and the parent locale is on the global object',
() => { expect(findLocaleData('de-BE')).toEqual(localeDe); });
it('should find the registered LOCALE_DATA even if the same locale is on the global object',
() => { expect(findLocaleData('fr')).not.toBe(fakeGlobalFr); });
}); });
}); });
} }