From 5296c04f61bd370e7031ee7072ced586faadcfba Mon Sep 17 00:00:00 2001 From: Olivier Combe Date: Mon, 15 Jul 2019 15:28:07 +0200 Subject: [PATCH] fix(ivy): set LOCALE_ID when using the injector (#31566) In `BrowserModule` the value of `LOCALE_ID` is defined in the `APPLICATION_MODULE_PROVIDERS` after `APP_INITIALIZER` has run. This PR ensures that `LOCALE_ID` is also set for ivy at the same moment which allows the application to fetch the locale from a backend (for example). Fixes #31465 FW-1436 #resolve PR Close #31566 --- packages/common/test/i18n/format_date_spec.ts | 25 +++--- .../common/test/i18n/format_number_spec.ts | 90 +++++++++---------- packages/core/src/application_module.ts | 11 ++- packages/core/src/application_ref.ts | 11 ++- packages/core/src/core_private_export.ts | 2 +- .../core/src/core_render3_private_export.ts | 1 - packages/core/src/i18n/localization.ts | 5 ++ packages/core/src/render3/i18n.ts | 10 +-- packages/core/src/render3/index.ts | 1 - packages/core/test/acceptance/di_spec.ts | 4 +- packages/core/test/application_ref_spec.ts | 15 ++++ 11 files changed, 103 insertions(+), 72 deletions(-) diff --git a/packages/common/test/i18n/format_date_spec.ts b/packages/common/test/i18n/format_date_spec.ts index 02219f5751..085f81375a 100644 --- a/packages/common/test/i18n/format_date_spec.ts +++ b/packages/common/test/i18n/format_date_spec.ts @@ -15,6 +15,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 as DEFAULT_LOCALE_ID} from '@angular/core'; describe('Format date', () => { describe('toDate', () => { @@ -46,13 +47,12 @@ describe('Format date', () => { describe('formatDate', () => { const isoStringWithoutTime = '2015-01-01'; - const defaultLocale = 'en-US'; const defaultFormat = 'mediumDate'; let date: Date; // Check the transformation of a date into a pattern function expectDateFormatAs(date: Date | string, pattern: any, output: string): void { - expect(formatDate(date, pattern, defaultLocale)).toEqual(output, `pattern: "${pattern}"`); + expect(formatDate(date, pattern, DEFAULT_LOCALE_ID)).toEqual(output, `pattern: "${pattern}"`); } beforeAll(() => { @@ -209,7 +209,8 @@ describe('Format date', () => { }; Object.keys(dateFixtures).forEach((pattern: string) => { - expect(formatDate(date, pattern, defaultLocale, '+0430')).toMatch(dateFixtures[pattern]); + expect(formatDate(date, pattern, DEFAULT_LOCALE_ID, '+0430')) + .toMatch(dateFixtures[pattern]); }); }); @@ -253,22 +254,22 @@ describe('Format date', () => { }; Object.keys(dateFixtures).forEach((pattern: string) => { - expect(formatDate(date, pattern, defaultLocale)).toMatch(dateFixtures[pattern]); + expect(formatDate(date, pattern, DEFAULT_LOCALE_ID)).toMatch(dateFixtures[pattern]); }); }); it('should format invalid in IE ISO date', - () => expect(formatDate('2017-01-11T12:00:00.014-0500', defaultFormat, defaultLocale)) + () => expect(formatDate('2017-01-11T12:00:00.014-0500', defaultFormat, DEFAULT_LOCALE_ID)) .toEqual('Jan 11, 2017')); it('should format invalid in Safari ISO date', - () => expect(formatDate('2017-01-20T12:00:00+0000', defaultFormat, defaultLocale)) + () => expect(formatDate('2017-01-20T12:00:00+0000', defaultFormat, DEFAULT_LOCALE_ID)) .toEqual('Jan 20, 2017')); // https://github.com/angular/angular/issues/9524 // https://github.com/angular/angular/issues/9524 it('should format correctly with iso strings that contain time', - () => expect(formatDate('2017-05-07T22:14:39', 'dd-MM-yyyy HH:mm', defaultLocale)) + () => expect(formatDate('2017-05-07T22:14:39', 'dd-MM-yyyy HH:mm', DEFAULT_LOCALE_ID)) .toMatch(/07-05-2017 \d{2}:\d{2}/)); // https://github.com/angular/angular/issues/21491 @@ -276,22 +277,22 @@ describe('Format date', () => { // this test only works if the timezone is not in UTC // which is the case for BrowserStack when we test Safari if (new Date().getTimezoneOffset() !== 0) { - expect(formatDate('2018-01-11T13:00:00', 'HH', defaultLocale)) - .not.toEqual(formatDate('2018-01-11T13:00:00Z', 'HH', defaultLocale)); + expect(formatDate('2018-01-11T13:00:00', 'HH', DEFAULT_LOCALE_ID)) + .not.toEqual(formatDate('2018-01-11T13:00:00Z', 'HH', DEFAULT_LOCALE_ID)); } }); // https://github.com/angular/angular/issues/16624 // https://github.com/angular/angular/issues/17478 it('should show the correct time when the timezone is fixed', () => { - expect(formatDate('2017-06-13T10:14:39+0000', 'shortTime', defaultLocale, '+0000')) + expect(formatDate('2017-06-13T10:14:39+0000', 'shortTime', DEFAULT_LOCALE_ID, '+0000')) .toEqual('10:14 AM'); - expect(formatDate('2017-06-13T10:14:39+0000', 'h:mm a', defaultLocale, '+0000')) + expect(formatDate('2017-06-13T10:14:39+0000', 'h:mm a', DEFAULT_LOCALE_ID, '+0000')) .toEqual('10:14 AM'); }); it('should remove bidi control characters', - () => expect(formatDate(date, 'MM/dd/yyyy', defaultLocale) !.length).toEqual(10)); + () => expect(formatDate(date, 'MM/dd/yyyy', DEFAULT_LOCALE_ID) !.length).toEqual(10)); it(`should format the date correctly in various locales`, () => { expect(formatDate(date, 'short', 'de')).toEqual('15.06.15, 09:03'); diff --git a/packages/common/test/i18n/format_number_spec.ts b/packages/common/test/i18n/format_number_spec.ts index 970b68f5ef..9a8bd07b99 100644 --- a/packages/common/test/i18n/format_number_spec.ts +++ b/packages/common/test/i18n/format_number_spec.ts @@ -12,10 +12,9 @@ import localeFr from '@angular/common/locales/fr'; import localeAr from '@angular/common/locales/ar'; import {formatCurrency, formatNumber, formatPercent, registerLocaleData} from '@angular/common'; import {describe, expect, it} from '@angular/core/testing/src/testing_internal'; +import {ɵDEFAULT_LOCALE_ID as DEFAULT_LOCALE_ID} from '@angular/core'; describe('Format number', () => { - const defaultLocale = 'en-US'; - beforeAll(() => { registerLocaleData(localeEn); registerLocaleData(localeEsUS); @@ -26,18 +25,18 @@ describe('Format number', () => { describe('Number', () => { describe('transform', () => { it('should return correct value for numbers', () => { - expect(formatNumber(12345, defaultLocale)).toEqual('12,345'); - expect(formatNumber(123, defaultLocale, '.2')).toEqual('123.00'); - expect(formatNumber(1, defaultLocale, '3.')).toEqual('001'); - expect(formatNumber(1.1, defaultLocale, '3.4-5')).toEqual('001.1000'); - expect(formatNumber(1.123456, defaultLocale, '3.4-5')).toEqual('001.12346'); - expect(formatNumber(1.1234, defaultLocale)).toEqual('1.123'); - expect(formatNumber(1.123456, defaultLocale, '.2')).toEqual('1.123'); - expect(formatNumber(1.123456, defaultLocale, '.4')).toEqual('1.1235'); + expect(formatNumber(12345, DEFAULT_LOCALE_ID)).toEqual('12,345'); + expect(formatNumber(123, DEFAULT_LOCALE_ID, '.2')).toEqual('123.00'); + expect(formatNumber(1, DEFAULT_LOCALE_ID, '3.')).toEqual('001'); + expect(formatNumber(1.1, DEFAULT_LOCALE_ID, '3.4-5')).toEqual('001.1000'); + expect(formatNumber(1.123456, DEFAULT_LOCALE_ID, '3.4-5')).toEqual('001.12346'); + expect(formatNumber(1.1234, DEFAULT_LOCALE_ID)).toEqual('1.123'); + expect(formatNumber(1.123456, DEFAULT_LOCALE_ID, '.2')).toEqual('1.123'); + expect(formatNumber(1.123456, DEFAULT_LOCALE_ID, '.4')).toEqual('1.1235'); }); it('should throw if minFractionDigits is explicitly higher than maxFractionDigits', () => { - expect(() => formatNumber(1.1, defaultLocale, '3.4-2')) + expect(() => formatNumber(1.1, DEFAULT_LOCALE_ID, '3.4-2')) .toThrowError(/is higher than the maximum/); }); }); @@ -51,27 +50,27 @@ describe('Format number', () => { describe('Percent', () => { describe('transform', () => { it('should return correct value for numbers', () => { - expect(formatPercent(1.23, defaultLocale)).toEqual('123%'); - expect(formatPercent(1.2, defaultLocale, '.2')).toEqual('120.00%'); - expect(formatPercent(1.2, defaultLocale, '4.2')).toEqual('0,120.00%'); + expect(formatPercent(1.23, DEFAULT_LOCALE_ID)).toEqual('123%'); + expect(formatPercent(1.2, DEFAULT_LOCALE_ID, '.2')).toEqual('120.00%'); + expect(formatPercent(1.2, DEFAULT_LOCALE_ID, '4.2')).toEqual('0,120.00%'); expect(formatPercent(1.2, 'fr', '4.2')).toEqual('0 120,00 %'); expect(formatPercent(1.2, 'ar', '4.2')).toEqual('0,120.00‎%‎'); // see issue #20136 - expect(formatPercent(0.12345674, defaultLocale, '0.0-10')).toEqual('12.345674%'); - expect(formatPercent(0, defaultLocale, '0.0-10')).toEqual('0%'); - expect(formatPercent(0.00, defaultLocale, '0.0-10')).toEqual('0%'); - expect(formatPercent(1, defaultLocale, '0.0-10')).toEqual('100%'); - expect(formatPercent(0.1, defaultLocale, '0.0-10')).toEqual('10%'); - expect(formatPercent(0.12, defaultLocale, '0.0-10')).toEqual('12%'); - expect(formatPercent(0.123, defaultLocale, '0.0-10')).toEqual('12.3%'); - expect(formatPercent(12.3456, defaultLocale, '0.0-10')).toEqual('1,234.56%'); - expect(formatPercent(12.345600, defaultLocale, '0.0-10')).toEqual('1,234.56%'); - expect(formatPercent(12.345699999, defaultLocale, '0.0-6')).toEqual('1,234.57%'); - expect(formatPercent(12.345699999, defaultLocale, '0.4-6')).toEqual('1,234.5700%'); - expect(formatPercent(100, defaultLocale, '0.4-6')).toEqual('10,000.0000%'); - expect(formatPercent(100, defaultLocale, '0.0-10')).toEqual('10,000%'); - expect(formatPercent(1.5e2, defaultLocale)).toEqual('15,000%'); - expect(formatPercent(1e100, defaultLocale)).toEqual('1E+102%'); + expect(formatPercent(0.12345674, DEFAULT_LOCALE_ID, '0.0-10')).toEqual('12.345674%'); + expect(formatPercent(0, DEFAULT_LOCALE_ID, '0.0-10')).toEqual('0%'); + expect(formatPercent(0.00, DEFAULT_LOCALE_ID, '0.0-10')).toEqual('0%'); + expect(formatPercent(1, DEFAULT_LOCALE_ID, '0.0-10')).toEqual('100%'); + expect(formatPercent(0.1, DEFAULT_LOCALE_ID, '0.0-10')).toEqual('10%'); + expect(formatPercent(0.12, DEFAULT_LOCALE_ID, '0.0-10')).toEqual('12%'); + expect(formatPercent(0.123, DEFAULT_LOCALE_ID, '0.0-10')).toEqual('12.3%'); + expect(formatPercent(12.3456, DEFAULT_LOCALE_ID, '0.0-10')).toEqual('1,234.56%'); + expect(formatPercent(12.345600, DEFAULT_LOCALE_ID, '0.0-10')).toEqual('1,234.56%'); + expect(formatPercent(12.345699999, DEFAULT_LOCALE_ID, '0.0-6')).toEqual('1,234.57%'); + expect(formatPercent(12.345699999, DEFAULT_LOCALE_ID, '0.4-6')).toEqual('1,234.5700%'); + expect(formatPercent(100, DEFAULT_LOCALE_ID, '0.4-6')).toEqual('10,000.0000%'); + expect(formatPercent(100, DEFAULT_LOCALE_ID, '0.0-10')).toEqual('10,000%'); + expect(formatPercent(1.5e2, DEFAULT_LOCALE_ID)).toEqual('15,000%'); + expect(formatPercent(1e100, DEFAULT_LOCALE_ID)).toEqual('1E+102%'); }); }); }); @@ -80,16 +79,16 @@ describe('Format number', () => { const defaultCurrencyCode = 'USD'; describe('transform', () => { it('should return correct value for numbers', () => { - expect(formatCurrency(123, defaultLocale, '$')).toEqual('$123.00'); - expect(formatCurrency(12, defaultLocale, 'EUR', 'EUR', '.1')).toEqual('EUR12.0'); - expect( - formatCurrency(5.1234, defaultLocale, defaultCurrencyCode, defaultCurrencyCode, '.0-3')) + expect(formatCurrency(123, DEFAULT_LOCALE_ID, '$')).toEqual('$123.00'); + expect(formatCurrency(12, DEFAULT_LOCALE_ID, 'EUR', 'EUR', '.1')).toEqual('EUR12.0'); + expect(formatCurrency( + 5.1234, DEFAULT_LOCALE_ID, defaultCurrencyCode, defaultCurrencyCode, '.0-3')) .toEqual('USD5.123'); - expect(formatCurrency(5.1234, defaultLocale, defaultCurrencyCode)).toEqual('USD5.12'); - expect(formatCurrency(5.1234, defaultLocale, '$')).toEqual('$5.12'); - expect(formatCurrency(5.1234, defaultLocale, 'CA$')).toEqual('CA$5.12'); - expect(formatCurrency(5.1234, defaultLocale, '$')).toEqual('$5.12'); - expect(formatCurrency(5.1234, defaultLocale, '$', defaultCurrencyCode, '5.2-2')) + expect(formatCurrency(5.1234, DEFAULT_LOCALE_ID, defaultCurrencyCode)).toEqual('USD5.12'); + expect(formatCurrency(5.1234, DEFAULT_LOCALE_ID, '$')).toEqual('$5.12'); + expect(formatCurrency(5.1234, DEFAULT_LOCALE_ID, 'CA$')).toEqual('CA$5.12'); + expect(formatCurrency(5.1234, DEFAULT_LOCALE_ID, '$')).toEqual('$5.12'); + expect(formatCurrency(5.1234, DEFAULT_LOCALE_ID, '$', defaultCurrencyCode, '5.2-2')) .toEqual('$00,005.12'); expect(formatCurrency(5.1234, 'fr', '$', defaultCurrencyCode, '5.2-2')) .toEqual('00 005,12 $'); @@ -98,20 +97,21 @@ describe('Format number', () => { it('should support any currency code name', () => { // currency code is unknown, default formatting options will be used - expect(formatCurrency(5.1234, defaultLocale, 'unexisting_ISO_code')) + expect(formatCurrency(5.1234, DEFAULT_LOCALE_ID, 'unexisting_ISO_code')) .toEqual('unexisting_ISO_code5.12'); // currency code is USD, the pipe will format based on USD but will display "Custom name" - expect(formatCurrency(5.1234, defaultLocale, 'Custom name')).toEqual('Custom name5.12'); + expect(formatCurrency(5.1234, DEFAULT_LOCALE_ID, 'Custom name')).toEqual('Custom name5.12'); }); it('should round to the default number of digits if no digitsInfo', () => { // IDR has a default number of digits of 0 - expect(formatCurrency(5.1234, defaultLocale, 'IDR', 'IDR')).toEqual('IDR5'); - expect(formatCurrency(5.1234, defaultLocale, 'IDR', 'IDR', '.2')).toEqual('IDR5.12'); - expect(formatCurrency(5.1234, defaultLocale, 'Custom name', 'IDR')).toEqual('Custom name5'); + expect(formatCurrency(5.1234, DEFAULT_LOCALE_ID, 'IDR', 'IDR')).toEqual('IDR5'); + expect(formatCurrency(5.1234, DEFAULT_LOCALE_ID, 'IDR', 'IDR', '.2')).toEqual('IDR5.12'); + expect(formatCurrency(5.1234, DEFAULT_LOCALE_ID, 'Custom name', 'IDR')) + .toEqual('Custom name5'); // BHD has a default number of digits of 3 - expect(formatCurrency(5.1234, defaultLocale, 'BHD', 'BHD')).toEqual('BHD5.123'); - expect(formatCurrency(5.1234, defaultLocale, 'BHD', 'BHD', '.1-2')).toEqual('BHD5.12'); + expect(formatCurrency(5.1234, DEFAULT_LOCALE_ID, 'BHD', 'BHD')).toEqual('BHD5.123'); + expect(formatCurrency(5.1234, DEFAULT_LOCALE_ID, 'BHD', 'BHD', '.1-2')).toEqual('BHD5.12'); }); }); }); diff --git a/packages/core/src/application_module.ts b/packages/core/src/application_module.ts index aee1f02a3e..fa8e44affb 100644 --- a/packages/core/src/application_module.ts +++ b/packages/core/src/application_module.ts @@ -14,11 +14,14 @@ import {Console} from './console'; import {Injector, StaticProvider} from './di'; import {Inject, Optional, SkipSelf} from './di/metadata'; import {ErrorHandler} from './error_handler'; +import {DEFAULT_LOCALE_ID} from './i18n/localization'; import {LOCALE_ID} from './i18n/tokens'; +import {ivyEnabled} from './ivy_switch'; import {ComponentFactoryResolver} from './linker'; import {Compiler} from './linker/compiler'; import {NgModule} from './metadata'; import {SCHEDULER} from './render3/component_ref'; +import {setLocaleId} from './render3/i18n'; import {NgZone} from './zone'; export function _iterableDiffersFactory() { @@ -31,15 +34,21 @@ export function _keyValueDiffersFactory() { export function _localeFactory(locale?: string): string { if (locale) { + if (ivyEnabled) { + setLocaleId(locale); + } return locale; } // Use `goog.LOCALE` as default value for `LOCALE_ID` token for Closure Compiler. // Note: default `goog.LOCALE` value is `en`, when Angular used `en-US`. In order to preserve // backwards compatibility, we use Angular default value over Closure Compiler's one. if (ngI18nClosureMode && typeof goog !== 'undefined' && goog.LOCALE !== 'en') { + if (ivyEnabled) { + setLocaleId(goog.LOCALE); + } return goog.LOCALE; } - return 'en-US'; + return DEFAULT_LOCALE_ID; } /** diff --git a/packages/core/src/application_ref.ts b/packages/core/src/application_ref.ts index e8c6f81545..e87f10051c 100644 --- a/packages/core/src/application_ref.ts +++ b/packages/core/src/application_ref.ts @@ -8,15 +8,16 @@ import {Observable, Observer, Subscription, merge} from 'rxjs'; import {share} from 'rxjs/operators'; - import {ApplicationInitStatus} from './application_init'; import {APP_BOOTSTRAP_LISTENER, PLATFORM_INITIALIZER} from './application_tokens'; import {getCompilerFacade} from './compiler/compiler_facade'; import {Console} from './console'; import {Injectable, InjectionToken, Injector, StaticProvider} from './di'; import {ErrorHandler} from './error_handler'; +import {DEFAULT_LOCALE_ID} from './i18n/localization'; import {LOCALE_ID} from './i18n/tokens'; import {Type} from './interface/type'; +import {ivyEnabled} from './ivy_switch'; import {COMPILER_OPTIONS, CompilerFactory, CompilerOptions} from './linker/compiler'; import {ComponentFactory, ComponentRef} from './linker/component_factory'; import {ComponentFactoryBoundToModule, ComponentFactoryResolver} from './linker/component_factory_resolver'; @@ -26,7 +27,7 @@ import {isComponentResourceResolutionQueueEmpty, resolveComponentResources} from import {WtfScopeFn, wtfCreateScope, wtfLeave} from './profile/profile'; import {assertNgModuleType} from './render3/assert'; import {ComponentFactory as R3ComponentFactory} from './render3/component_ref'; -import {DEFAULT_LOCALE_ID, setLocaleId} from './render3/i18n'; +import {setLocaleId} from './render3/i18n'; import {NgModuleFactory as R3NgModuleFactory} from './render3/ng_module_ref'; import {Testability, TestabilityRegistry} from './testability/testability'; import {isDevMode} from './util/is_dev_mode'; @@ -264,8 +265,10 @@ export class PlatformRef { throw new Error('No ErrorHandler. Is platform module (BrowserModule) included?'); } // If the `LOCALE_ID` provider is defined at bootstrap we set the value for runtime i18n (ivy) - const localeId = moduleRef.injector.get(LOCALE_ID, DEFAULT_LOCALE_ID); - setLocaleId(localeId); + if (ivyEnabled) { + const localeId = moduleRef.injector.get(LOCALE_ID, DEFAULT_LOCALE_ID); + setLocaleId(localeId || DEFAULT_LOCALE_ID); + } moduleRef.onDestroy(() => remove(this._modules, moduleRef)); ngZone !.runOutsideAngular( () => ngZone !.onError.subscribe( diff --git a/packages/core/src/core_private_export.ts b/packages/core/src/core_private_export.ts index 510f4326f1..fa70f17633 100644 --- a/packages/core/src/core_private_export.ts +++ b/packages/core/src/core_private_export.ts @@ -16,6 +16,7 @@ export {Console as ɵConsole} from './console'; export {inject, setCurrentInjector as ɵsetCurrentInjector, ɵɵinject} from './di/injector_compatibility'; export {getInjectableDef as ɵgetInjectableDef, ɵɵInjectableDef, ɵɵInjectorDef} from './di/interface/defs'; export {APP_ROOT as ɵAPP_ROOT} from './di/scope'; +export {DEFAULT_LOCALE_ID as ɵDEFAULT_LOCALE_ID} from './i18n/localization'; export {ivyEnabled as ɵivyEnabled} from './ivy_switch'; export {ComponentFactory as ɵComponentFactory} from './linker/component_factory'; export {CodegenComponentFactoryResolver as ɵCodegenComponentFactoryResolver} from './linker/component_factory_resolver'; @@ -27,7 +28,6 @@ export {_sanitizeHtml as ɵ_sanitizeHtml} from './sanitization/html_sanitizer'; export {_sanitizeStyle as ɵ_sanitizeStyle} from './sanitization/style_sanitizer'; export {_sanitizeUrl as ɵ_sanitizeUrl} from './sanitization/url_sanitizer'; export {global as ɵglobal} from './util/global'; - export {looseIdentical as ɵlooseIdentical,} from './util/comparison'; export {stringify as ɵstringify} from './util/stringify'; export {makeDecorator as ɵmakeDecorator} from './util/decorators'; diff --git a/packages/core/src/core_render3_private_export.ts b/packages/core/src/core_render3_private_export.ts index e2401de524..c44e8f62f6 100644 --- a/packages/core/src/core_render3_private_export.ts +++ b/packages/core/src/core_render3_private_export.ts @@ -173,7 +173,6 @@ export { i18nConfigureLocalize as ɵi18nConfigureLocalize, ɵɵi18nLocalize, setLocaleId as ɵsetLocaleId, - DEFAULT_LOCALE_ID as ɵDEFAULT_LOCALE_ID, setClassMetadata as ɵsetClassMetadata, ɵɵresolveWindow, ɵɵresolveDocument, diff --git a/packages/core/src/i18n/localization.ts b/packages/core/src/i18n/localization.ts index 0cb492f18b..27cd62fecf 100644 --- a/packages/core/src/i18n/localization.ts +++ b/packages/core/src/i18n/localization.ts @@ -29,3 +29,8 @@ export function getPluralCase(value: any, locale: string): string { return 'other'; } } + +/** + * The locale id that the application is using by default (for translations and ICU expressions). + */ +export const DEFAULT_LOCALE_ID = 'en-US'; diff --git a/packages/core/src/render3/i18n.ts b/packages/core/src/render3/i18n.ts index ebe95e525f..2274876f9d 100644 --- a/packages/core/src/render3/i18n.ts +++ b/packages/core/src/render3/i18n.ts @@ -7,14 +7,12 @@ */ import '../util/ng_i18n_closure_mode'; - -import {getPluralCase} from '../i18n/localization'; +import {DEFAULT_LOCALE_ID, getPluralCase} from '../i18n/localization'; import {SRCSET_ATTRS, URI_ATTRS, VALID_ATTRS, VALID_ELEMENTS, getTemplateContent} from '../sanitization/html_sanitizer'; import {InertBodyHelper} from '../sanitization/inert_body'; import {_sanitizeUrl, sanitizeSrcset} from '../sanitization/url_sanitizer'; import {addAllToArray} from '../util/array_utils'; import {assertDataInRange, assertDefined, assertEqual, assertGreaterThan} from '../util/assert'; - import {attachPatchData} from './context_discovery'; import {bind, setDelayProjection, ɵɵload} from './instructions/all'; import {attachI18nOpCodesDebug} from './instructions/lview_debug'; @@ -1358,7 +1356,6 @@ export function ɵɵi18nLocalize(input: string, placeholders?: {[key: string]: s * This is the ivy version of `LOCALE_ID` that was defined as an injection token for the view engine * but is now defined as a global value. */ -export const DEFAULT_LOCALE_ID = 'en-US'; let LOCALE_ID = DEFAULT_LOCALE_ID; /** @@ -1369,7 +1366,10 @@ let LOCALE_ID = DEFAULT_LOCALE_ID; * @param localeId */ export function setLocaleId(localeId: string) { - LOCALE_ID = localeId.toLowerCase().replace(/_/g, '-'); + assertDefined(localeId, `Expected localeId to be defined`); + if (typeof localeId === 'string') { + LOCALE_ID = localeId.toLowerCase().replace(/_/g, '-'); + } } /** diff --git a/packages/core/src/render3/index.ts b/packages/core/src/render3/index.ts index 7edb7c7ed3..bda9602381 100644 --- a/packages/core/src/render3/index.ts +++ b/packages/core/src/render3/index.ts @@ -142,7 +142,6 @@ export { } from './state'; export { - DEFAULT_LOCALE_ID, ɵɵi18n, ɵɵi18nAttributes, ɵɵi18nExp, diff --git a/packages/core/test/acceptance/di_spec.ts b/packages/core/test/acceptance/di_spec.ts index 351253ed86..d32dcbc68f 100644 --- a/packages/core/test/acceptance/di_spec.ts +++ b/packages/core/test/acceptance/di_spec.ts @@ -7,7 +7,7 @@ */ import {CommonModule} from '@angular/common'; -import {Attribute, ChangeDetectorRef, Component, Directive, ElementRef, EventEmitter, Host, HostBinding, INJECTOR, Inject, Injectable, InjectionToken, Injector, Input, LOCALE_ID, ModuleWithProviders, NgModule, Optional, Output, Pipe, PipeTransform, Self, SkipSelf, TemplateRef, ViewChild, ViewContainerRef, forwardRef} from '@angular/core'; +import {Attribute, ChangeDetectorRef, Component, Directive, ElementRef, EventEmitter, Host, HostBinding, INJECTOR, Inject, Injectable, InjectionToken, Injector, Input, LOCALE_ID, ModuleWithProviders, NgModule, Optional, Output, Pipe, PipeTransform, Self, SkipSelf, TemplateRef, ViewChild, ViewContainerRef, forwardRef, ɵDEFAULT_LOCALE_ID as DEFAULT_LOCALE_ID} from '@angular/core'; import {ViewRef} from '@angular/core/src/render3/view_ref'; import {TestBed} from '@angular/core/testing'; import {ivyEnabled, onlyInIvy} from '@angular/private/testing'; @@ -1508,7 +1508,7 @@ describe('di', () => { const fixture = TestBed.createComponent(MyComp); fixture.detectChanges(); // takes `LOCALE_ID` from module injector, since we skip Component level with @SkipSelf - expect(fixture.componentInstance.localeId).toBe('en-US'); + expect(fixture.componentInstance.localeId).toBe(DEFAULT_LOCALE_ID); }); it('should work when injecting dependency in Directives', () => { diff --git a/packages/core/test/application_ref_spec.ts b/packages/core/test/application_ref_spec.ts index 3974575acd..ba9589ccb5 100644 --- a/packages/core/test/application_ref_spec.ts +++ b/packages/core/test/application_ref_spec.ts @@ -343,6 +343,21 @@ class SomeComponent { expect(getLocaleId()).toEqual('ro'); }); + + it('should wait for APP_INITIALIZER to set providers for `LOCALE_ID`', async() => { + let locale: string = ''; + + const promise = Promise.resolve().then(() => { locale = 'fr-FR'; }); + + const testModule = createModule({ + providers: [ + {provide: APP_INITIALIZER, useValue: () => promise, multi: true}, + {provide: LOCALE_ID, useFactory: () => locale} + ] + }); + const app = await defaultPlatform.bootstrapModule(testModule); + expect(app.injector.get(LOCALE_ID)).toEqual('fr-FR'); + }); }); describe('bootstrapModuleFactory', () => {