Browse Source

add support for ui localizations

pull/10617/head
mehmet-erim 5 years ago
parent
commit
12682ad238
  1. 21
      npm/ng-packs/packages/core/src/lib/core.module.ts
  2. 15
      npm/ng-packs/packages/core/src/lib/models/common.ts
  3. 161
      npm/ng-packs/packages/core/src/lib/services/localization.service.ts
  4. 1
      npm/ng-packs/packages/core/src/lib/tokens/index.ts
  5. 13
      npm/ng-packs/packages/core/src/lib/tokens/localization.token.ts

21
npm/ng-packs/packages/core/src/lib/core.module.ts

@ -27,6 +27,7 @@ import { CookieLanguageProvider } from './providers/cookie-language.provider';
import { LocaleProvider } from './providers/locale.provider';
import { LocalizationService } from './services/localization.service';
import { oAuthStorage } from './strategies/auth-flow.strategy';
import { localizationContributor, LOCALIZATIONS } from './tokens/localization.token';
import { coreOptionsFactory, CORE_OPTIONS } from './tokens/options.token';
import { TENANT_KEY } from './tokens/tenant-key.token';
import { noop } from './utils/common-utils';
@ -176,6 +177,26 @@ export class CoreModule {
},
{ provide: OAuthStorage, useFactory: storageFactory },
{ provide: TENANT_KEY, useValue: options.tenantKey || '__tenant' },
{
provide: LOCALIZATIONS,
multi: true,
useValue: localizationContributor(options.localizations),
deps: [LocalizationService],
},
],
};
}
static forChild(options = {} as ABP.Child): ModuleWithProviders<RootCoreModule> {
return {
ngModule: RootCoreModule,
providers: [
{
provide: LOCALIZATIONS,
multi: true,
useValue: localizationContributor(options.localizations),
deps: [LocalizationService],
},
],
};
}

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

@ -11,6 +11,21 @@ export namespace ABP {
skipGetAppConfiguration?: boolean;
sendNullsAsQueryParam?: boolean;
tenantKey?: string;
localizations?: Localization[];
}
export interface Child {
localizations?: Localization[];
}
export interface Localization {
culture: string;
resources: LocalizationResource[];
}
export interface LocalizationResource {
resourceName: string;
texts: Record<string, string>;
}
export interface HasPolicy {

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

@ -1,10 +1,11 @@
import { registerLocaleData } from '@angular/common';
import { Injectable, Injector, isDevMode, Optional, SkipSelf } from '@angular/core';
import { from, Observable, Subject } from 'rxjs';
import { BehaviorSubject, combineLatest, from, Observable, Subject } from 'rxjs';
import { filter, map, mapTo, switchMap } from 'rxjs/operators';
import { ABP } from '../models/common';
import { LocalizationWithDefault } from '../models/localization';
import { ApplicationConfigurationDto } from '../proxy/volo/abp/asp-net-core/mvc/application-configurations/models';
import { localizations$ } from '../tokens/localization.token';
import { CORE_OPTIONS } from '../tokens/options.token';
import { createLocalizer, createLocalizerWithFallback } from '../utils/localization-utils';
import { interpolate } from '../utils/string-utils';
@ -16,6 +17,12 @@ export class LocalizationService {
private latestLang = this.sessionState.getLanguage();
private _languageChange$ = new Subject<string>();
private localLocalizations$ = new BehaviorSubject(
new Map<string, Map<string, Record<string, string>>>(),
);
private localizations$ = new BehaviorSubject(new Map<string, Record<string, string>>());
/**
* Returns currently selected language
*/
@ -38,6 +45,62 @@ export class LocalizationService {
if (otherInstance) throw new Error('LocalizationService should have only one instance.');
this.listenToSetLanguage();
this.initLocalizationValues();
}
private initLocalizationValues() {
localizations$.subscribe(val => this.addLocalization(val));
const remoteLocalizations$ = this.configState.getDeep$('localization.values') as Observable<
Record<string, Record<string, string>>
>;
const currentLanguage$ = this.sessionState.getLanguage$();
const localLocalizations$ = combineLatest([currentLanguage$, this.localLocalizations$]).pipe(
map(([currentLang, localizations]) => localizations.get(currentLang)),
);
combineLatest([remoteLocalizations$, localLocalizations$])
.pipe(
map(([remote, local]) => {
if (remote) {
Object.entries(remote).forEach(entry => {
const resourceName = entry[0];
const remoteTexts = entry[1];
let resource = local.get(resourceName) || {};
resource = { ...resource, ...remoteTexts };
local.set(resourceName, resource);
});
}
return local;
}),
)
.subscribe(val => this.localizations$.next(val));
}
addLocalization(localizations?: ABP.Localization[]) {
if (!localizations) return;
const localizationMap = this.localLocalizations$.value;
localizations.forEach(loc => {
const cultureMap =
localizationMap.get(loc.culture) || new Map<string, Record<string, string>>();
loc.resources.forEach(res => {
let resource: Record<string, string> = cultureMap.get(res.resourceName) || {};
resource = { ...resource, ...res.texts };
cultureMap.set(res.resourceName, resource);
});
localizationMap.set(loc.culture, cultureMap);
});
this.localLocalizations$.next(localizationMap);
}
private listenToSetLanguage() {
@ -70,15 +133,15 @@ export class LocalizationService {
get(key: string | LocalizationWithDefault, ...interpolateParams: string[]): Observable<string> {
return this.configState
.getAll$()
.pipe(map(state => getLocalization(state, key, ...interpolateParams)));
.pipe(map(state => this.getLocalization(state, key, ...interpolateParams)));
}
getResource(resourceName: string) {
return this.configState.getDeep(`localization.values.${resourceName}`);
return this.localizations$.value.get(resourceName);
}
getResource$(resourceName: string) {
return this.configState.getDeep$(`localization.values.${resourceName}`);
return this.localizations$.pipe(map(res => res.get(resourceName)));
}
/**
@ -87,7 +150,7 @@ export class LocalizationService {
* @param interpolateParams Values to intepolate.
*/
instant(key: string | LocalizationWithDefault, ...interpolateParams: string[]): string {
return getLocalization(this.configState.getAll(), key, ...interpolateParams);
return this.getLocalization(this.configState.getAll(), key, ...interpolateParams);
}
localize(resourceName: string, key: string, defaultValue: string): Observable<string> {
@ -117,60 +180,62 @@ export class LocalizationService {
const localization = this.configState.getOne('localization');
return createLocalizerWithFallback(localization)(resourceNames, keys, defaultValue);
}
}
function getLocalization(
state: ApplicationConfigurationDto,
key: string | LocalizationWithDefault,
...interpolateParams: string[]
) {
if (!key) key = '';
let defaultValue: string;
private getLocalization(
state: ApplicationConfigurationDto,
key: string | LocalizationWithDefault,
...interpolateParams: string[]
) {
if (!key) key = '';
let defaultValue: string;
if (typeof key !== 'string') {
defaultValue = key.defaultValue;
key = key.key;
}
if (typeof key !== 'string') {
defaultValue = key.defaultValue;
key = key.key;
}
const keys = key.split('::') as string[];
const warn = (message: string) => {
if (isDevMode) console.warn(message);
};
const keys = key.split('::') as string[];
const warn = (message: string) => {
if (isDevMode) console.warn(message);
};
if (keys.length < 2) {
warn('The localization source separator (::) not found.');
return defaultValue || (key as string);
}
if (!state.localization) return defaultValue || keys[1];
if (keys.length < 2) {
warn('The localization source separator (::) not found.');
return defaultValue || (key as string);
}
if (!state.localization) return defaultValue || keys[1];
const sourceName = keys[0] || state.localization.defaultResourceName;
const sourceKey = keys[1];
const sourceName = keys[0] || state.localization.defaultResourceName;
const sourceKey = keys[1];
if (sourceName === '_') {
return defaultValue || sourceKey;
}
if (sourceName === '_') {
return defaultValue || sourceKey;
}
if (!sourceName) {
warn('Localization source name is not specified and the defaultResourceName was not defined!');
if (!sourceName) {
warn(
'Localization source name is not specified and the defaultResourceName was not defined!',
);
return defaultValue || sourceKey;
}
return defaultValue || sourceKey;
}
const source = state.localization.values[sourceName];
if (!source) {
warn('Could not find localization source: ' + sourceName);
return defaultValue || sourceKey;
}
const source = this.localizations$.value.get(sourceName);
if (!source) {
warn('Could not find localization source: ' + sourceName);
return defaultValue || sourceKey;
}
let localization = source[sourceKey];
if (typeof localization === 'undefined') {
return defaultValue || sourceKey;
}
let localization = source[sourceKey];
if (typeof localization === 'undefined') {
return defaultValue || sourceKey;
}
interpolateParams = interpolateParams.filter(params => params != null);
if (localization) localization = interpolate(localization, interpolateParams);
interpolateParams = interpolateParams.filter(params => params != null);
if (localization) localization = interpolate(localization, interpolateParams);
if (typeof localization !== 'string') localization = '';
if (typeof localization !== 'string') localization = '';
return localization || defaultValue || (key as string);
return localization || defaultValue || (key as string);
}
}

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

@ -1,6 +1,7 @@
export * from './app-config.token';
export * from './cookie-language-key.token';
export * from './list.token';
export * from './localization.token';
export * from './lodaer-delay.token';
export * from './manage-profile.token';
export * from './options.token';

13
npm/ng-packs/packages/core/src/lib/tokens/localization.token.ts

@ -0,0 +1,13 @@
import { InjectionToken } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { ABP } from '../models/common';
export const LOCALIZATIONS = new InjectionToken('LOCALIZATIONS');
export function localizationContributor(localizations: ABP.Localization[]) {
if (localizations) {
localizations$.next([...localizations$.value, ...localizations]);
}
}
export const localizations$ = new BehaviorSubject([]);
Loading…
Cancel
Save