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 { LocaleProvider } from './providers/locale.provider';
import { LocalizationService } from './services/localization.service'; import { LocalizationService } from './services/localization.service';
import { oAuthStorage } from './strategies/auth-flow.strategy'; import { oAuthStorage } from './strategies/auth-flow.strategy';
import { localizationContributor, LOCALIZATIONS } from './tokens/localization.token';
import { coreOptionsFactory, CORE_OPTIONS } from './tokens/options.token'; import { coreOptionsFactory, CORE_OPTIONS } from './tokens/options.token';
import { TENANT_KEY } from './tokens/tenant-key.token'; import { TENANT_KEY } from './tokens/tenant-key.token';
import { noop } from './utils/common-utils'; import { noop } from './utils/common-utils';
@ -176,6 +177,26 @@ export class CoreModule {
}, },
{ provide: OAuthStorage, useFactory: storageFactory }, { provide: OAuthStorage, useFactory: storageFactory },
{ provide: TENANT_KEY, useValue: options.tenantKey || '__tenant' }, { 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; skipGetAppConfiguration?: boolean;
sendNullsAsQueryParam?: boolean; sendNullsAsQueryParam?: boolean;
tenantKey?: string; 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 { 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 { registerLocaleData } from '@angular/common';
import { Injectable, Injector, isDevMode, Optional, SkipSelf } from '@angular/core'; 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 { filter, map, mapTo, switchMap } from 'rxjs/operators';
import { ABP } from '../models/common'; import { ABP } from '../models/common';
import { LocalizationWithDefault } from '../models/localization'; import { LocalizationWithDefault } from '../models/localization';
import { ApplicationConfigurationDto } from '../proxy/volo/abp/asp-net-core/mvc/application-configurations/models'; 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 { CORE_OPTIONS } from '../tokens/options.token';
import { createLocalizer, createLocalizerWithFallback } from '../utils/localization-utils'; import { createLocalizer, createLocalizerWithFallback } from '../utils/localization-utils';
import { interpolate } from '../utils/string-utils'; import { interpolate } from '../utils/string-utils';
@ -16,6 +17,12 @@ export class LocalizationService {
private latestLang = this.sessionState.getLanguage(); private latestLang = this.sessionState.getLanguage();
private _languageChange$ = new Subject<string>(); 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 * Returns currently selected language
*/ */
@ -38,6 +45,62 @@ export class LocalizationService {
if (otherInstance) throw new Error('LocalizationService should have only one instance.'); if (otherInstance) throw new Error('LocalizationService should have only one instance.');
this.listenToSetLanguage(); 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() { private listenToSetLanguage() {
@ -70,15 +133,15 @@ export class LocalizationService {
get(key: string | LocalizationWithDefault, ...interpolateParams: string[]): Observable<string> { get(key: string | LocalizationWithDefault, ...interpolateParams: string[]): Observable<string> {
return this.configState return this.configState
.getAll$() .getAll$()
.pipe(map(state => getLocalization(state, key, ...interpolateParams))); .pipe(map(state => this.getLocalization(state, key, ...interpolateParams)));
} }
getResource(resourceName: string) { getResource(resourceName: string) {
return this.configState.getDeep(`localization.values.${resourceName}`); return this.localizations$.value.get(resourceName);
} }
getResource$(resourceName: string) { 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. * @param interpolateParams Values to intepolate.
*/ */
instant(key: string | LocalizationWithDefault, ...interpolateParams: string[]): string { 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> { localize(resourceName: string, key: string, defaultValue: string): Observable<string> {
@ -117,60 +180,62 @@ export class LocalizationService {
const localization = this.configState.getOne('localization'); const localization = this.configState.getOne('localization');
return createLocalizerWithFallback(localization)(resourceNames, keys, defaultValue); return createLocalizerWithFallback(localization)(resourceNames, keys, defaultValue);
} }
}
function getLocalization( private getLocalization(
state: ApplicationConfigurationDto, state: ApplicationConfigurationDto,
key: string | LocalizationWithDefault, key: string | LocalizationWithDefault,
...interpolateParams: string[] ...interpolateParams: string[]
) { ) {
if (!key) key = ''; if (!key) key = '';
let defaultValue: string; let defaultValue: string;
if (typeof key !== 'string') { if (typeof key !== 'string') {
defaultValue = key.defaultValue; defaultValue = key.defaultValue;
key = key.key; key = key.key;
} }
const keys = key.split('::') as string[]; const keys = key.split('::') as string[];
const warn = (message: string) => { const warn = (message: string) => {
if (isDevMode) console.warn(message); if (isDevMode) console.warn(message);
}; };
if (keys.length < 2) { if (keys.length < 2) {
warn('The localization source separator (::) not found.'); warn('The localization source separator (::) not found.');
return defaultValue || (key as string); return defaultValue || (key as string);
} }
if (!state.localization) return defaultValue || keys[1]; if (!state.localization) return defaultValue || keys[1];
const sourceName = keys[0] || state.localization.defaultResourceName; const sourceName = keys[0] || state.localization.defaultResourceName;
const sourceKey = keys[1]; const sourceKey = keys[1];
if (sourceName === '_') { if (sourceName === '_') {
return defaultValue || sourceKey; return defaultValue || sourceKey;
} }
if (!sourceName) { if (!sourceName) {
warn('Localization source name is not specified and the defaultResourceName was not defined!'); 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]; const source = this.localizations$.value.get(sourceName);
if (!source) { if (!source) {
warn('Could not find localization source: ' + sourceName); warn('Could not find localization source: ' + sourceName);
return defaultValue || sourceKey; return defaultValue || sourceKey;
} }
let localization = source[sourceKey]; let localization = source[sourceKey];
if (typeof localization === 'undefined') { if (typeof localization === 'undefined') {
return defaultValue || sourceKey; return defaultValue || sourceKey;
} }
interpolateParams = interpolateParams.filter(params => params != null); interpolateParams = interpolateParams.filter(params => params != null);
if (localization) localization = interpolate(localization, interpolateParams); 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 './app-config.token';
export * from './cookie-language-key.token'; export * from './cookie-language-key.token';
export * from './list.token'; export * from './list.token';
export * from './localization.token';
export * from './lodaer-delay.token'; export * from './lodaer-delay.token';
export * from './manage-profile.token'; export * from './manage-profile.token';
export * from './options.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