Browse Source

Merge pull request #6052 from abpframework/refactor/locale

Moved the registerLocale to core/locale secondary endpoint
pull/6099/head
Bunyamin Coskuner 6 years ago
committed by GitHub
parent
commit
e2e46967e3
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 102
      docs/en/UI/Angular/Localization.md
  2. 2
      npm/ng-packs/apps/dev-app/src/app/app.module.ts
  3. 7
      npm/ng-packs/packages/core/locale/ng-package.json
  4. 1
      npm/ng-packs/packages/core/locale/src/public-api.ts
  5. 66
      npm/ng-packs/packages/core/locale/src/utils/register-locale.ts
  6. 2
      npm/ng-packs/packages/core/src/lib/constants/different-locales.ts
  7. 12
      npm/ng-packs/packages/core/src/lib/constants/included-locales.ts
  8. 1
      npm/ng-packs/packages/core/src/lib/constants/index.ts
  9. 4
      npm/ng-packs/packages/core/src/lib/models/common.ts
  10. 4
      npm/ng-packs/packages/core/src/lib/providers/locale.provider.ts
  11. 11
      npm/ng-packs/packages/core/src/lib/services/localization.service.ts
  12. 5
      npm/ng-packs/packages/core/src/lib/services/replaceable-components.service.ts
  13. 8
      npm/ng-packs/packages/core/src/lib/states/config.state.ts
  14. 88
      npm/ng-packs/packages/core/src/lib/tests/config-state.service.spec.ts
  15. 1
      npm/ng-packs/packages/core/src/lib/tests/config.plugin.spec.ts
  16. 5
      npm/ng-packs/packages/core/src/lib/tests/config.state.spec.ts
  17. 10
      npm/ng-packs/packages/core/src/lib/tests/initial-utils.spec.ts
  18. 10
      npm/ng-packs/packages/core/src/lib/tests/locale.provider.spec.ts
  19. 5
      npm/ng-packs/packages/core/src/lib/tests/localization.service.spec.ts
  20. 1
      npm/ng-packs/packages/core/src/lib/tokens/index.ts
  21. 14
      npm/ng-packs/packages/core/src/lib/tokens/locale-error-handler.token.ts
  22. 4
      npm/ng-packs/packages/core/src/lib/tokens/options.token.ts
  23. 65
      npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts
  24. 1
      npm/ng-packs/tsconfig.json
  25. 1585
      npm/ng-packs/yarn.lock
  26. 2
      templates/app/angular/src/app/app.module.ts
  27. 2
      templates/module/angular/projects/dev-app/src/app/app.module.ts

102
docs/en/UI/Angular/Localization.md

@ -193,44 +193,122 @@ import { Component } from '@angular/core';
export class AppComponent {} export class AppComponent {}
``` ```
## Mapping of Culture Name to Angular Locale File Name ## Registering a New Locale
Since ABP has more than one language, Angular locale files loads lazily using [Webpack's import function](https://webpack.js.org/api/module-methods/#import-1) to avoid increasing the bundle size and register to Angular core using the [`registerLocaleData`](https://angular.io/api/common/registerLocaleData) function. The chunks to be included in the bundle are specified by the [Webpack's magic comments](https://webpack.js.org/api/module-methods/#magic-comments) as hard-coded. Therefore a `registerLocale` function that returns Webpack `import` function must be passed to `CoreModule`.
### registerLocaleFn
A `registerLocaleFn` is a higher order function that accepts `cultureNameLocaleFileMap` object and `errorHandlerFn` function as params and returns Webpack import function. A `registerLocale` function must be passed to the `forRoot` of the `CoreModule` as shown below:
```js
// app.module.ts
import { registerLocale } from '@abp/ng.core/locale';
// if you have commercial license and the language management module, add the below import
// import { registerLocale } from '@volo/abp.ng.language-management/locale';
@NgModule({
imports: [
// ...
CoreModule.forRoot({
// ...other options,
registerLocaleFn: registerLocale(
// you can pass the cultureNameLocaleFileMap and errorHandlerFn as optionally
{
cultureNameLocaleFileMap: { 'pt-BR': 'pt' },
errorHandlerFn: ({ resolve, reject, locale, error }) => {
// the error can be handled here
},
},
)
}),
//...
]
```
### Mapping of Culture Name to Angular Locale File Name
Some of the culture names defined in .NET do not match Angular locales. In such cases, the Angular app throws an error like below at runtime: Some of the culture names defined in .NET do not match Angular locales. In such cases, the Angular app throws an error like below at runtime:
![locale-error](./images/locale-error.png) ![locale-error](./images/locale-error.png)
If you see an error like this, you should pass the `cultureNameLocaleFileMap` property like below to CoreModule's forRoot static method. If you see an error like this, you should pass the `cultureNameLocaleFileMap` property like below to the `registerLocale` function.
```js ```js
// app.module.ts // app.module.ts
import { registerLocale } from '@abp/ng.core/locale';
// if you have commercial license and the language management module, add the below import
// import { registerLocale } from '@volo/abp.ng.language-management/locale';
@NgModule({ @NgModule({
imports: [ imports: [
// other imports // ...
CoreModule.forRoot({ CoreModule.forRoot({
// other options // ...other options,
cultureNameLocaleFileMap: { registerLocaleFn: registerLocale(
"DotnetCultureName": "AngularLocaleFileName", {
"pt-BR": "pt" // example cultureNameLocaleFileMap: {
} "DotnetCultureName": "AngularLocaleFileName",
}) "pt-BR": "pt" // example
},
},
)
}),
//... //...
``` ```
See [all locale files in Angular](https://github.com/angular/angular/tree/master/packages/common/locales). See [all locale files in Angular](https://github.com/angular/angular/tree/master/packages/common/locales).
## Adding new culture ### Adding a New Culture
Add the below code to the `app.module.ts` by replacing `your-locale` placeholder with a correct locale name.
```js ```js
//app.module.ts //app.module.ts
import { storeLocaleData } from '@abp/ng.core'; import { storeLocaleData } from '@abp/ng.core/locale';
import( import(
/* webpackChunkName: "_locale-your-locale-js"*/ /* webpackChunkName: "_locale-your-locale-js"*/
/* webpackMode: "eager" */ /* webpackMode: "eager" */
'@angular/common/locales/your-locale.js' '@angular/common/locales/your-locale.js'
).then(m => storeLocaleData(m.default, 'your-locale')); ).then(m => storeLocaleData(m.default, 'your-locale'));
``` ```
...or a custom `registerLocale` function can be passed to the `CoreModule`:
```js
// register-locale.ts
import { differentLocales } from '@abp/ng.core';
export function registerLocale(locale: string) {
return import(
/* webpackChunkName: "_locale-[request]"*/
/* webpackInclude: /[/\\](en|fr).js/ */
/* webpackExclude: /[/\\]global|extra/ */
`@angular/common/locales/${differentLocales[locale] || locale}.js`
)
}
// app.module.ts
import { registerLocale } from './register-locale';
@NgModule({
imports: [
// ...
CoreModule.forRoot({
// ...other options,
registerLocaleFn: registerLocale
}),
//...
]
```
## See Also ## See Also
* [Localization in ASP.NET Core](../../Localization.md) * [Localization in ASP.NET Core](../../Localization.md)

2
npm/ng-packs/apps/dev-app/src/app/app.module.ts

@ -1,5 +1,6 @@
import { AccountConfigModule } from '@abp/ng.account/config'; import { AccountConfigModule } from '@abp/ng.account/config';
import { CoreModule } from '@abp/ng.core'; import { CoreModule } from '@abp/ng.core';
import { registerLocale } from '@abp/ng.core/locale';
import { IdentityConfigModule } from '@abp/ng.identity/config'; import { IdentityConfigModule } from '@abp/ng.identity/config';
import { SettingManagementConfigModule } from '@abp/ng.setting-management/config'; import { SettingManagementConfigModule } from '@abp/ng.setting-management/config';
import { TenantManagementConfigModule } from '@abp/ng.tenant-management/config'; import { TenantManagementConfigModule } from '@abp/ng.tenant-management/config';
@ -28,6 +29,7 @@ const INSPECTION_TOOLS = [
AppRoutingModule, AppRoutingModule,
CoreModule.forRoot({ CoreModule.forRoot({
environment, environment,
registerLocaleFn: registerLocale(),
sendNullsAsQueryParam: false, sendNullsAsQueryParam: false,
skipGetAppConfiguration: false, skipGetAppConfiguration: false,
}), }),

7
npm/ng-packs/packages/core/locale/ng-package.json

@ -0,0 +1,7 @@
{
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../dist/core/locale",
"lib": {
"entryFile": "src/public-api.ts"
}
}

1
npm/ng-packs/packages/core/locale/src/public-api.ts

@ -0,0 +1 @@
export * from './utils/register-locale';

66
npm/ng-packs/packages/core/locale/src/utils/register-locale.ts

@ -0,0 +1,66 @@
import { differentLocales } from '@abp/ng.core';
import { registerLocaleData } from '@angular/common';
import { isDevMode } from '@angular/core';
export interface LocaleErrorHandlerData {
resolve: any;
reject: any;
error: any;
locale: string;
}
let localeMap = {};
export interface RegisterLocaleData {
cultureNameLocaleFileMap?: Record<string, string>;
errorHandlerFn?: (data: LocaleErrorHandlerData) => any;
}
export function registerLocale(
{
cultureNameLocaleFileMap = {},
errorHandlerFn = defaultLocalErrorHandlerFn,
} = {} as RegisterLocaleData,
) {
return (locale: string): Promise<any> => {
localeMap = { ...differentLocales, ...cultureNameLocaleFileMap };
return new Promise((resolve, reject) => {
return import(
/* webpackChunkName: "_locale-[request]"*/
/* webpackInclude: /[/\\](ar|cs|en|fr|pt|tr|ru|hu|sl|zh-Hans|zh-Hant).js/ */
/* webpackExclude: /[/\\]global|extra/ */
`@angular/common/locales/${localeMap[locale] || locale}.js`
)
.then(resolve)
.catch(error => {
errorHandlerFn({
resolve,
reject,
error,
locale,
});
});
});
};
}
const extraLocales = {};
export function storeLocaleData(data: any, localeId: string) {
extraLocales[localeId] = data;
}
export async function defaultLocalErrorHandlerFn({ locale, resolve }: LocaleErrorHandlerData) {
if (extraLocales[locale]) {
resolve({ default: extraLocales[localeMap[locale] || locale] });
return;
}
if (isDevMode) {
console.error(
`Cannot find the ${locale} locale file. You can check how can add new culture at https://docs.abp.io/en/abp/latest/UI/Angular/Localization#adding-a-new-culture`,
);
}
resolve();
}

2
npm/ng-packs/packages/core/src/lib/constants/different-locales.ts

@ -1,7 +1,7 @@
// Different locales from .NET // Different locales from .NET
// Key is .NET locale, value is Angular locale // Key is .NET locale, value is Angular locale
export default { export const differentLocales = {
aa: 'en', aa: 'en',
'aa-DJ': 'en', 'aa-DJ': 'en',
'aa-ER': 'en', 'aa-ER': 'en',

12
npm/ng-packs/packages/core/src/lib/constants/included-locales.ts

@ -1,12 +0,0 @@
export const includedLocales = [
'tr',
'ar',
'cs',
'en',
'fr',
'pt',
'ru',
'sl',
'zh-Hans',
'zh-Hant',
];

1
npm/ng-packs/packages/core/src/lib/constants/index.ts

@ -1,2 +1 @@
export * from './different-locales'; export * from './different-locales';
export * from './included-locales';

4
npm/ng-packs/packages/core/src/lib/models/common.ts

@ -1,16 +1,16 @@
import { EventEmitter, Type } from '@angular/core'; import { EventEmitter, Type } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { NgxsStoragePluginOptions } from '@ngxs/storage-plugin';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { eLayoutType } from '../enums/common'; import { eLayoutType } from '../enums/common';
import { Config } from './config'; import { Config } from './config';
import { NgxsStoragePluginOptions } from '@ngxs/storage-plugin';
export namespace ABP { export namespace ABP {
export interface Root { export interface Root {
environment: Partial<Config.Environment>; environment: Partial<Config.Environment>;
registerLocaleFn: (locale: string) => Promise<any>;
skipGetAppConfiguration?: boolean; skipGetAppConfiguration?: boolean;
sendNullsAsQueryParam?: boolean; sendNullsAsQueryParam?: boolean;
cultureNameLocaleFileMap?: Dictionary<string>;
ngxsStoragePluginOptions?: NgxsStoragePluginOptions & { key?: string[] }; ngxsStoragePluginOptions?: NgxsStoragePluginOptions & { key?: string[] };
} }

4
npm/ng-packs/packages/core/src/lib/providers/locale.provider.ts

@ -1,5 +1,5 @@
import { LOCALE_ID, Provider } from '@angular/core'; import { LOCALE_ID, Provider } from '@angular/core';
import localesMapping from '../constants/different-locales'; import { differentLocales } from '../constants/different-locales';
import { LocalizationService } from '../services/localization.service'; import { LocalizationService } from '../services/localization.service';
export class LocaleId extends String { export class LocaleId extends String {
@ -9,7 +9,7 @@ export class LocaleId extends String {
toString(): string { toString(): string {
const { currentLang } = this.localizationService; const { currentLang } = this.localizationService;
return localesMapping[currentLang] || currentLang; return differentLocales[currentLang] || currentLang;
} }
valueOf(): string { valueOf(): string {

11
npm/ng-packs/packages/core/src/lib/services/localization.service.ts

@ -1,14 +1,15 @@
import { registerLocaleData } from '@angular/common';
import { Injectable, Injector, NgZone, Optional, SkipSelf } from '@angular/core'; import { Injectable, Injector, NgZone, Optional, SkipSelf } from '@angular/core';
import { ActivatedRouteSnapshot, Router } from '@angular/router'; import { ActivatedRouteSnapshot, Router } from '@angular/router';
import { Actions, ofActionSuccessful, Store } from '@ngxs/store'; import { Actions, ofActionSuccessful, Store } from '@ngxs/store';
import { noop, Observable } from 'rxjs'; import { noop, Observable } from 'rxjs';
import { filter, map } from 'rxjs/operators'; import { filter, map } from 'rxjs/operators';
import { SetLanguage } from '../actions/session.actions'; import { SetLanguage } from '../actions/session.actions';
import { ABP } from '../models/common';
import { Config } from '../models/config'; import { Config } from '../models/config';
import { ConfigState } from '../states/config.state'; import { ConfigState } from '../states/config.state';
import { registerLocale } from '../utils/initial-utils';
import { createLocalizer, createLocalizerWithFallback } from '../utils/localization-utils';
import { CORE_OPTIONS } from '../tokens/options.token'; import { CORE_OPTIONS } from '../tokens/options.token';
import { createLocalizer, createLocalizerWithFallback } from '../utils/localization-utils';
type ShouldReuseRoute = (future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot) => boolean; type ShouldReuseRoute = (future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot) => boolean;
@ -52,7 +53,11 @@ export class LocalizationService {
router.routeReuseStrategy.shouldReuseRoute = () => false; router.routeReuseStrategy.shouldReuseRoute = () => false;
router.navigated = false; router.navigated = false;
return registerLocale(locale, this.injector).then(() => { const { registerLocaleFn }: ABP.Root = this.injector.get(CORE_OPTIONS);
return registerLocaleFn(locale).then(module => {
if (module?.default) registerLocaleData(module.default);
this.ngZone.run(async () => { this.ngZone.run(async () => {
await router.navigateByUrl(router.url).catch(noop); await router.navigateByUrl(router.url).catch(noop);
router.routeReuseStrategy.shouldReuseRoute = shouldReuseRoute; router.routeReuseStrategy.shouldReuseRoute = shouldReuseRoute;

5
npm/ng-packs/packages/core/src/lib/services/replaceable-components.service.ts

@ -1,9 +1,8 @@
import { Injectable, NgZone } from '@angular/core'; import { Injectable, NgZone } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { ReplaceableComponents } from '../models/replaceable-components'; import { ReplaceableComponents } from '../models/replaceable-components';
import { BehaviorSubject, Observable } from 'rxjs';
import { noop } from '../utils/common-utils';
import { map, filter } from 'rxjs/operators';
import { InternalStore } from '../utils/internal-store-utils'; import { InternalStore } from '../utils/internal-store-utils';
import { reloadRoute } from '../utils/route-utils'; import { reloadRoute } from '../utils/route-utils';

8
npm/ng-packs/packages/core/src/lib/states/config.state.ts

@ -226,17 +226,15 @@ export class ConfigState {
}), }),
), ),
switchMap(configuration => { switchMap(configuration => {
let lang = configuration.localization.currentCulture.cultureName; if (this.store.selectSnapshot(SessionState.getLanguage)) return of(null);
let lang = configuration.localization.currentCulture.cultureName;
if (lang.includes(';')) { if (lang.includes(';')) {
lang = lang.split(';')[0]; lang = lang.split(';')[0];
} }
document.documentElement.setAttribute('lang', lang); document.documentElement.setAttribute('lang', lang);
return dispatch(new SetLanguage(lang, false));
return this.store.selectSnapshot(SessionState.getLanguage)
? of(null)
: dispatch(new SetLanguage(lang, false));
}), }),
catchError((err: HttpErrorResponse) => { catchError((err: HttpErrorResponse) => {
dispatch(new RestOccurError(err)); dispatch(new RestOccurError(err));

88
npm/ng-packs/packages/core/src/lib/tests/config-state.service.spec.ts

@ -6,94 +6,6 @@ import { Config } from '../models/config';
import { ConfigStateService } from '../services/config-state.service'; import { ConfigStateService } from '../services/config-state.service';
import { ConfigState } from '../states'; import { ConfigState } from '../states';
const CONFIG_STATE_DATA = {
environment: {
production: false,
application: {
name: 'MyProjectName',
},
oAuthConfig: {
issuer: 'https://localhost:44305',
},
apis: {
default: {
url: 'https://localhost:44305',
},
other: {
url: 'https://localhost:44306',
},
},
localization: {
defaultResourceName: 'MyProjectName',
},
},
requirements: {
layouts: [null, null, null],
},
localization: {
values: {
MyProjectName: {
"'{0}' and '{1}' do not match.": "'{0}' and '{1}' do not match.",
},
AbpIdentity: {
Identity: 'identity',
},
},
languages: [
{
cultureName: 'cs',
uiCultureName: 'cs',
displayName: 'Čeština',
flagIcon: null,
},
],
currentCulture: {
displayName: 'English',
englishName: 'English',
threeLetterIsoLanguageName: 'eng',
twoLetterIsoLanguageName: 'en',
isRightToLeft: false,
cultureName: 'en',
name: 'en',
nativeName: 'English',
dateTimeFormat: {
calendarAlgorithmType: 'SolarCalendar',
dateTimeFormatLong: 'dddd, MMMM d, yyyy',
shortDatePattern: 'M/d/yyyy',
fullDateTimePattern: 'dddd, MMMM d, yyyy h:mm:ss tt',
dateSeparator: '/',
shortTimePattern: 'h:mm tt',
longTimePattern: 'h:mm:ss tt',
},
},
defaultResourceName: null,
},
auth: {
policies: {
'AbpIdentity.Roles': true,
},
grantedPolicies: {
'Abp.Identity': false,
},
},
setting: {
values: {
'Abp.Localization.DefaultLanguage': 'en',
},
},
currentUser: {
isAuthenticated: false,
id: null,
tenantId: null,
userName: null,
email: null,
roles: [],
} as ApplicationConfiguration.CurrentUser,
features: {
values: {},
},
} as Config.State;
describe('ConfigStateService', () => { describe('ConfigStateService', () => {
let service: ConfigStateService; let service: ConfigStateService;
let spectator: SpectatorService<ConfigStateService>; let spectator: SpectatorService<ConfigStateService>;

1
npm/ng-packs/packages/core/src/lib/tests/config.plugin.spec.ts

@ -6,6 +6,7 @@ const options: ABP.Root = {
environment: { environment: {
production: false, production: false,
}, },
registerLocaleFn: () => Promise.resolve(),
}; };
const event = new InitState(); const event = new InitState();

5
npm/ng-packs/packages/core/src/lib/tests/config.state.spec.ts

@ -8,7 +8,7 @@ import { Config } from '../models/config';
import { ApplicationConfigurationService, ConfigStateService } from '../services'; import { ApplicationConfigurationService, ConfigStateService } from '../services';
import { ConfigState } from '../states'; import { ConfigState } from '../states';
export const CONFIG_STATE_DATA = { export const CONFIG_STATE_DATA = ({
environment: { environment: {
production: false, production: false,
application: { application: {
@ -98,7 +98,8 @@ export const CONFIG_STATE_DATA = {
'Chat.Enable': 'True', 'Chat.Enable': 'True',
}, },
}, },
} as Config.State; registerLocaleFn: () => Promise.resolve(),
} as any) as Config.State;
describe('ConfigState', () => { describe('ConfigState', () => {
let spectator: SpectatorService<ConfigStateService>; let spectator: SpectatorService<ConfigStateService>;

10
npm/ng-packs/packages/core/src/lib/tests/initial-utils.spec.ts

@ -22,7 +22,13 @@ describe('InitialUtils', () => {
component: DummyComponent, component: DummyComponent,
mocks: [Store, OAuthService], mocks: [Store, OAuthService],
providers: [ providers: [
{ provide: CORE_OPTIONS, useValue: { environment: { oAuthConfig: { issuer: 'test' } } } }, {
provide: CORE_OPTIONS,
useValue: {
environment: { oAuthConfig: { issuer: 'test' } },
registerLocaleFn: () => Promise.resolve(),
},
},
], ],
}); });
@ -76,7 +82,7 @@ describe('InitialUtils', () => {
const store = spectator.inject(Store); const store = spectator.inject(Store);
store.selectSnapshot.andCallFake(selector => selector({ SessionState: { language: 'tr' } })); store.selectSnapshot.andCallFake(selector => selector({ SessionState: { language: 'tr' } }));
injectorSpy.mockReturnValueOnce(store); injectorSpy.mockReturnValueOnce(store);
injectorSpy.mockReturnValueOnce({ cultureNameLocaleFileMap: {} }); injectorSpy.mockReturnValueOnce({ registerLocaleFn: () => Promise.resolve() });
expect(typeof localeInitializer(injector)).toBe('function'); expect(typeof localeInitializer(injector)).toBe('function');
expect(await localeInitializer(injector)()).toBe('resolved'); expect(await localeInitializer(injector)()).toBe('resolved');
}); });

10
npm/ng-packs/packages/core/src/lib/tests/locale.provider.spec.ts

@ -1,8 +1,8 @@
import { Component, LOCALE_ID } from '@angular/core'; import { Component, LOCALE_ID } from '@angular/core';
import { createRoutingFactory, SpectatorHost, SpectatorRouting } from '@ngneat/spectator/jest'; import { createRoutingFactory, SpectatorRouting } from '@ngneat/spectator/jest';
import { differentLocales } from '../constants/different-locales';
import { LocaleId } from '../providers';
import { LocalizationService } from '../services'; import { LocalizationService } from '../services';
import { LocaleProvider, LocaleId } from '../providers';
import localesMapping from '../constants/different-locales';
@Component({ selector: 'abp-dummy', template: '' }) @Component({ selector: 'abp-dummy', template: '' })
export class DummyComponent {} export class DummyComponent {}
@ -28,10 +28,10 @@ describe('LocaleProvider', () => {
spectator = createComponent(); spectator = createComponent();
const localizationService = spectator.inject(LocalizationService); const localizationService = spectator.inject(LocalizationService);
expect(spectator.inject(LOCALE_ID).valueOf()).toBe(localesMapping['en-US'] || 'en-US'); expect(spectator.inject(LOCALE_ID).valueOf()).toBe(differentLocales['en-US'] || 'en-US');
(localizationService as any).currentLang = 'tr'; (localizationService as any).currentLang = 'tr';
expect(spectator.inject(LOCALE_ID).valueOf()).toBe(localesMapping['tr'] || 'tr'); expect(spectator.inject(LOCALE_ID).valueOf()).toBe(differentLocales['tr'] || 'tr');
}); });
}); });
}); });

5
npm/ng-packs/packages/core/src/lib/tests/localization.service.spec.ts

@ -16,7 +16,10 @@ describe('LocalizationService', () => {
mocks: [Store, Router], mocks: [Store, Router],
providers: [ providers: [
{ provide: Actions, useValue: new Subject() }, { provide: Actions, useValue: new Subject() },
{ provide: CORE_OPTIONS, useValue: { cultureNameLocaleFileMap: {} } }, {
provide: CORE_OPTIONS,
useValue: { registerLocaleFn: () => Promise.resolve(), cultureNameLocaleFileMap: {} },
},
], ],
}); });

1
npm/ng-packs/packages/core/src/lib/tokens/index.ts

@ -1,3 +1,2 @@
export * from './list.token'; export * from './list.token';
export * from './locale-error-handler.token';
export * from './options.token'; export * from './options.token';

14
npm/ng-packs/packages/core/src/lib/tokens/locale-error-handler.token.ts

@ -1,14 +0,0 @@
import { InjectionToken, Injector } from '@angular/core';
export interface LocaleErrorHandlerData {
resolve: any;
reject: any;
error: any;
locale: string;
storedLocales: Record<string, any>;
injector: Injector;
}
export const LOCALE_ERROR_HANDLER = new InjectionToken<(data: LocaleErrorHandlerData) => any>(
'LOCALE_ERROR_HANDLER',
);

4
npm/ng-packs/packages/core/src/lib/tokens/options.token.ts

@ -1,12 +1,10 @@
import { InjectionToken } from '@angular/core'; import { InjectionToken } from '@angular/core';
import differentLocales from '../constants/different-locales';
import { ABP } from '../models/common'; import { ABP } from '../models/common';
export const CORE_OPTIONS = new InjectionToken<ABP.Root>('CORE_OPTIONS'); export const CORE_OPTIONS = new InjectionToken<ABP.Root>('CORE_OPTIONS');
export function coreOptionsFactory({ cultureNameLocaleFileMap = {}, ...options }: ABP.Root) { export function coreOptionsFactory({ ...options }: ABP.Root) {
return { return {
...options, ...options,
cultureNameLocaleFileMap: { ...differentLocales, ...cultureNameLocaleFileMap },
} as ABP.Root; } as ABP.Root;
} }

65
npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts

@ -1,5 +1,5 @@
import { registerLocaleData } from '@angular/common'; import { registerLocaleData } from '@angular/common';
import { Injector, isDevMode } from '@angular/core'; import { Injector } from '@angular/core';
import { Store } from '@ngxs/store'; import { Store } from '@ngxs/store';
import { OAuthService } from 'angular-oauth2-oidc'; import { OAuthService } from 'angular-oauth2-oidc';
import { tap } from 'rxjs/operators'; import { tap } from 'rxjs/operators';
@ -8,7 +8,6 @@ import { ABP } from '../models/common';
import { AuthService } from '../services/auth.service'; import { AuthService } from '../services/auth.service';
import { ConfigState } from '../states/config.state'; import { ConfigState } from '../states/config.state';
import { clearOAuthStorage } from '../strategies/auth-flow.strategy'; import { clearOAuthStorage } from '../strategies/auth-flow.strategy';
import { LocaleErrorHandlerData, LOCALE_ERROR_HANDLER } from '../tokens/locale-error-handler.token';
import { CORE_OPTIONS } from '../tokens/options.token'; import { CORE_OPTIONS } from '../tokens/options.token';
import { getRemoteEnv } from './environment-utils'; import { getRemoteEnv } from './environment-utils';
import { parseTenantFromUrl } from './multi-tenancy-utils'; import { parseTenantFromUrl } from './multi-tenancy-utils';
@ -43,68 +42,18 @@ export function checkAccessToken(store: Store, injector: Injector) {
export function localeInitializer(injector: Injector) { export function localeInitializer(injector: Injector) {
const fn = () => { const fn = () => {
const store: Store = injector.get(Store); const store: Store = injector.get(Store);
const { registerLocaleFn }: ABP.Root = injector.get(CORE_OPTIONS);
const lang = store.selectSnapshot(state => state.SessionState.language) || 'en'; const lang = store.selectSnapshot(state => state.SessionState.language) || 'en';
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
registerLocale(lang, injector).then(() => resolve('resolved'), reject); registerLocaleFn(lang).then(module => {
if (module?.default) registerLocaleData(module.default);
return resolve('resolved');
}, reject);
}); });
}; };
return fn; return fn;
} }
export function registerLocale(locale: string, injector: Injector): Promise<any> {
const { cultureNameLocaleFileMap } = injector.get(CORE_OPTIONS, {} as ABP.Root);
const errorHandlerFn = injector.get(LOCALE_ERROR_HANDLER, defaultLocalErrorHandlerFn);
return new Promise((resolve, reject) => {
return import(
/* webpackChunkName: "_locale-[request]"*/
/* webpackInclude: /[/\\](ar|cs|en|fr|pt|tr|ru|hu|sl|zh-Hans|zh-Hant).js/ */
/* webpackExclude: /[/\\]global|extra/ */
`@angular/common/locales/${cultureNameLocaleFileMap[locale] || locale}.js`
)
.then(module => {
registerLocaleData(module.default, locale);
resolve(module.default);
})
.catch(error => {
errorHandlerFn({
resolve,
reject,
error,
injector,
locale,
storedLocales: { ...extraLocales },
});
});
});
}
const extraLocales = {};
export function storeLocaleData(data: any, localeId: string) {
extraLocales[localeId] = data;
}
async function defaultLocalErrorHandlerFn({
locale,
storedLocales,
resolve,
injector,
}: LocaleErrorHandlerData) {
if (storedLocales[locale]) {
registerLocaleData(storedLocales[locale], locale);
resolve();
return;
}
if (isDevMode) {
console.error(
`Cannot find the ${locale} locale file. You can check how can add new culture at https://docs.abp.io/en/abp/latest/UI/Angular/Localization#adding-new-culture`,
);
}
resolve();
}

1
npm/ng-packs/tsconfig.json

@ -16,6 +16,7 @@
"types": ["jest"], "types": ["jest"],
"paths": { "paths": {
"@abp/ng.core": ["packages/core/src/public-api.ts"], "@abp/ng.core": ["packages/core/src/public-api.ts"],
"@abp/ng.core/locale": ["packages/core/locale/src/public-api.ts"],
"@abp/ng.theme.shared": ["packages/theme-shared/src/public-api.ts"], "@abp/ng.theme.shared": ["packages/theme-shared/src/public-api.ts"],
"@abp/ng.theme.shared/extensions": ["packages/theme-shared/extensions/src/public-api.ts"], "@abp/ng.theme.shared/extensions": ["packages/theme-shared/extensions/src/public-api.ts"],
"@abp/ng.components/tree": ["packages/components/tree/src/public-api.ts"], "@abp/ng.components/tree": ["packages/components/tree/src/public-api.ts"],

1585
npm/ng-packs/yarn.lock

File diff suppressed because it is too large

2
templates/app/angular/src/app/app.module.ts

@ -1,5 +1,6 @@
import { AccountConfigModule } from '@abp/ng.account/config'; import { AccountConfigModule } from '@abp/ng.account/config';
import { CoreModule } from '@abp/ng.core'; import { CoreModule } from '@abp/ng.core';
import { registerLocale } from '@abp/ng.core/locale';
import { IdentityConfigModule } from '@abp/ng.identity/config'; import { IdentityConfigModule } from '@abp/ng.identity/config';
import { SettingManagementConfigModule } from '@abp/ng.setting-management/config'; import { SettingManagementConfigModule } from '@abp/ng.setting-management/config';
import { TenantManagementConfigModule } from '@abp/ng.tenant-management/config'; import { TenantManagementConfigModule } from '@abp/ng.tenant-management/config';
@ -21,6 +22,7 @@ import { APP_ROUTE_PROVIDER } from './route.provider';
AppRoutingModule, AppRoutingModule,
CoreModule.forRoot({ CoreModule.forRoot({
environment, environment,
registerLocaleFn: registerLocale(),
}), }),
ThemeSharedModule.forRoot(), ThemeSharedModule.forRoot(),
AccountConfigModule.forRoot(), AccountConfigModule.forRoot(),

2
templates/module/angular/projects/dev-app/src/app/app.module.ts

@ -1,5 +1,6 @@
import { AccountConfigModule } from '@abp/ng.account/config'; import { AccountConfigModule } from '@abp/ng.account/config';
import { CoreModule } from '@abp/ng.core'; import { CoreModule } from '@abp/ng.core';
import { registerLocale } from '@abp/ng.core/locale';
import { IdentityConfigModule } from '@abp/ng.identity/config'; import { IdentityConfigModule } from '@abp/ng.identity/config';
import { SettingManagementConfigModule } from '@abp/ng.setting-management/config'; import { SettingManagementConfigModule } from '@abp/ng.setting-management/config';
import { TenantManagementConfigModule } from '@abp/ng.tenant-management/config'; import { TenantManagementConfigModule } from '@abp/ng.tenant-management/config';
@ -22,6 +23,7 @@ import { ThemeBasicModule } from '@abp/ng.theme.basic';
AppRoutingModule, AppRoutingModule,
CoreModule.forRoot({ CoreModule.forRoot({
environment, environment,
registerLocaleFn: registerLocale(),
sendNullsAsQueryParam: false, sendNullsAsQueryParam: false,
skipGetAppConfiguration: false, skipGetAppConfiguration: false,
}), }),

Loading…
Cancel
Save