Skip to content

Commit

Permalink
refactor(common): move the low level locale registering to core (#33523)
Browse files Browse the repository at this point in the history
To limit the exposure of the private `LOCALE_DATA` from outside
`@angular/core` this commit exposes private functions in the core
to hide the internal structures better.

* The `registerLocaleData()` implementation has moved from
`@angular/common` to `@angular/core`. A stub that delegates to
core has been left in common for backward compatibility.
* A new `ɵunregisterLocaleData()` function has been provided,
which is particularly useful in tests to clear out registered locales
to prevent subsequent tests from being affected.
* A private export of `ɵregisterLocaleData()` has been removed
from `@angular/common`. This was not being used and is accessible
via `@angular/core` anyway.

PR Close #33523
  • Loading branch information
petebacondarwin authored and atscott committed Nov 5, 2019
1 parent 5b21b71 commit 7e8eec5
Show file tree
Hide file tree
Showing 13 changed files with 187 additions and 125 deletions.
1 change: 0 additions & 1 deletion packages/common/public_api.ts
Expand Up @@ -12,6 +12,5 @@
* Entry point for all public APIs of this package.
*/
export * from './src/common';
export {registerLocaleData as ɵregisterLocaleData} from './src/i18n/locale_data';

// This file only reexports content of the `src` folder. Keep it that way.
32 changes: 4 additions & 28 deletions packages/common/src/i18n/locale_data.ts
Expand Up @@ -6,40 +6,16 @@
* found in the LICENSE file at https://angular.io/license
*/

import {ɵLOCALE_DATA as LOCALE_DATA, ɵLocaleDataIndex as LocaleDataIndex} from '@angular/core';
import {ɵregisterLocaleData} from '@angular/core';

/**
* Register global data to be used internally by Angular. See the
* ["I18n guide"](guide/i18n#i18n-pipes) to know how to import additional locale data.
*
* The signature registerLocaleData(data: any, extraData?: any) is deprecated since v5.1
*
* @publicApi
*/
// The signature registerLocaleData(data: any, extraData?: any) is deprecated since v5.1
export function registerLocaleData(data: any, localeId?: string | any, extraData?: any): void {
if (typeof localeId !== 'string') {
extraData = localeId;
localeId = data[LocaleDataIndex.LocaleId];
}

localeId = localeId.toLowerCase().replace(/_/g, '-');

LOCALE_DATA[localeId] = data;

if (extraData) {
LOCALE_DATA[localeId][LocaleDataIndex.ExtraData] = extraData;
}
return ɵregisterLocaleData(data, localeId, 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}
15 changes: 7 additions & 8 deletions packages/common/src/i18n/locale_data_api.ts
Expand Up @@ -6,9 +6,8 @@
* found in the LICENSE file at https://angular.io/license
*/

import {ɵLocaleDataIndex, ɵfindLocaleData, ɵgetLocalePluralCase} from '@angular/core';
import {ɵCurrencyIndex, ɵExtraLocaleDataIndex, ɵLocaleDataIndex, ɵfindLocaleData, ɵgetLocalePluralCase} from '@angular/core';
import {CURRENCIES_EN, CurrenciesSymbols} from './currencies';
import {CurrencyIndex, ExtraLocaleDataIndex} from './locale_data';

/**
* Format styles that can be used to represent numbers.
Expand Down Expand Up @@ -524,7 +523,7 @@ function checkFullData(data: any) {
export function getLocaleExtraDayPeriodRules(locale: string): (Time | [Time, Time])[] {
const data = ɵfindLocaleData(locale);
checkFullData(data);
const rules = data[ɵLocaleDataIndex.ExtraData][ExtraLocaleDataIndex.ExtraDayPeriodsRules] || [];
const rules = data[ɵLocaleDataIndex.ExtraData][ɵExtraLocaleDataIndex.ExtraDayPeriodsRules] || [];
return rules.map((rule: string | [string, string]) => {
if (typeof rule === 'string') {
return extractTime(rule);
Expand Down Expand Up @@ -555,8 +554,8 @@ export function getLocaleExtraDayPeriods(
const data = ɵfindLocaleData(locale);
checkFullData(data);
const dayPeriodsData = <string[][][]>[
data[ɵLocaleDataIndex.ExtraData][ExtraLocaleDataIndex.ExtraDayPeriodFormats],
data[ɵLocaleDataIndex.ExtraData][ExtraLocaleDataIndex.ExtraDayPeriodStandalone]
data[ɵLocaleDataIndex.ExtraData][ɵExtraLocaleDataIndex.ExtraDayPeriodFormats],
data[ɵLocaleDataIndex.ExtraData][ɵExtraLocaleDataIndex.ExtraDayPeriodStandalone]
];
const dayPeriods = getLastDefinedValue(dayPeriodsData, formStyle) || [];
return getLastDefinedValue(dayPeriods, width) || [];
Expand Down Expand Up @@ -621,13 +620,13 @@ function extractTime(time: string): Time {
*/
export function getCurrencySymbol(code: string, format: 'wide' | 'narrow', locale = 'en'): string {
const currency = getLocaleCurrencies(locale)[code] || CURRENCIES_EN[code] || [];
const symbolNarrow = currency[CurrencyIndex.SymbolNarrow];
const symbolNarrow = currency[ɵCurrencyIndex.SymbolNarrow];

if (format === 'narrow' && typeof symbolNarrow === 'string') {
return symbolNarrow;
}

return currency[CurrencyIndex.Symbol] || code;
return currency[ɵCurrencyIndex.Symbol] || code;
}

// Most currencies have cents, that's why the default is 2
Expand All @@ -647,7 +646,7 @@ export function getNumberOfCurrencyDigits(code: string): number {
let digits;
const currency = CURRENCIES_EN[code];
if (currency) {
digits = currency[CurrencyIndex.NbOfDigits];
digits = currency[ɵCurrencyIndex.NbOfDigits];
}
return typeof digits === 'number' ? digits : DEFAULT_NB_OF_CURRENCY_DIGITS;
}
18 changes: 9 additions & 9 deletions packages/common/test/i18n/format_date_spec.ts
Expand Up @@ -5,8 +5,6 @@
* 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 {registerLocaleData} from '@angular/common';
import localeAr from '@angular/common/locales/ar';
import localeDe from '@angular/common/locales/de';
import localeEn from '@angular/common/locales/en';
Expand All @@ -15,7 +13,7 @@ import localeHu from '@angular/common/locales/hu';
import localeSr from '@angular/common/locales/sr';
import localeTh from '@angular/common/locales/th';
import {isDate, toDate, formatDate} from '@angular/common/src/i18n/format_date';
import {ɵDEFAULT_LOCALE_ID} from '@angular/core';
import {ɵDEFAULT_LOCALE_ID, ɵunregisterLocaleData, ɵregisterLocaleData} from '@angular/core';

describe('Format date', () => {
describe('toDate', () => {
Expand Down Expand Up @@ -57,14 +55,16 @@ describe('Format date', () => {
}

beforeAll(() => {
registerLocaleData(localeEn, localeEnExtra);
registerLocaleData(localeDe);
registerLocaleData(localeHu);
registerLocaleData(localeSr);
registerLocaleData(localeTh);
registerLocaleData(localeAr);
ɵregisterLocaleData(localeEn, localeEnExtra);
ɵregisterLocaleData(localeDe);
ɵregisterLocaleData(localeHu);
ɵregisterLocaleData(localeSr);
ɵregisterLocaleData(localeTh);
ɵregisterLocaleData(localeAr);
});

afterAll(() => ɵunregisterLocaleData());

beforeEach(() => { date = new Date(2015, 5, 15, 9, 3, 1, 550); });

it('should format each component correctly', () => {
Expand Down
14 changes: 8 additions & 6 deletions packages/common/test/i18n/format_number_spec.ts
Expand Up @@ -10,18 +10,20 @@ import localeEn from '@angular/common/locales/en';
import localeEsUS from '@angular/common/locales/es-US';
import localeFr from '@angular/common/locales/fr';
import localeAr from '@angular/common/locales/ar';
import {formatCurrency, formatNumber, formatPercent, registerLocaleData} from '@angular/common';
import {formatCurrency, formatNumber, formatPercent} from '@angular/common';
import {describe, expect, it} from '@angular/core/testing/src/testing_internal';
import {ɵDEFAULT_LOCALE_ID} from '@angular/core';
import {ɵDEFAULT_LOCALE_ID, ɵunregisterLocaleData, ɵregisterLocaleData} from '@angular/core';

describe('Format number', () => {
beforeAll(() => {
registerLocaleData(localeEn);
registerLocaleData(localeEsUS);
registerLocaleData(localeFr);
registerLocaleData(localeAr);
ɵregisterLocaleData(localeEn);
ɵregisterLocaleData(localeEsUS);
ɵregisterLocaleData(localeFr);
ɵregisterLocaleData(localeAr);
});

afterAll(() => ɵunregisterLocaleData());

describe('Number', () => {
describe('transform', () => {
it('should return correct value for numbers', () => {
Expand Down
46 changes: 7 additions & 39 deletions packages/common/test/i18n/locale_data_api_spec.ts
Expand Up @@ -6,56 +6,24 @@
* found in the LICENSE file at https://angular.io/license
*/

import {ɵfindLocaleData} from '@angular/core';
import localeCaESVALENCIA from '@angular/common/locales/ca-ES-VALENCIA';
import {ɵregisterLocaleData, ɵunregisterLocaleData} from '@angular/core';

import localeEn from '@angular/common/locales/en';
import localeFr from '@angular/common/locales/fr';
import localeZh from '@angular/common/locales/zh';
import localeFrCA from '@angular/common/locales/fr-CA';
import localeEnAU from '@angular/common/locales/en-AU';
import {registerLocaleData} from '../../src/i18n/locale_data';
import {getCurrencySymbol, getLocaleDateFormat, FormatWidth, getNumberOfCurrencyDigits} from '../../src/i18n/locale_data_api';

{
describe('locale data api', () => {
beforeAll(() => {
registerLocaleData(localeCaESVALENCIA);
registerLocaleData(localeEn);
registerLocaleData(localeFr);
registerLocaleData(localeFrCA);
registerLocaleData(localeFr, 'fake-id');
registerLocaleData(localeFrCA, 'fake_Id2');
registerLocaleData(localeZh);
registerLocaleData(localeEnAU);
ɵregisterLocaleData(localeEn);
ɵregisterLocaleData(localeFr);
ɵregisterLocaleData(localeZh);
ɵregisterLocaleData(localeEnAU);
});

describe('findLocaleData', () => {
it('should throw if the LOCALE_DATA for the chosen locale or its parent locale is not available',
() => {
expect(() => ɵfindLocaleData('pt-AO'))
.toThrowError(/Missing locale data for the locale "pt-AO"/);
});

it('should return english data if the locale is en-US',
() => { expect(ɵfindLocaleData('en-US')).toEqual(localeEn); });

it('should return the exact LOCALE_DATA if it is available',
() => { expect(ɵfindLocaleData('fr-CA')).toEqual(localeFrCA); });

it('should return the parent LOCALE_DATA if it exists and exact locale is not available',
() => { expect(ɵfindLocaleData('fr-BE')).toEqual(localeFr); });

it(`should find the LOCALE_DATA even if the locale id is badly formatted`, () => {
expect(ɵfindLocaleData('ca-ES-VALENCIA')).toEqual(localeCaESVALENCIA);
expect(ɵfindLocaleData('CA_es_Valencia')).toEqual(localeCaESVALENCIA);
});

it(`should find the LOCALE_DATA if the locale id was registered`, () => {
expect(ɵfindLocaleData('fake-id')).toEqual(localeFr);
expect(ɵfindLocaleData('fake_iD')).toEqual(localeFr);
expect(ɵfindLocaleData('fake-id2')).toEqual(localeFrCA);
});
});
afterAll(() => { ɵunregisterLocaleData(); });

describe('getting currency symbol', () => {
it('should return the correct symbol', () => {
Expand Down
13 changes: 7 additions & 6 deletions packages/common/test/i18n/localization_spec.ts
Expand Up @@ -10,20 +10,21 @@ import localeRo from '@angular/common/locales/ro';
import localeSr from '@angular/common/locales/sr';
import localeZgh from '@angular/common/locales/zgh';
import localeFr from '@angular/common/locales/fr';
import {LOCALE_ID} from '@angular/core';
import {LOCALE_ID, ɵunregisterLocaleData, ɵregisterLocaleData} from '@angular/core';
import {TestBed, inject} from '@angular/core/testing';
import {NgLocaleLocalization, NgLocalization, getPluralCategory} from '@angular/common/src/i18n/localization';
import {registerLocaleData} from '../../src/i18n/locale_data';

{
describe('l10n', () => {
beforeAll(() => {
registerLocaleData(localeRo);
registerLocaleData(localeSr);
registerLocaleData(localeZgh);
registerLocaleData(localeFr);
ɵregisterLocaleData(localeRo);
ɵregisterLocaleData(localeSr);
ɵregisterLocaleData(localeZgh);
ɵregisterLocaleData(localeFr);
});

afterAll(() => ɵunregisterLocaleData());

describe('NgLocalization', () => {
function roTests() {
it('should return plural cases for the provided locale',
Expand Down
11 changes: 4 additions & 7 deletions packages/common/test/pipes/date_pipe_spec.ts
Expand Up @@ -6,24 +6,21 @@
* found in the LICENSE file at https://angular.io/license
*/

import {DatePipe, registerLocaleData} from '@angular/common';
import {DatePipe} from '@angular/common';
import localeEn from '@angular/common/locales/en';
import localeEnExtra from '@angular/common/locales/extra/en';
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
import {JitReflector} from '@angular/platform-browser-dynamic/src/compiler_reflector';
import {ɵunregisterLocaleData, ɵregisterLocaleData} from '@angular/core';

{
let date: Date;
describe('DatePipe', () => {
const isoStringWithoutTime = '2015-01-01';
let pipe: DatePipe;

// Check the transformation of a date into a pattern
function expectDateFormatAs(date: Date | string, pattern: any, output: string): void {
expect(pipe.transform(date, pattern)).toEqual(output);
}

beforeAll(() => { registerLocaleData(localeEn, localeEnExtra); });
beforeAll(() => ɵregisterLocaleData(localeEn, localeEnExtra));
afterAll(() => ɵunregisterLocaleData());

beforeEach(() => {
date = new Date(2015, 5, 15, 9, 3, 1, 550);
Expand Down
15 changes: 9 additions & 6 deletions packages/common/test/pipes/number_pipe_spec.ts
Expand Up @@ -11,19 +11,22 @@ import localeEsUS from '@angular/common/locales/es-US';
import localeFr from '@angular/common/locales/fr';
import localeAr from '@angular/common/locales/ar';
import localeDeAt from '@angular/common/locales/de-AT';
import {registerLocaleData, CurrencyPipe, DecimalPipe, PercentPipe, formatNumber} from '@angular/common';
import {ɵunregisterLocaleData, ɵregisterLocaleData} from '@angular/core';
import {CurrencyPipe, DecimalPipe, PercentPipe} from '@angular/common';
import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testing_internal';

{
describe('Number pipes', () => {
beforeAll(() => {
registerLocaleData(localeEn);
registerLocaleData(localeEsUS);
registerLocaleData(localeFr);
registerLocaleData(localeAr);
registerLocaleData(localeDeAt);
ɵregisterLocaleData(localeEn);
ɵregisterLocaleData(localeEsUS);
ɵregisterLocaleData(localeFr);
ɵregisterLocaleData(localeAr);
ɵregisterLocaleData(localeDeAt);
});

afterAll(() => ɵunregisterLocaleData());

describe('DecimalPipe', () => {
describe('transform', () => {
let pipe: DecimalPipe;
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/core_private_export.ts
Expand Up @@ -33,6 +33,6 @@ export {makeDecorator as ɵmakeDecorator} from './util/decorators';
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 {NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR as ɵNOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR} from './view/provider';
export {getLocalePluralCase as ɵgetLocalePluralCase, findLocaleData as ɵfindLocaleData} from './i18n/locale_data_api';
export {LOCALE_DATA as ɵLOCALE_DATA, LocaleDataIndex as ɵLocaleDataIndex} from './i18n/locale_data';
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} 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';
14 changes: 14 additions & 0 deletions packages/core/src/i18n/locale_data.ts
Expand Up @@ -36,3 +36,17 @@ export enum LocaleDataIndex {
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}

0 comments on commit 7e8eec5

Please sign in to comment.