From 7b7291416f03f5b1166e44e82d9ba4c4bdc016cd Mon Sep 17 00:00:00 2001 From: Andreas Loew Date: Wed, 13 Nov 2024 15:27:40 +0100 Subject: [PATCH] Simplified Isolation: Using a separate store object instead of duplicating variables locally and using if to distinguish between both. --- .../src/lib/translate.service.spec.ts | 128 +++++++++++++++++- .../src/lib/translate.service.ts | 59 +++----- 2 files changed, 146 insertions(+), 41 deletions(-) diff --git a/projects/ngx-translate/src/lib/translate.service.spec.ts b/projects/ngx-translate/src/lib/translate.service.spec.ts index 52b9dd1..c438979 100644 --- a/projects/ngx-translate/src/lib/translate.service.spec.ts +++ b/projects/ngx-translate/src/lib/translate.service.spec.ts @@ -1,12 +1,14 @@ import {fakeAsync, TestBed, tick} from "@angular/core/testing"; -import {Observable, of, timer, zip, defer} from "rxjs"; +import {Observable, of, timer, zip, defer, EMPTY} from "rxjs"; import {take, toArray, first, map} from "rxjs/operators"; import { LangChangeEvent, TranslateLoader, TranslateService, - TranslationChangeEvent, TranslationObject, Translation, provideTranslateService + TranslationChangeEvent, TranslationObject, Translation, provideTranslateService, TranslatePipe, TranslateModule } from "../public-api"; +import {Component} from "@angular/core"; +import {Router, RouterOutlet} from "@angular/router"; let translations: TranslationObject = {"TEST": "This is a test"}; @@ -1039,6 +1041,126 @@ describe("TranslateService", () => expect(translate.getBrowserCultureLang()).toBeUndefined(); }); }); +}); -}); +describe('TranslateService (isolate)', () => { + const translationsRoot = { + en: {test : "en-root"}, + de: {test : "de-root"} + } + + const translationsChild = { + en: {test: "en-child"}, + de: {test : "de-child"} + } + + class StaticTranslateLoader implements TranslateLoader { + constructor(private translations: Record) { + } + + getTranslation(lang: string) { + const translations = this.translations[lang]; + if (translations) { + return of(translations) + } else { + return EMPTY; + } + } + } + + @Component({ + standalone: true, + selector: "lib-isolated-child", + template: ` +
{{ 'test' | translate }}
+ `, + imports: [ + TranslatePipe + ], + providers: [ + TranslateModule.forChild({ + isolate: true, + loader: { + provide: TranslateLoader, + useFactory: () => new StaticTranslateLoader(translationsChild), + }, + }).providers! + ] + }) + class IsolatedChildComponent + { + constructor(private translate:TranslateService) + { + translate.use("de"); + } + } + + @Component({ + standalone: true, + selector: "lib-shared-child", + template: ` +
{{ 'test' | translate }}
+ `, + imports: [ + TranslatePipe + ], + providers: [ + TranslateModule.forChild({}).providers! + ] + }) + class SharedChildComponent + { + } + + @Component({ + standalone: true, + imports: [RouterOutlet, IsolatedChildComponent, SharedChildComponent, TranslatePipe], + selector: "lib-test", + template: ` +
{{ 'test' | translate }}
+ + + ` + }) + class AppTestComponent { + constructor(private translate:TranslateService) + { + translate.use("en"); + } + } + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + provideTranslateService({ + extend: true, + loader: { + provide: TranslateLoader, + useFactory: () => new StaticTranslateLoader(translationsRoot) + } + }), + ] + }).compileComponents(); + }) + + it('switches root and child component independently', async () => { + const fixture = TestBed.createComponent(AppTestComponent); + + const app = fixture.nativeElement + + fixture.detectChanges(); + + expect(app.querySelector("div.root").textContent).toEqual("en-root"); + expect(app.querySelector("div.isolated-child").textContent).toEqual("de-child"); + expect(app.querySelector("div.shared-child").textContent).toEqual("en-root"); + + // switch root + TestBed.inject(TranslateService).use("de"); + fixture.detectChanges(); + + expect(app.querySelector("div.root").textContent).toEqual("de-root"); + expect(app.querySelector("div.isolated-child").textContent).toEqual("de-child"); + expect(app.querySelector("div.shared-child").textContent).toEqual("de-root"); + }) +}) \ No newline at end of file diff --git a/projects/ngx-translate/src/lib/translate.service.ts b/projects/ngx-translate/src/lib/translate.service.ts index 53bc1c4..c9047c5 100644 --- a/projects/ngx-translate/src/lib/translate.service.ts +++ b/projects/ngx-translate/src/lib/translate.service.ts @@ -91,13 +91,6 @@ const makeObservable = (value: T | Observable): Observable => { export class TranslateService { private loadingTranslations!: Observable; private pending = false; - private _onTranslationChange: EventEmitter = new EventEmitter(); - private _onLangChange: EventEmitter = new EventEmitter(); - private _onDefaultLangChange: EventEmitter = new EventEmitter(); - private _defaultLang!: string; - private _currentLang!: string; - private _langs: string[] = []; - private _translations: Record = {}; private _translationRequests: Record> = {}; private lastUseLanguage: string|null = null; @@ -109,7 +102,7 @@ export class TranslateService { * }); */ get onTranslationChange(): EventEmitter { - return this.isolate ? this._onTranslationChange : this.store.onTranslationChange; + return this.store.onTranslationChange; } /** @@ -119,7 +112,7 @@ export class TranslateService { * }); */ get onLangChange(): EventEmitter { - return this.isolate ? this._onLangChange : this.store.onLangChange; + return this.store.onLangChange; } /** @@ -129,67 +122,51 @@ export class TranslateService { * }); */ get onDefaultLangChange() { - return this.isolate ? this._onDefaultLangChange : this.store.onDefaultLangChange; + return this.store.onDefaultLangChange; } /** * The default lang to fallback when translations are missing on the current lang */ get defaultLang(): string { - return this.isolate ? this._defaultLang : this.store.defaultLang; + return this.store.defaultLang; } set defaultLang(defaultLang: string) { - if (this.isolate) { - this._defaultLang = defaultLang; - } else { - this.store.defaultLang = defaultLang; - } + this.store.defaultLang = defaultLang; } /** * The lang currently used */ get currentLang(): string { - return this.isolate ? this._currentLang : this.store.currentLang; + return this.store.currentLang; } set currentLang(currentLang: string) { - if (this.isolate) { - this._currentLang = currentLang; - } else { - this.store.currentLang = currentLang; - } + this.store.currentLang = currentLang; } /** * an array of langs */ get langs(): string[] { - return this.isolate ? this._langs : this.store.langs; + return this.store.langs; } set langs(langs: string[]) { - if (this.isolate) { - this._langs = langs; - } else { - this.store.langs = langs; - } + this.store.langs = langs; } /** * a list of translations per lang */ get translations(): Record { - return this.isolate ? this._translations : this.store.translations; + return this.store.translations; } set translations(translations: Record) { - if (this.isolate) { - this._translations = translations; - } else { - this.store.translations = translations; - } + this.store.translations = translations; } /** @@ -210,10 +187,16 @@ export class TranslateService { public parser: TranslateParser, public missingTranslationHandler: MissingTranslationHandler, @Inject(USE_DEFAULT_LANG) private useDefaultLang = true, - @Inject(ISOALTE_TRANSLATE_SERVICE) private isolate = false, + @Inject(ISOALTE_TRANSLATE_SERVICE) isolate = false, @Inject(USE_EXTEND) private extend = false, - @Inject(DEFAULT_LANGUAGE) defaultLanguage: string) { - /** set the default language from configuration */ + @Inject(DEFAULT_LANGUAGE) defaultLanguage: string + ) + { + if(isolate) + { + this.store = new TranslateStore(); + } + if (defaultLanguage) { this.setDefaultLang(defaultLanguage); } @@ -361,7 +344,7 @@ export class TranslateService { this.loadingTranslations .subscribe({ next: (res: InterpolatableTranslationObject) => { - this.translations[lang] = this.extend && this.translations[lang] ? { ...res, ...this.translations[lang] } : res; + this.translations[lang] = (this.extend && this.translations[lang]) ? { ...res, ...this.translations[lang] } : res; this.updateLangs(); this.pending = false; },