ngx-translate / core

The internationalization (i18n) library for Angular
MIT License
4.53k stars 580 forks source link

Configure TranslateModule (especially loader) in an APP_INITIALIZER #1051

Open Disane87 opened 5 years ago

Disane87 commented 5 years ago

Hey guys, thank you for this awesome lib! I really love it!

Actually I have a problem which occured with your library. I've set a custom translation loader within my module:

 modules.TranslateModule.forRoot({
            missingTranslationHandler: {
                provide: modules.MissingTranslationHandler,
                useClass: modules.MyMissingTranslationHandler
            },
            useDefaultLang: false,
            loader: {
                provide: modules.TranslateLoader,
                useClass: modules.TranslationApiLoader,
                deps: [DataService]
            }
        }),

The TranslationApiLoader performans an encapsulated api call to our api and fetches all translation data for the chosen language:

export class TranslationApiLoader implements TranslateLoader {
    private translation = new Subject<any>();

    constructor(private dataService: DataService) { }

    getTranslation(lang: string): Observable<any> {
        const resources: Resources = new Resources([
            { resource: Resource.translation, resourceId: lang }
        ]);
        const endpoint = resources.getEndpoint();

        this.dataService.get<VemasResponse<any>>(resources, true).then(response => {
            if (response) {
                localStorage.setItem(endpoint, JSON.stringify(response.data));
                this.translation.next(response.data);
            } else {
                this.translation.next(this.getLocalTranslation(endpoint));
            }
        }, error => {
            this.translation.next(this.getLocalTranslation(endpoint));
        });

        return this.translation.asObservable();
    }

    getLocalTranslation(endpoint: string): any {
        let localTranslation;

        /** Wenn das hier knallt, dann gibt es vermutlich ein Problem beim Lesen */
        try {
            localTranslation = JSON.parse(localStorage.getItem(endpoint));
        } catch (error) {
            localStorage.removeItem(endpoint);
        }

        if (localTranslation) {
            return localTranslation;
        } else {
            return [];
        }
    }
}

In the background we have an interceptor, which provides the api url, auth etc, so that a developer doesn't have to care about this.

After some refactoring of our application, we discovered recently, that our translation is not loaded, because our interceptor has no appsettings (which are resolved via APP_INITIALIZER) in the module providers:

   {
            provide: APP_INITIALIZER,
            useFactory: (appSettingsService: AppSettingsService) => () => {
                return appSettingsService.Load();
            }, // Bitte dokumentieren, wie diese Syntax zu verstehen ist (CH)
            deps: [AppSettingsService],
            multi: true
        },

Is there any way to configure the translate module and the loader within that APP_INITIALIZER? I've already tried it, but I had no lock with it. The injected DataService is always null:

ERROR Error: Uncaught (in promise): TypeError: this.dataService.get(...).then is not a function

 {
            provide: APP_INITIALIZER,
            useFactory: (dataService: DataService) => () => {
                modules.TranslateModule.forRoot({
                    missingTranslationHandler: {
                        provide: modules.MissingTranslationHandler,
                        useClass: modules.MyMissingTranslationHandler
                    },
                    useDefaultLang: false,
                    loader: {
                        provide: modules.TranslateLoader,
                        useClass: modules.TranslationApiLoader,
                        deps: [dataService]
                    }
                });

                debugger;
                return timer(1000).toPromise();
            }, // Bitte dokumentieren, wie diese Syntax zu verstehen ist (CH)
            deps: [DataService],
            multi: true
        },
19jake68 commented 5 years ago

Have you managed to resolve this? I'm facing similar issue

Disane87 commented 5 years ago

@19jake68 no, sorry!

baskaranr commented 5 years ago

@Disane87 @19jake68 In app.component.ts, could you try below code, it works for me ` ngOnInit() {

    this.loadTrans();
}

loadTrans() {
    setTimeout(() => {
     this.getTranslationData()
      .subscribe(
        response => {
          this.translateService.setTranslation('en', response, true);
        },
        err => {
          console.log(err);
        }
      );
    }, 100);
}

getTranslationData() {
    const url = 'api to get translate data'; //Hope api base path available in app.components.ts
    return this.http.get(url);
}`
alexmgrant commented 5 years ago

For anyone still looking at this, I found a way to load translations after App Init. The ngx-translate lib allows you to override the current loader. So we can pass a new factory after the app has completed bootstrapping.

export function HttpLoaderFactory(handler: HttpBackend, valueAvailableAfterInit) {
  const http = new HttpClient(handler);
  return new TranslateHttpLoader(http, valueAvailableAfterInit, '.json');
}

export class AppModule {
  constructor(
    private translate: TranslateService,
    private handler: HttpBackend
  ) {}

  ngDoBootstrap() {
    const valueAccessableAfterBootstrap = `I'll leave this to your use-case. For me it is an environment variable overwritten via ngOnInit in app.component`;

    this.translate.currentLoader = HttpLoaderFactory(this.handler, valueAccessableAfterBootstrap); // replace loader
    this.translate.reloadLang('en-US').pipe(take(1)).subscribe(); // reload translations with new loader
  }
}

https://stackoverflow.com/a/57584510/2547607