Core: How to load separate translation files per module and per component in angular 5 ?

Created on 7 Jul 2018  路  10Comments  路  Source: ngx-translate/core

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

Most helpful comment

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.

All 10 comments

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

```typescript
@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:

```typescript
class MyTranslateLoader implements TranslateLoader {
constructor() {}

public getTranslation(lang: string): Observable {
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 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.

@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,
      }
}),



md5-a982be11ced3ba76f76443f714ae068e



export class LazyTranslateLoader implements TranslateLoader {
  getTranslation(lang: string): Observable<any> {
    return from(import(`../../assets/i18n/lazyModule/${lang}.json`));
  }
}



md5-4ed49727091940cd98e4905162042575



TranslateModule.forChild({
      defaultLanguage: 'ET',
      isolate: true,
      loader: {provide: TranslateLoader, useClass: LazyTranslateLoader}
    }),



md5-d391db72f04a0b3eb9838af8340d7052



  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.

Was this page helpful?
0 / 5 - 0 ratings