Skip to content

Commit 6b675d6

Browse files
authored
feat(core): add option for lazy loaded modules to extend translations
Fixes #425
1 parent 31b77ae commit 6b675d6

File tree

4 files changed

+44
-7
lines changed

4 files changed

+44
-7
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@ export class SharedModule { }
8787
When you lazy load a module, you should use the `forChild` static method to import the `TranslateModule`.
8888

8989
Since lazy loaded modules use a different injector from the rest of your application, you can configure them separately with a different loader/compiler/parser/missing translations handler.
90+
91+
To make a child module extend translations from parent modules use `extend: true`. This will cause the service to also
92+
use translations from its parent module.
93+
9094
You can also isolate the service by using `isolate: true`. In which case the service is a completely isolated instance (for translations, current lang, events, ...).
9195
Otherwise, by default, it will share its data with other instances of the service (but you can still use a different loader/compiler/parser/handler even if you don't isolate the service).
9296

projects/ngx-translate/core/src/lib/translate.service.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {isDefined, mergeDeep} from "./util";
1111

1212
export const USE_STORE = new InjectionToken<string>('USE_STORE');
1313
export const USE_DEFAULT_LANG = new InjectionToken<string>('USE_DEFAULT_LANG');
14+
export const USE_EXTEND = new InjectionToken<string>('USE_EXTEND');
1415

1516
export interface TranslationChangeEvent {
1617
translations: any;
@@ -152,7 +153,8 @@ export class TranslateService {
152153
public parser: TranslateParser,
153154
public missingTranslationHandler: MissingTranslationHandler,
154155
@Inject(USE_DEFAULT_LANG) private useDefaultLang: boolean = true,
155-
@Inject(USE_STORE) private isolate: boolean = false) {
156+
@Inject(USE_STORE) private isolate: boolean = false,
157+
@Inject(USE_EXTEND) private extend: boolean = false) {
156158
}
157159

158160
/**
@@ -223,8 +225,8 @@ export class TranslateService {
223225
private retrieveTranslations(lang: string): Observable<any> {
224226
let pending: Observable<any>;
225227

226-
// if this language is unavailable, ask for it
227-
if (typeof this.translations[lang] === "undefined") {
228+
// if this language is unavailable or extend is true, ask for it
229+
if (typeof this.translations[lang] === "undefined" || this.extend) {
228230
this._translationRequests[lang] = this._translationRequests[lang] || this.getTranslation(lang);
229231
pending = this._translationRequests[lang];
230232
}
@@ -252,7 +254,7 @@ export class TranslateService {
252254
this.loadingTranslations
253255
.subscribe({
254256
next: (res: Object) => {
255-
this.translations[lang] = res;
257+
this.translations[lang] = this.extend && this.translations[lang] ? { ...res, ...this.translations[lang] } : res;
256258
this.updateLangs();
257259
this.pending = false;
258260
},
@@ -270,7 +272,7 @@ export class TranslateService {
270272
*/
271273
public setTranslation(lang: string, translations: Object, shouldMerge: boolean = false): void {
272274
translations = this.compiler.compileTranslations(translations, lang);
273-
if (shouldMerge && this.translations[lang]) {
275+
if ((shouldMerge || this.extend) && this.translations[lang]) {
274276
this.translations[lang] = mergeDeep(this.translations[lang], translations);
275277
} else {
276278
this.translations[lang] = translations;

projects/ngx-translate/core/src/public_api.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {TranslateDirective} from "./lib/translate.directive";
88
import {TranslatePipe} from "./lib/translate.pipe";
99
import {TranslateStore} from "./lib/translate.store";
1010
import {USE_STORE} from "./lib/translate.service";
11+
import {USE_EXTEND} from "./lib/translate.service";
1112
import {USE_DEFAULT_LANG} from "./lib/translate.service";
1213

1314
export * from "./lib/translate.loader";
@@ -26,6 +27,8 @@ export interface TranslateModuleConfig {
2627
missingTranslationHandler?: Provider;
2728
// isolate the service instance, only works for lazy loaded modules or components with the "providers" property
2829
isolate?: boolean;
30+
// extends translations for a given language instead of ignoring them if present
31+
extend?: boolean;
2932
useDefaultLang?: boolean;
3033
}
3134

@@ -54,6 +57,7 @@ export class TranslateModule {
5457
TranslateStore,
5558
{provide: USE_STORE, useValue: config.isolate},
5659
{provide: USE_DEFAULT_LANG, useValue: config.useDefaultLang},
60+
{provide: USE_EXTEND, useValue: config.extend},
5761
TranslateService
5862
]
5963
};
@@ -72,6 +76,7 @@ export class TranslateModule {
7276
config.missingTranslationHandler || {provide: MissingTranslationHandler, useClass: FakeMissingTranslationHandler},
7377
{provide: USE_STORE, useValue: config.isolate},
7478
{provide: USE_DEFAULT_LANG, useValue: config.useDefaultLang},
79+
{provide: USE_EXTEND, useValue: config.extend},
7580
TranslateService
7681
]
7782
};

projects/ngx-translate/core/tests/translate.store.spec.ts

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ import {TranslateModule, TranslateService} from "../src/public_api";
1212
})
1313
class RootCmp {
1414
constructor(public translate: TranslateService) {
15-
translate.setTranslation('en', {"TEST": "Root"});
15+
translate.setTranslation('en', {
16+
"TEST": "Root",
17+
'ROOT': 'Root'
18+
});
1619
translate.use('en');
1720
}
1821
}
@@ -28,7 +31,10 @@ function getLazyLoadedModule(importedModule: ModuleWithProviders) {
2831
@Component({selector: 'lazy', template: 'lazy-loaded-child'})
2932
class ChildLazyLoadedComponent {
3033
constructor(public translate: TranslateService) {
31-
translate.setTranslation('en', {"TEST": "Lazy"});
34+
translate.setTranslation('en', {
35+
"TEST": "Lazy",
36+
'CHILD': 'Child'
37+
});
3238
translate.use('en');
3339
expect(translate.instant('TEST')).toEqual('Lazy');
3440
}
@@ -197,4 +203,24 @@ describe("module", () => {
197203
sub.unsubscribe();
198204
}))
199205
);
206+
207+
it('should extend translations with extend true', fakeAsync(inject(
208+
[Router, Location, NgModuleFactoryLoader],
209+
(router: Router, location: Location, loader: SpyNgModuleFactoryLoader) => {
210+
let loadedModule = getLazyLoadedModule(TranslateModule.forChild({ extend: true }));
211+
loader.stubbedModules = { expected: loadedModule };
212+
213+
const fixture = createRoot(router, RootCmp);
214+
const translate: TranslateService = TestBed.get(TranslateService);
215+
216+
router.resetConfig([{path: 'lazy', loadChildren: 'expected'}]);
217+
218+
router.navigateByUrl('/lazy/loaded/child');
219+
advance(fixture);
220+
221+
expect(translate.instant('TEST')).toEqual('Lazy');
222+
expect(translate.instant('ROOT')).toEqual('Root');
223+
expect(translate.instant('CHILD')).toEqual('Child');
224+
}))
225+
);
200226
});

0 commit comments

Comments
 (0)