core icon indicating copy to clipboard operation
core copied to clipboard

translate.use(langCode) is not working in some cases

Open j-a-h-i-r opened this issue 3 years ago • 11 comments

Current behavior

In app.component.ts I have set up the TranslateService as follows:

translate.addLangs(['en', 'bn']);
translate.use('en');

translate.onLangChange.subscribe((change) => {
      console.log('langChange', change);
})

In another module, I'm doing this to change the current language,

this.translate.use(langCode);

When I change the language to bn, it does not work.

BUT, if I write translate.use('bn'); in app.component.ts, language change starts working.

What I've noticed is that, when I set en as the language in app.component.ts the onLangChange prints an empty object as the translations property.

// in onLangChange callback 
Object { lang: "bn", translations: {} }

Expected behavior

Changing language should work regardless of what language was set during initialization

How do you think that we should fix this?

Minimal reproduction of the problem with instructions

Environment


ngx-translate version: 13.0.0
Angular version: 10.1.5


Browser:
- [ ] Chrome (desktop) version XX
- [ ] Chrome (Android) version XX
- [ ] Chrome (iOS) version XX
- [ x] Firefox version 81
- [ ] Safari (desktop) version XX
- [ ] Safari (iOS) version XX
- [ ] IE version XX
- [ ] Edge version XX
 
For Tooling issues:
- Node version: 12.16   
- Platform:   Windows

Others:

j-a-h-i-r avatar Oct 08 '20 19:10 j-a-h-i-r

This workaround works for me (angular 11 et ionic 5) :

I force to load all possible language in app.components :

this.translate.addLangs(['fr', 'en', 'es', 'de'])

this.translate.use('en') this.translate.use('es') this.translate.use('de') this.translate.use('fr')

then in other component, i can call again this.translate.use('xxx')

This issue was not present in previous version for angular 8

sguilly avatar Jan 06 '21 07:01 sguilly

Same problem for me, the workaround of @sguilly worked. But this wasn't a problem previously.

nschipperbrainsmith avatar Jan 20 '21 12:01 nschipperbrainsmith

Hi guys! I also noticed this "issue". After a few attempts, I got it by using same loader config in forChild as in forRoot:

app.module.ts (can be core.module or whatever):

export const HttpLoaderFactory = (httpClient: HttpClient) => {
  return new ...
};

@NgModule({
  declarations: [AppComponent],
  entryComponents: [],
  imports: [
    ...,
    TranslateModule.forRoot({
      loader: {
        provide: TranslateLoader,
        useFactory: HttpLoaderFactory,
        deps: [HttpClient],
      },
    }),
  ],
  providers: [
    { provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
  ],
  bootstrap: [AppComponent],
})
export class AppModule {}

app.component.ts:

export class AppComponent {
  constructor(private readonly translateService: TranslateService) {
    this.setDefaultLang();
  }

  private setDefaultLang() {
    this.translateService.addLangs(['en', 'fr', 'es-ES', 'es-419']); // array of available langs
    this.translateService.setDefaultLang('es-ES');
  }
}

lang-test-module.ts (the module from where we want to change lang):

import { HttpClient } from '@angular/common/http';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { HttpLoaderFactory } from 'src/app/app.module.ts';
...
@NgModule({
  imports: [
    ...,
    TranslateModule.forChild({
      loader: {
        provide: TranslateLoader,
        useFactory: HttpLoaderFactory,
        deps: [HttpClient],
      },
    }),
  ],
  declarations: [LangTestPage],
})
export class LangTestModule {}

I don't know if it is a good solution...

joseph-navant avatar Mar 16 '21 13:03 joseph-navant

@joseph-navant : your tip don't work in my code

sguilly avatar Mar 18 '21 09:03 sguilly

@joseph-navant : your tip don't work in my code

Can I help you?

  • I have the TranslateModule.forRoot() (with loader) in my core.module (core.module is imported in app.module)
  • My HttpLoaderFactory is using MultiTranslateHttpLoader (https://github.com/denniske/ngx-translate-multi-http-loader)
  • I added available langs and default lang in app.component-ts (as u can see in the code above)
  • I have the TranslateModule.forChild() (with loader) in the module from I want to change lang (this module is lazy loaded)
  • The HttpLoaderFactory i'm using in this module is imported from my core.module (can be app.module or whatever)
  • In LangTestPage I get available langs and render it in radio-buttons
  • When I click in one of them, it calls use() from translateService and voilá

lang-test.page.ts

import { Component, OnInit } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';

@Component({
  selector: 'app-lang-test',
  templateUrl: './lang-test.page.html',
  styleUrls: ['./lang-test.page.scss'],
})
export class LangTestPage implements OnInit {
  langs: string[];
  selectedLang: string;

  constructor(private readonly translateService: TranslateService) {}

  ngOnInit() {
    this.langs = this.translateService.getLangs();
    this.selectedLang =
      this.translateService.currentLang || this.translateService.defaultLang;
  }

  onLanguageChange() {
    this.translateService.use(this.selectedLang);
  }
}

lang-test.page.html (using ionic framework)

<ion-radio-group [(ngModel)]="selectedLang" (ionChange)="onLanguageChange()">
  <ng-container *ngFor="let lang of langs">
    <ion-item mode="md">
      <ion-radio [value]="lang"></ion-radio>
      <span>{{ 'SETTINGS.LANGUAGE.' + lang.toUpperCase() | translate }}</span>
    </ion-item>
  </ng-container>
</ion-radio-group>

When this code is rendered, every ion-radio value has the lang code I defined in app.component.ts. Something like this:

...
<ion-radio value="en"></ion-radio>
...
<ion-radio value="fr"></ion-radio>
...
<ion-radio value="es-ES"></ion-radio>
...
<ion-radio value="es-419"></ion-radio>
...

So, after all, the use from translateService is receiving the lang code. For example: this.translateService.use('en')

joseph-navant avatar Mar 18 '21 10:03 joseph-navant

Thanks @joseph-navant, your solution worked for me.

bkondakor avatar Apr 22 '21 11:04 bkondakor

This workaround works for me (angular 11 et ionic 5) :

I force to load all possible language in app.components :

this.translate.addLangs(['fr', 'en', 'es', 'de'])

this.translate.use('en') this.translate.use('es') this.translate.use('de') this.translate.use('fr')

then in other component, i can call again this.translate.use('xxx')

This issue was not present in previous version for angular 8

This solution worked for me, thanks @sguilly , although I think that this solution is very dirty

aristotekean avatar Dec 29 '21 02:12 aristotekean

If you use TranslateModule.forChild(), you get the default FakeTranslateLoader per here: https://github.com/ngx-translate/core/blob/ce3c70c76577550718417912516136da51922c42/projects/ngx-translate/core/src/public_api.ts#L72)

So for me, I changed my non-root modules to just be TranslateModule and the languages started to load as expected (same behavior as before).

@NgModule({
  imports: [
     ...
     TranslateModule,
     ...
  ]
}

jrasm91 avatar Feb 18 '22 21:02 jrasm91

I just came across this issue myself.

In my case, my language is set elsewhere by some other javascript so doing translateService.use inside the app module constructor is not an option because the language is not set yet.

Instead I want to do it inside the AppInitializer where my code loads the language from the backend. Doing translate.use correctly downloads the language (I can see it in network) but all translations appear blank.

Not sure what to do, it feels very much like a bug.

worthy7 avatar Dec 04 '23 21:12 worthy7

Ah, it works on some reloads. So I think perhaps the bug is that you cannot set a new use language before it has loaded the default language? I am not sure about where, but it is definately a race condition problem somehow.

worthy7 avatar Dec 04 '23 22:12 worthy7

Ok I found the issue.

For some unknown reason, if you try to load angular locale data (or do any import maybe?) and at the same time try to do a .use then the use will only work (properly show the translations instead of blanks) IF the use completes before the loading of the import is complete.

To fix it, I had to write this stupid code in order to force the angular locale loading to happen only after use was complete.


injector
                .get(TranslateService)
                .use(abp.localization.currentLanguage.name)
                .subscribe(
                  () =>
                    loadAngularLocale(angularLocale, resolve, result, reject),
                  (err) => {
                    loadAngularLocale(angularLocale, resolve, result, reject);
                    console.log(err);
                  }
                );

@ocombe Would love to hear your input on this if you have a minute

worthy7 avatar Dec 04 '23 22:12 worthy7