-
-
Notifications
You must be signed in to change notification settings - Fork 588
Lazy loaded module is not adding the new custom translateLoader #444
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Comments
Might be the same issue that I have: #425? |
Yes is similar, I try your solution but it didn't work for me |
Just bumped into this as well. I wanted my app module to fetch translations from "/assets/i18n/{lang}.json" and my lazy loaded modules to fetch from "/assets/i18n/{feature}/{lang}.json" but was unable to get this to work. The custom loader configured by forChild(...) was ignored. |
what @k-schneider describes is exactly the same that i reported |
the problem is that you must reconfigure the service in all lazy loaded modules but if you didn't set but if you set |
@atiertant i can't set isolate to true because i'll need the parent translations, i was expecting to be able to add more translations based on my current module (lazy) |
will be nice to take advantage of lazy loading even with the translations, so we can add the needed translations for the module without lose the main (root) translations. actually i'm trying with this approach
but the instruction for set the new translations is being omited |
@jlberrocal think in isolate mode when store has no translation for the key, it should ask his parent using injector.parent recursivly |
@atiertant no exactly, because what about keeping the same instance? what the forChild method needs to do is only insert the new translations keeping the same instance of the TranslateService |
@jlberrocal have a look at #379 @ocombe wouldn't like to change all spec and wanted to have one instance of service by lazy loaded modules to avoid this changes... this is a good idea but not working as expected for now... the problem is service configuration is saved in his instance, so new service instance need reconfigure. the second problem is that store doesn't identify service instance so when the root loader already loaded a lang, new loader isn't called... |
I am facing the same problem where the lazy loaded forChild loader never being called (unless I set isolate to true). Is there a way around it? I tried to call thanks! |
Would be really ace if it would no longer be required to set isolate in order to get lazy loading without having to provide 'common' translations in every lazy loaded module. I don't want to use isolate because I really want only 1 instance of TranslateService (hence using the forChildren). |
I also have the same problem as @sebelga . The translate.use('xx'); does not load the new translations. Any thoughts? |
@avilao Did you set |
I never managed to do it. So I created my own service to add translations to the root translateModule. @Injectable()
export class LocalizationService {
private modulesTranslation: any = {};
constructor(private http: Http, private translate: TranslateService) { }
loadTranslationModule(module: string): Observable<any> {
const lang = this.translate.currentLang || 'en';
if (this.modulesTranslation[module] && this.modulesTranslation[module][lang]) {
return Observable.of(this.modulesTranslation[module][lang]);
}
const uri = `assets/${module}/i18n/${lang}.json`;
return this.http.get(uri)
.map(res => res.json())
.do((i18n: any) => {
if (!this.modulesTranslation[module]) {
this.modulesTranslation[module] = {};
}
this.modulesTranslation[module][lang] = i18n;
this.translate.setTranslation(lang, i18n, true); // add translation to global translations
console.log(`[Localization] lang (${lang}) loaded for module (${module})...`);
})
.catch((err: any) => {
console.log(`[Localization] lang (${lang}) not found for module (${module})`);
return Observable.of({});
});
}
} |
@sebelga never do the action of a subscription in a |
This is a resolver. It will automatically subscribe. In resolver you don't return 'Subscription'. It's true that it could have been a "map" instead but I don't think it matters. |
I worked around this issue with a custom MissingTranslationHandler as the correct currentLoader is set on the lazily loaded module translate service. In my lazy loaded module export function HttpLoaderFactory(http: Http) {
// define custom resolution path for translation
return new TranslateHttpLoader(http, './assets/i18n/association/', '.json');
}
...
TranslateModule.forChild({
loader: {
provide: TranslateLoader,
useFactory: (HttpLoaderFactory),
deps: [ Http ]
},
missingTranslationHandler: {
provide: MissingTranslationHandler,
useClass: CustomMissingTranslationHandler
}
}) and the common missing translation handler: export class CustomMissingTranslationHandler implements MissingTranslationHandler {
handle(params: MissingTranslationHandlerParams) {
return params.translateService.currentLoader.getTranslation(params.translateService.currentLang)
.map(r => {
const trad = r[params.key];
if (trad) {
return trad;
} else {
const prefix = (<any>params.translateService.currentLoader).prefix;
console.warn(`translation not found for key ${prefix} ${params.key} in ${params.translateService.currentLang}`);
return `**${params.key}**`;
}
});
}
}```
and the global loader for common translations
```typscript
export function authServiceBuilder(backend: XHRBackend, options: RequestOptions, authService: AuthenticationService) {
return new HttpService(backend, options, authService);
}
...
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [ Http ]
},
missingTranslationHandler: {
provide: MissingTranslationHandler,
useClass: CustomMissingTranslationHandler
}
}) |
I wrote a article about how to have 1 json file per lazy loaded module without having to write a new Custom Loader etc... it's quiet simple, only the documentation is not clear in fact: |
Having same issue with lazy loaded translations. What i was looking for was: load shared translations in root module, add additional translations for each lazy loaded module. Don't make request twice when translations for given module are already loaded (eg. when you navigate from one lazy module to another and back). Actually the same as @sebelga posted. |
@jlberrocal it works, but it is just a part of what we need. We don't want to make http request each time we navigate between lazy loaded modules. I currently added some additional module which prevents requesting translations for same module twice. |
@sebelga your sollution is actually really good but how would you handle language switching than? i did something like that with resolver but language switching never works for me :( |
I've implemented a solution for this issue which seems to work. Being relatively a newbie in Angular, I would appreciate your comments. The solution loads the translation for lazy loaded modules (once they are loaded) and merges that into a single translation service. It supports switching languages before and after lazy loaded modules are loaded. I'm using a service (LazyAPIService) which is used to collect data from every loaded module. Each module calls the Add function to add itself in the module constructor, with a unique name and the translation asset path to the list (modules might be loaded more then once when used as child modules of lazy loaded modules, so we do not add a module if the name already exists) MultiTranslateHttpLoader is used as the translation loader. It loads the translation for each module added to the LazyAPIService. It uses the deepmerge "all" function to merge the loaded JSON translations into a single JSON object. Since the loader holds the injected singleton LazyAPIService, loading other languages (which will call getTranslation) will load the language from all the added modules so far (including the lazy loaded ones). The MultiTranslateHttpLoader marks a flag once it finishes it's initial load on the app.module startup. This means that any module added afterwards is a lazy loaded module. Since LazyAPIService is constructed before the TranslationService, I use the Injector service to manually retrieve the TranslateService when needed in the AddTranslation. Don't forget to add the assests in angular.json: // LazyModuleData.ts *******************************************
export interface LazyModuleData {
name: string;
translationPath?: string;
}
// lazy-api.service.ts *******************************************
import { Injectable, Injector } from '@angular/core';
import { Observable, Subject } from 'rxjs';
import { LazyModuleData } from '../interfaces/LazyModuleData';
import { TranslateService } from '@ngx-translate/core';
import { HttpClient} from '@angular/common/http';
@Injectable({
providedIn: 'root'
})
export class LazyAPIService {
private _moduleLoadedNotify = new Subject<LazyModuleData>();
get moduleLoadedNotify(): Observable<LazyModuleData> {
return this._moduleLoadedNotify.asObservable();
}
private _modules: Array<LazyModuleData> = [];
private _translationLoaded = false;
get translationLoaded() {
return this._translationLoaded;
}
set translationLoaded(value: boolean) {
this._translationLoaded = value;
}
get modules() {
return this._modules;
}
constructor(
private injector: Injector,
private http: HttpClient
) {
}
public Add(module: LazyModuleData) {
// do not add if already added.
for (var m of this._modules) {
if (m.name === module.name)
return;
}
this._modules.push(module);
if (this._translationLoaded)
this.AddTranslation(module);
this.Notify(module);
}
public Notify(module: LazyModuleData) {
this._moduleLoadedNotify.next(module);
}
public AddTranslation(module: LazyModuleData) {
let translate = this.injector.get(TranslateService);
for (let l of translate.langs) {
const path = module.translationPath + l + ".json";
console.log("getting translation from ", path);
this.http.get(path).subscribe(
res => {
console.log("got translation!");
console.log(res);
translate.setTranslation(l, res, true);
},
error => {
console.log("error getting translation");
console.log(error);
}
);
};
}
}
// multi-translate-http-loader.ts *******************************************
import { HttpClient } from "@angular/common/http";
import { TranslateLoader } from "@ngx-translate/core";
import { Observable, forkJoin, of } from "rxjs";
import { catchError, map } from "rxjs/operators";
import { all } from "deepmerge";
import { LazyAPIService } from './services/lazy-api.service';
export class MultiTranslateHttpLoader implements TranslateLoader {
constructor(
private http: HttpClient,
private lazyAPI: LazyAPIService
) { }
public getTranslation(lang: string): Observable<any> {
// this makes sure that from now on, we load and merge translation for addition (lazy loaded) modules.
this.lazyAPI.translationLoaded = true;
// load translation of all added modules.
const requests = this.lazyAPI.modules.map(module => {
const path = module.translationPath + lang + ".json";
return this.http.get(path).pipe(catchError(res => {
console.error("Could not find translation file:", path);
return of({});
}));
});
return forkJoin(requests).pipe(map(response => all(response)));
}
}
// app.module.ts *******************************************
// in @NgModule imports:
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient, LazyAPIService]
}
// http loader factory function
export function HttpLoaderFactory(
http: HttpClient,
lazyAPI: LazyAPIService
) {
lazyAPI.Add({ name: "app", translationPath: "./assets/translate/" });
return new MultiTranslateHttpLoader(http, lazyAPI);;
}
// lazy loaded (or any other) module contructor *****************************
constructor(private lazyAPI: LazyAPIService) {
lazyAPI.Add({ name: "admin", translationPath: "./assets/translate/admin/" });
} |
@ardent42c your answer solved my problem of loading the translation files from lazy loaded modules, but now I'm facing an issue: public AddTranslation(module: LazyModuleData) {
.... code ....
this.http.get(path).subscribe(
res => {
console.log("got translation!");
console.log(res);
translate.setTranslation(l, res, true);
// new added line to fix the issue:
translate.use(l);
},
error => {
console.log("error getting translation");
console.log(error);
}
);
};
} |
P.S:
I don't know if it's the reason. |
I'm submitting a ... (check one with "x")
Current behavior
When a lazy loaded module is being loaded it triggers the factory for create the new TranslateHttpLoader however it is not adding the translations in the file
Expected/desired behavior
Load the translations of the lazy loaded module
Reproduction of the problem
I can't provide a plnkr because but this is the code that i'm using
Please tell us about your environment:
Kubuntu 16.04
ngx-translate version: core=6.0.0 loader: 0.0.3
Angular version: 2.4.7
Browser: all
The text was updated successfully, but these errors were encountered: