Core: It can't work normally within lazyload(loadChildren) NgModules

Created on 8 Sep 2016  路  26Comments  路  Source: ngx-translate/core

I'm submitting a ... (check one with "x")

[ ] bug report => check the FAQ and search github for a similar issue or PR before submitting
[x] support request => check the FAQ and search github for a similar issue before submitting
[ ] feature request

Current behavior
when I use lazyload(loadChildren) route, it's not easy to subscribe TranslateService change between each NgModules. I create a global translate service to subscribe the change but it fetch '*.json' when each NgModule loaded.

Expected/desired behavior
It can auto change the language within NgModules

Please tell us about your environment:

  • ng2-translate version: 2.4.3
  • Angular version: 2.0.0-rc.6
  • Browser: [all]
  • Language: [TypeScript 2.0-rc]

Most helpful comment

Ok, static instances it is then, thanks for the info

I'm not sure about that. I think the correct behaviour was already proposed here https://github.com/ocombe/ng2-translate/issues/209
We just have to configure TranslationModule with forRoot call in AppModule and then import and re-export it in SharedModule. This solution seems to work for me, strings are translated in both main and lazy modules and resource bundles are not loaded more than once

All 26 comments

Did you define import the TranslateModule in your root app module ?

@ocombe, no, I only import the TranslateModule in the share module.

I have 4 modules, others are article, user, and root app. How could I do?

if you import it in the shared module, you should add it to the exports of the shared module as well

oh you're calling the service from within the module class, and you're calling "use" again there, you shouldn't have to do that

@ocombe, I forgot to tell you that I call 'use' in the root app https://github.com/doxiaodong/darlin-angular2/blob/develop/src/app/footer/footer.component.ts#L45, and if I do not call in a lazyload module(like article), it will look like as follow. image

You should define all of that in the app root component: https://github.com/doxiaodong/darlin-angular2/blob/develop/src/app/app.component.ts
And that's all, not in any of the other sub modules

https://github.com/ocombe/ng2-translate/issues/217 is probably related to this. I have a similar setup and it's not working in lazy-loaded sub modules.

Also getting same behaviour/error.

Using https://github.com/AngularClass/angular2-webpack-starter and following your sharedModule setup.

Works fine for regular components but not for lazy loaded ones.

I had the same problem.
using a module for setting "translate.use(LANG)" and import into every lazyload module fixed it for me.

Ok I just checked the problem, this is what I did:

  • import TranslateModule into my SharedModule
  • prepare the translation in my SharedModule:
@NgModule({
    imports: [
        ...MODULES,
        TranslateModule.forRoot({
            provide: TranslateLoader,
            useFactory: (http: Http) => new TranslateLocalStorageLoader(http, 'assets/i18n', '.json?t=' + (new Date().getTime())),
            deps: [Http]
        })
    ],
    declarations: [
        ...COMPONENTS,
        ...DIRECTIVES,
        ...PIPES
    ],
    providers: [
        ...PROVIDED_SERVICES
    ],
    exports: [
        ...COMPONENTS,
        ...DIRECTIVES,
        ...PIPES,

        // modules
        ...MODULES,
        TranslateModule
    ]
})
export class SharedModule {
    constructor(translate: TranslateService) {
        // get the current UserLang
        userLang = translate.getBrowserLang();

        // this language will be used as a fallback when a translation isn't found in the current language
        translate.setDefaultLang('en');

        // the lang to use, if the lang isn't available, it will use the current loader to get them
        translate.use(userLang);
    }
}

And voil脿 ! It works in all my lazy loaded modules, as long as they import the SharedModule

Well, first of all, thanks for having a look at this, but I noticed this does not completely fix the problem.

What it does fix is that no strings will be translated at all. What it doesn't fix is changing the language (using translate.use(...)) by calling that method in a non-lazy-loaded module won't translate / update any strings in the lazy-loaded module.

EDIT:
it works, if you only have one TranslateService used by making the translate property in your SharedModule static. But that is no good option (considering unit testing for example, and it leads to tight coupling).

export class SharedModule {
    public static translate;
    constructor(translate: TranslateService) {
        SharedModule.translate = translate;
        // ...code from above
    }
}
export class NavbarComponent {
    private switchLang(lang: string): void {
        // this.translate.use(lang);
        SharedModule.translate.use(lang);
    }
}

Could you please have a look at this again? Thank you.

It fetch '*.json' when each NgModule loaded

ok, I still have a trick in my sleeve if I cannot find a way to fix this, I can make the translations and currentLang static so that you can have multiple instances but they keep the same "state", but it means that it won't be possible to have different instances of the service in different states (I don't think it's a good idea, but there may be some use cases).

I will try to fix it this week end, but I don't know if I'll have the time (because I need to prepare myself for angular connect next week).

Singleton services provided in shared modules are still cloned by all lazy modules which is an expected behaviour, look here https://angular.io/docs/ts/latest/guide/ngmodule.html#!#shared-module

Do not specify app-wide singleton providers in a shared module. A lazy loaded module that imports that shared module will make its own copy of the service

Ok, static instances it is then, thanks for the info

Ok, static instances it is then, thanks for the info

I'm not sure about that. I think the correct behaviour was already proposed here https://github.com/ocombe/ng2-translate/issues/209
We just have to configure TranslationModule with forRoot call in AppModule and then import and re-export it in SharedModule. This solution seems to work for me, strings are translated in both main and lazy modules and resource bundles are not loaded more than once

@chpasha Yes, that is the correct way to do this.

Never ever call .forRoot() of a module in a shared module. It is called forRoot for a reason.

Yep, SamVerschueren's proposed solution works perfectly for me.

@SamVerschueren nice, and should I close this issue? @ocombe

Yes :)
If someone wants to update the docs to explain this better that'd be nice

Since I didn't found any plunker working with ngx-translate librairy and I also had some difficulties to manage to make it work with LoadChildren,
I've setup a way that work pretty well for me:

_I've created two SharedModules, (one for lazyLoading and one for the other part of my application)_

SharedLazyModule for lazy loading content:

@NgModule({
  imports: [
    HttpModule,
    CommonModule,
    TranslateModule.**forChild**({}),
  ],
  exports: [
    CommonModule,
    TranslateModule
  ]
})
export class SharedLazyModule {}

SahredModule for App

// AoT requires an exported function for factories
export function HttpLoaderFactory(http: Http) {
   return new TranslateHttpLoader(http, './assets/i18n/', '.json');
}

@NgModule({
    imports: [
      HttpModule,
      CommonModule,
      TranslateModule.**forRoot**({
           provide: TranslateLoader,
           useFactory: HttpLoaderFactory,
           deps: [Http],
         })
    ],
    exports: [
      CommonModule,
      TranslateModule
    ]
})
export class SharedModule {

     constructor(private translate: TranslateService) {

        translate.addLangs(["en", "fr"]);
        translate.setDefaultLang('en');

        let browserLang = translate.getBrowserLang();
        translate.use(browserLang.match(/en|fr/) ? browserLang : 'en');
    }
}

See Plunker:
https://plnkr.co/LVmIuI1Xw9vFn0IuC2jW

Hi @neolanders What about putting the second one into a Core module along with all the singletons, per the style guide. Or is this a different animal? I'm trying to get this working in this app that contains many lazy loaded features. Currently only the dashboard needs i18n support.

Hi @dancancro, you are totaly right. The SharedModule could be the appModule or any other CoreModule.
The only consern should be to use the "forRoot" for your modules imports and in your LazyShared Modules should use the "forChild" as per the style guide.

@neolanders Thanks. Can you take a look at my project and see what I might be doing wrong? It is based on JHipster and so the language services are provided by way of JhiLanguageService in the ng-jhipster library which uses a JhiConfigService to configure ngx-translate without my needing to import and configure the TranslateModule in my app.module. So when I add TranslateModule.forRoot(...) to imports of AppModule, everything breaks and I just see "translation not found..." messages everywhere.

I don't know how to make my lazy-loaded pages use the same translation instances as the eager-loaded part of the app so that changing the language in an eager component of the nav bar affects the language used by the lazy-loaded pages.

I have also tried this idea but I think it suffers from the same problem that I don't know how to control ngx-translate configuration in a JHipster app. This is probably more a JHipster question but maybe you have some ideas. I'm asking in different places. Such is life when using open source software.

Was this page helpful?
0 / 5 - 0 ratings