Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to load separate translation files per module and per component in angular 5 ? #886

Open
Vishnu0522 opened this issue Jul 7, 2018 · 13 comments

Comments

@Vishnu0522
Copy link

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

@ratidzidziguri
Copy link

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.

@adelloste
Copy link

adelloste commented Jul 19, 2018

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

@BrianCerasuolo
Copy link

@adelloste Using this approach, I run into the issue described here #876

Do you know a way around this issue?

@robertbrower-technologies
Copy link

robertbrower-technologies commented Aug 15, 2018

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

@david-dlc-cerezo
Copy link

david-dlc-cerezo commented Sep 3, 2019

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.

@shane-arthur
Copy link

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.

@david-dlc-cerezo
Copy link

@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
Copy link

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));
 }
}

@prambhan
Copy link

prambhan commented Dec 24, 2019

@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.

@akiik
Copy link

akiik commented Sep 23, 2020

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.

@axell9641
Copy link

@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!!!

@brabenetz
Copy link

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.

@axell9641
Copy link

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.

It didn't work for me either until I added this config inside the feature module's constructor
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests