core
core copied to clipboard
How to load separate translation files per module and per component in angular 5 ?
Expected/desired behavior I am new in angular , Our application is large and we don't want to load whole translation in single call . We want to load translation in per module as well as per component wise.
My environment is : Angular CLI: 6.0.8 Node: 8.10.0 typescript 2.7.2
if you search around there are some solutions but those do not work, I am also having this problem and so far i can not find any way around it.
Hi @Vishnu0522 as you see in the official doc of ngx-translate, there is this paragraph...if you are using lazy loading and you have a different modules, you can configure different services by using isolate: true. With this approach, the service is a completely isolated instance (for translations, current lang, events, ...).
@adelloste Using this approach, I run into the issue described here https://github.com/ngx-translate/core/issues/876
Do you know a way around this issue?
How to load translations per module is described in the documentation. If you want to load on a component by component basis you can try something like this:
import { Component, OnInit } from '@angular/core'; import { HttpClient } from '@angular/common/http';
import { TranslateService } from '@ngx-translate/core';
export const EN_TRANSLATIONS = { 'found': 'found', 'not found': 'not found' };
@Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit {
translationsUrl = 'assets/i18n';
constructor(private translate: TranslateService, private http: HttpClient) { this.translate.setDefaultLang('en'); this.translate.use('de'); this.translate.setTranslation('en', EN_TRANSLATIONS); }
ngOnInit() { this.loadTranslations('de'); }
loadTranslations(locale: string) {
return this.http.get(${this.translationsUrl}/${this.constructor.name}-${locale}.json).subscribe((data: any) => {
this.translate.setTranslation(locale, data);
});
}
}
Tested w/ Angular 6
Here is well explained. https://medium.com/@TuiZ/how-to-split-your-i18n-file-per-lazy-loaded-module-with-ngx-translate-3caef57a738f
However, I didn't get it working in my project. Seems that even with the flag isolate: true i'm still getting the same TranslateService with root translations.
Experiencing the same as @david-dlc-cerezo, the xhr call is only made for the file specified in the forRoot factory, the lazy-loaded child modules factories are called, but XHR request does not fire.
As of, translation service only has the root translations.
@shane-arthur I finally kind of get it working but I'm not sure how/why 🤣
One thing I did it was to add TranslateService to the providers array on the module where I use Translate.forChild
@NgModule({
imports: [
...,
TranslateModule.forChild({
loader: {
provide: TranslateLoader,
useFactory: MyImporterFactory
},
isolate: true
}),
...
],
providers: [
...,
TranslateService
]
})
export class MyModule {}
But I can ensure this was what finally isolated the TranslateService in my Module.
Angular documentation about providers says:
When the router creates a component within the lazy-loaded context, Angular prefers service instances created from these providers to the service instances of the application root injector.
The original @Vishnu0522 question asked also about loading a different set of translations for each module... Well if for a module you should provide a TranslateLoader with your customized public getTranslation(lang: string): Observable<any> method, my guess is that in a component you should do the same.
I just tested a PoC about that... and worked! 🎉
To have a different TranslateService with its TranslateLoader on each Component:
class MyTranslateLoader implements TranslateLoader {
constructor() {}
public getTranslation(lang: string): Observable<any> {
const translations = ...// Obtain your translations as you wish
return of(translations);
}
}
// AoT requires an exported function for factories
export function MyTranslateLoaderFactory() {
return new MyTranslateLoader();
}
@Component({
selector: 'my-component',
templateUrl: './my-component.component.html',
styleUrls: ['./my-component.component.scss'],
providers: [
TranslateService,
{
provide: TranslateLoader,
useFactory: MyTranslateLoaderFactory
}
]
})
export class SchoolEditionComponent implements OnInit {
constructor(
private readonly translate: TranslateService,
) {}
ngOnInit() {
// This will show the loaded translations
this.translate.getTranslation('en').subscribe(translations => console.log(translations));
}
}
@shane-arthur I finally kind of get it working but I'm not sure how/why
One thing I did it was to add
TranslateServiceto the providers array on the module where I useTranslate.forChild@NgModule({ imports: [ ..., TranslateModule.forChild({ loader: { provide: TranslateLoader, useFactory: MyImporterFactory }, isolate: true }), ... ], providers: [ ..., TranslateService ] }) export class MyModule {}But I can ensure this was what finally isolated the
TranslateServicein my Module.Angular documentation about providers says:
When the router creates a component within the lazy-loaded context, Angular prefers service instances created from these providers to the service instances of the application root injector.
The original @Vishnu0522 question asked also about loading a different set of translations for each module... Well if for a module you should provide a
TranslateLoaderwith your customizedpublic getTranslation(lang: string): Observable<any>method, my guess is that in a component you should do the same.
@david-dlc-cerezo the solution from Medium was working absolutely fine earlier but now it doesn't work. I have Translate service provided in lazy loaded module as well. but unable to split the translate file per module.Isolate: true does not work at all. This is in angular 6 as well as 7. It was working perfectly fine not sure what affected it and where.
I was struggling with the same issue and got it working when using isolate: true and setting a default language(and current if needed) again.
For app module:
export class AppTranslateLoader implements TranslateLoader {
getTranslation(lang: string): Observable<any> {
return from(import(`../assets/i18n/${lang}.json`));
}
}
TranslateModule.forRoot({
defaultLanguage: 'ET',
loader: {
provide: TranslateLoader,
useClass: AppTranslateLoader,
}
}),
For lazy module:
export class LazyTranslateLoader implements TranslateLoader {
getTranslation(lang: string): Observable<any> {
return from(import(`../../assets/i18n/lazyModule/${lang}.json`));
}
}
TranslateModule.forChild({
defaultLanguage: 'ET',
isolate: true,
loader: {provide: TranslateLoader, useClass: LazyTranslateLoader}
}),
For setting a current langauge I'm using this in a module container component:
constructor(translate: TranslateService, store: Store<fromCore.State>) {
store.pipe(select(UserSelectors.selectUserLanguage)).subscribe(
(lang) => translate.use(lang)
);
}
I'm not using http loader due to browser cache issues.
@akiik I didn't know what you meant by "a module container component" so I initialize the TranslateService inside the constructor of my lazy-loaded module and it worked!!!! I can't thank you enough!!!
In case you use: this.translate.setTranslation(locale, data);
There is a third parameter "shouldMerge: boolean".
For me that was the central point which solved my Problems:
I always call this.translate.setTranslation(locale, data, true);.
This prevents that a component replaces all keys from the SPA. and visa versa.
ps.: isolation=true didn't worked for me. Maybe because of lazy-loading sub-modules which contains the components.
In case you use:
this.translate.setTranslation(locale, data);There is a third parameter "shouldMerge: boolean". For me that was the central point which solved my Problems: I always callthis.translate.setTranslation(locale, data, true);. This prevents that a component replaces all keys from the SPA. and visa versa.ps.: isolation=true didn't worked for me. Maybe because of lazy-loading sub-modules which contains the components.
It didn't work for me either until I added this config inside the feature module's constructor
