Skip to content

Commit

Permalink
feat(core): add option for lazy loaded modules to extend translations
Browse files Browse the repository at this point in the history
Fixes #425
  • Loading branch information
stemda authored Feb 5, 2020
1 parent 31b77ae commit 6b675d6
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 7 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ export class SharedModule { }
When you lazy load a module, you should use the `forChild` static method to import the `TranslateModule`.

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.

To make a child module extend translations from parent modules use `extend: true`. This will cause the service to also
use translations from its parent module.

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, ...).
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).

Expand Down
12 changes: 7 additions & 5 deletions projects/ngx-translate/core/src/lib/translate.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {isDefined, mergeDeep} from "./util";

export const USE_STORE = new InjectionToken<string>('USE_STORE');
export const USE_DEFAULT_LANG = new InjectionToken<string>('USE_DEFAULT_LANG');
export const USE_EXTEND = new InjectionToken<string>('USE_EXTEND');

export interface TranslationChangeEvent {
translations: any;
Expand Down Expand Up @@ -152,7 +153,8 @@ export class TranslateService {
public parser: TranslateParser,
public missingTranslationHandler: MissingTranslationHandler,
@Inject(USE_DEFAULT_LANG) private useDefaultLang: boolean = true,
@Inject(USE_STORE) private isolate: boolean = false) {
@Inject(USE_STORE) private isolate: boolean = false,
@Inject(USE_EXTEND) private extend: boolean = false) {
}

/**
Expand Down Expand Up @@ -223,8 +225,8 @@ export class TranslateService {
private retrieveTranslations(lang: string): Observable<any> {
let pending: Observable<any>;

// if this language is unavailable, ask for it
if (typeof this.translations[lang] === "undefined") {
// if this language is unavailable or extend is true, ask for it
if (typeof this.translations[lang] === "undefined" || this.extend) {
this._translationRequests[lang] = this._translationRequests[lang] || this.getTranslation(lang);
pending = this._translationRequests[lang];
}
Expand Down Expand Up @@ -252,7 +254,7 @@ export class TranslateService {
this.loadingTranslations
.subscribe({
next: (res: Object) => {
this.translations[lang] = res;
this.translations[lang] = this.extend && this.translations[lang] ? { ...res, ...this.translations[lang] } : res;
this.updateLangs();
this.pending = false;
},
Expand All @@ -270,7 +272,7 @@ export class TranslateService {
*/
public setTranslation(lang: string, translations: Object, shouldMerge: boolean = false): void {
translations = this.compiler.compileTranslations(translations, lang);
if (shouldMerge && this.translations[lang]) {
if ((shouldMerge || this.extend) && this.translations[lang]) {
this.translations[lang] = mergeDeep(this.translations[lang], translations);
} else {
this.translations[lang] = translations;
Expand Down
5 changes: 5 additions & 0 deletions projects/ngx-translate/core/src/public_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {TranslateDirective} from "./lib/translate.directive";
import {TranslatePipe} from "./lib/translate.pipe";
import {TranslateStore} from "./lib/translate.store";
import {USE_STORE} from "./lib/translate.service";
import {USE_EXTEND} from "./lib/translate.service";
import {USE_DEFAULT_LANG} from "./lib/translate.service";

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

Expand Down Expand Up @@ -54,6 +57,7 @@ export class TranslateModule {
TranslateStore,
{provide: USE_STORE, useValue: config.isolate},
{provide: USE_DEFAULT_LANG, useValue: config.useDefaultLang},
{provide: USE_EXTEND, useValue: config.extend},
TranslateService
]
};
Expand All @@ -72,6 +76,7 @@ export class TranslateModule {
config.missingTranslationHandler || {provide: MissingTranslationHandler, useClass: FakeMissingTranslationHandler},
{provide: USE_STORE, useValue: config.isolate},
{provide: USE_DEFAULT_LANG, useValue: config.useDefaultLang},
{provide: USE_EXTEND, useValue: config.extend},
TranslateService
]
};
Expand Down
30 changes: 28 additions & 2 deletions projects/ngx-translate/core/tests/translate.store.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ import {TranslateModule, TranslateService} from "../src/public_api";
})
class RootCmp {
constructor(public translate: TranslateService) {
translate.setTranslation('en', {"TEST": "Root"});
translate.setTranslation('en', {
"TEST": "Root",
'ROOT': 'Root'
});
translate.use('en');
}
}
Expand All @@ -28,7 +31,10 @@ function getLazyLoadedModule(importedModule: ModuleWithProviders) {
@Component({selector: 'lazy', template: 'lazy-loaded-child'})
class ChildLazyLoadedComponent {
constructor(public translate: TranslateService) {
translate.setTranslation('en', {"TEST": "Lazy"});
translate.setTranslation('en', {
"TEST": "Lazy",
'CHILD': 'Child'
});
translate.use('en');
expect(translate.instant('TEST')).toEqual('Lazy');
}
Expand Down Expand Up @@ -197,4 +203,24 @@ describe("module", () => {
sub.unsubscribe();
}))
);

it('should extend translations with extend true', fakeAsync(inject(
[Router, Location, NgModuleFactoryLoader],
(router: Router, location: Location, loader: SpyNgModuleFactoryLoader) => {
let loadedModule = getLazyLoadedModule(TranslateModule.forChild({ extend: true }));
loader.stubbedModules = { expected: loadedModule };

const fixture = createRoot(router, RootCmp);
const translate: TranslateService = TestBed.get(TranslateService);

router.resetConfig([{path: 'lazy', loadChildren: 'expected'}]);

router.navigateByUrl('/lazy/loaded/child');
advance(fixture);

expect(translate.instant('TEST')).toEqual('Lazy');
expect(translate.instant('ROOT')).toEqual('Root');
expect(translate.instant('CHILD')).toEqual('Child');
}))
);
});

0 comments on commit 6b675d6

Please sign in to comment.