diff --git a/npm/ng-packs/packages/core/src/lib/services/localization.service.ts b/npm/ng-packs/packages/core/src/lib/services/localization.service.ts index 96f9dd5ce8..60cd956044 100644 --- a/npm/ng-packs/packages/core/src/lib/services/localization.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/localization.service.ts @@ -1,19 +1,19 @@ import { registerLocaleData } from '@angular/common'; -import { Injectable, Injector, NgZone, Optional, SkipSelf } from '@angular/core'; -import { ActivatedRouteSnapshot, Router } from '@angular/router'; +import { Injectable, Injector, isDevMode, NgZone, Optional, SkipSelf } from '@angular/core'; +import { Router } from '@angular/router'; import { Store } from '@ngxs/store'; -import { noop, Observable, of, Subject } from 'rxjs'; -import { filter, map, mapTo, switchMap } from 'rxjs/operators'; -import { GetAppConfiguration } from '../actions/config.actions'; +import { noop, Observable, Subject } from 'rxjs'; +import { filter, map, mapTo, switchMap, tap } from 'rxjs/operators'; +import { ApplicationConfiguration } from '../models/application-configuration'; import { ABP } from '../models/common'; import { Config } from '../models/config'; -import { ConfigState } from '../states/config.state'; import { CORE_OPTIONS } from '../tokens/options.token'; import { createLocalizer, createLocalizerWithFallback } from '../utils/localization-utils'; +import { interpolate } from '../utils/string-utils'; +import { ApplicationConfigurationService } from './application-configuration.service'; +import { ConfigStateService } from './config-state.service'; import { SessionStateService } from './session-state.service'; -type ShouldReuseRoute = (future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot) => boolean; - @Injectable({ providedIn: 'root' }) export class LocalizationService { private latestLang = this.sessionState.getLanguage(); @@ -38,6 +38,8 @@ export class LocalizationService { @Optional() @SkipSelf() otherInstance: LocalizationService, + private configState: ConfigStateService, + private appConfigService: ApplicationConfigurationService, ) { if (otherInstance) throw new Error('LocalizationService should have only one instance.'); @@ -49,12 +51,14 @@ export class LocalizationService { .onLanguageChange$() .pipe( filter( - lang => - this.store.selectSnapshot( - ConfigState.getDeep('localization.currentCulture.cultureName'), - ) !== lang, + lang => this.configState.getDeep('localization.currentCulture.cultureName') !== lang, + ), + switchMap(lang => + this.appConfigService + .getConfiguration() + .pipe(tap(res => this.configState.setState(res))) + .pipe(mapTo(lang)), ), - switchMap(lang => this.store.dispatch(new GetAppConfiguration()).pipe(mapTo(lang))), ) .subscribe(lang => { this.registerLocale(lang); @@ -90,11 +94,17 @@ export class LocalizationService { key: string | Config.LocalizationWithDefault, ...interpolateParams: string[] ): Observable { - return this.store.select(ConfigState.getLocalization(key, ...interpolateParams)); + return this.configState + .getAll$() + .pipe(map(state => getLocalization(state, key, ...interpolateParams))); } getResource(resourceName: string) { - return this.store.select(ConfigState.getLocalizationResource(resourceName)); + return this.configState.getDeep(`localization.values.${resourceName}`); + } + + getResource$(resourceName: string) { + return this.configState.getDeep$(`localization.values.${resourceName}`); } /** @@ -103,18 +113,18 @@ export class LocalizationService { * @param interpolateParams Values to intepolate. */ instant(key: string | Config.LocalizationWithDefault, ...interpolateParams: string[]): string { - return this.store.selectSnapshot(ConfigState.getLocalization(key, ...interpolateParams)); + return getLocalization(this.configState.getAll(), key, ...interpolateParams); } localize(resourceName: string, key: string, defaultValue: string): Observable { - return this.store.select(ConfigState.getOne('localization')).pipe( + return this.configState.getOne$('localization').pipe( map(createLocalizer), map(localize => localize(resourceName, key, defaultValue)), ); } localizeSync(resourceName: string, key: string, defaultValue: string): string { - const localization = this.store.selectSnapshot(ConfigState.getOne('localization')); + const localization = this.configState.getOne('localization'); return createLocalizer(localization)(resourceName, key, defaultValue); } @@ -123,14 +133,71 @@ export class LocalizationService { keys: string[], defaultValue: string, ): Observable { - return this.store.select(ConfigState.getOne('localization')).pipe( + return this.configState.getOne$('localization').pipe( map(createLocalizerWithFallback), map(localizeWithFallback => localizeWithFallback(resourceNames, keys, defaultValue)), ); } localizeWithFallbackSync(resourceNames: string[], keys: string[], defaultValue: string): string { - const localization = this.store.selectSnapshot(ConfigState.getOne('localization')); + const localization = this.configState.getOne('localization'); return createLocalizerWithFallback(localization)(resourceNames, keys, defaultValue); } } + +function getLocalization( + state: ApplicationConfiguration.Response, + key: string | Config.LocalizationWithDefault, + ...interpolateParams: string[] +) { + if (!key) key = ''; + let defaultValue: string; + + 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); + }; + + 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]; + + if (sourceName === '_') { + return defaultValue || sourceKey; + } + + if (!sourceName) { + warn('Localization source name is not specified and the defaultResourceName was not defined!'); + + return defaultValue || sourceKey; + } + + const source = state.localization.values[sourceName]; + if (!source) { + warn('Could not find localization source: ' + sourceName); + return defaultValue || sourceKey; + } + + let localization = source[sourceKey]; + if (typeof localization === 'undefined') { + return defaultValue || sourceKey; + } + + // [TODO]: next line should be removed in v3.2, breaking change!!! + interpolateParams = interpolateParams.filter(params => params != null); + if (localization) localization = interpolate(localization, interpolateParams); + + if (typeof localization !== 'string') localization = ''; + + return localization || defaultValue || (key as string); +}