42

I am trying to make a method which would accept string key and return translated string value by using translate.instant(parameter). The problem is that it returns key(parameter). Usually this is returned if it doesn't find translation. I think the problem is that method gets called before loader loads translations.

My app.module.ts imports:

    TranslateModule.forRoot({
  loader: {
    provide: TranslateLoader,
    useFactory: (createTranslateLoader),
    deps: [HttpClient]
  }
})

createTranslateLoader function:

    export function createTranslateLoader(http: HttpClient) {
  return new TranslateHttpLoader(http, './assets/i18n/', '.json');
}

In my app.component:

constructor(public translate: TranslateService){
   translate.setDefaultLang('en');
   translate.use('en');
}

When I translate in html using pipes it works ok.

OjamaYellow
  • 899
  • 1
  • 13
  • 27
  • Add Your code with .instant or just replace `.instant()` by `.stream()` it's work for me – Salim Ibrohimi Sep 14 '17 at 10:14
  • in what way did you use .stream(), because when I add it, it says Argument of type 'Observable' is not assignable to parameter of type 'string' – OjamaYellow Sep 14 '17 at 10:21
  • please add Your code: – Salim Ibrohimi Sep 14 '17 at 10:23
  • 1
    `this.translateService.stream(event['title']) .subscribe((title) => this.titleService.setTitle(title))` – Salim Ibrohimi Sep 14 '17 at 10:23
  • cant get it work, i'm not that experienced, can you help me, I want to translate string in typescript and the key for translation is General.Add...how do I do this with stream? thank you. – OjamaYellow Sep 14 '17 at 10:29
  • `.get()`, `.instant()` and `.stream()` functions are similar, please check this doc: https://github.com/ngx-translate/core; – Salim Ibrohimi Sep 14 '17 at 10:32
  • Where is the code your method that will get the string key and returns a translated string? Is this in a provider? How are you injecting it? – Christian Benseler Sep 14 '17 at 10:33
  • Possible duplicate of [Unable to translate text using ngx-translate's service-translate.get](https://stackoverflow.com/questions/45231037/unable-to-translate-text-using-ngx-translates-service-translate-get) – Jota.Toledo Sep 14 '17 at 11:26
  • Maybe not a dup but related – Jota.Toledo Sep 14 '17 at 11:26
  • maybe add the code that is giving you problems? – Jota.Toledo Sep 14 '17 at 13:10
  • I also used the stream ` this.translate .stream('manageNotifications.popupMessages') .subscribe((v) => { this.reassignKeywordPopupMsg = v.reassignKeywordPopupMsg; this.deleteKeywordPopupMsg = v.deleteKeywordPopupMsg; } );` – Rajantha Fernando Jun 11 '21 at 12:37
  • I think this solution is the cleanest, which uses a provider: https://stackoverflow.com/questions/61907829/translate-service-not-working-on-load-page – user1689987 Aug 23 '21 at 03:38

6 Answers6

55

You are using the TranslateHttpLoader which makes an HTTP request when it's asked for translations - translate.use('en'). If you call the instant(messageKey) method before the HTTP call returns, ngx-translate will return the key, since it has no translations yet. So you should use the get(messageKey) method to get the translation - it's asynchronous and returns an Observable:

this.translateService.get('hello.world').subscribe((translated: string) => {
    console.log(res);
    //=> 'Hello world'

    // You can call instant() here
    const translation = this.translateService.instant('something.else');
    //=> 'Something else'
});

You can use the instant method only when you are sure that the translations have been already loaded (as in the code example) or you can write your custom synchronous translation loader and use instant anywhere.

Ján Halaša
  • 8,167
  • 1
  • 36
  • 36
  • 1
    I tried get before and it works but I cant use it in a method because returning something from async call is risky because you return before you get the value. I was thinking about writing custom loader but I dont know how. Do you have any idea? ty for answer – OjamaYellow Sep 14 '17 at 13:14
  • 1
    You can find an example of a synchronous loader at the [project website](https://github.com/ngx-translate/core#write--use-your-own-loader). The easiest way is to define the translations in the loader itself either as a `Map` object or a basic JSON object as is shown in the example. – Ján Halaša Sep 14 '17 at 13:39
  • 1
    I implemented it the way it shows in example, but now I think it doesn't find my folder with translations because everywhere only keys are written, am I missing something? – OjamaYellow Sep 14 '17 at 14:18
  • 1
    If should specify all your key/value pairs in your loader - the ones that you had in a separate JSON file, because ngx-translate doesn't load them from the JSON files anymore. If the problem persists, your best chance is to debug the loader and/or the `TranslateService` class from ngx-translate. – Ján Halaša Sep 14 '17 at 14:29
  • 1
    Excellent. Thanks. I was using OP's approach in the app.component.ts, and clearly the translations have not been loaded into memory yet. Your approach worked a treat. – Charles Robertson Jan 18 '22 at 21:30
22

You can use TranslateService only when translation file is loaded. If you want to use safely TranslateService.instant you can write an angular resolver or a guard. Resolver and guard wait to exec your component code until the observable return a value.

This is the code:

-------------------------GUARD------------------------------------



@Injectable()
export class TranslationLoaderGuard {

    constructor(private translate: TranslateService, private languageService: SetLanguageService) {
    }

    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
        this.languageService.inizializeLanguage()

        return new Observable((observer) => {
            this.translate.get("last.dummy").subscribe({
                next: () => {
                    observer.next(true);
                },
                error: error => {
                    observer.next(false);
                    observer.error(error);
                },
                complete: () => {
                    observer.complete();
                },
            })
        })
    }

    canActivateChild(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
        this.languageService.inizializeLanguage()

        return new Observable((observer) => {
            this.translate.get("last.dummy").subscribe({
                next: () => {
                    observer.next(true);
                },
                error: error => {
                    observer.next(false);
                    observer.error(error);
                },
                complete: () => {
                    observer.complete();
                },
            })
        })
    }

}

---------------------ROUTING MODULE------------------

let routing = RouterModule.forChild([
    {path: "dashboard", component: DashboardComponent, canActivate: [TranslationLoaderGuard],,
     children: [
        ........//here you can omit Guard
        },
}

-----Files i18n-----

In last line of each i18n translation file add the following line 
as is (do not translate this) ----> "last.dummy"="dummy translation"

SetLanguageService is a service that you can create to store the language used, for example in session storage (for example lang is 'it', 'en').

I hope this can help

palex01
  • 361
  • 5
  • 6
4

You can also make a dummy call, and await for the response. After the response, every instant call will work because its sure than the translations are loaded.

async ngOnInit() {
  await this.translate.get('dummyTranslation').toPromise().then();
  this.translate.instant("realTranslation");
0

Just wrap your $translate.instant with the onReady like so:

$translate.onReady(function () { //Code here })

abelabbesnabi
  • 1,819
  • 1
  • 16
  • 21
  • As far as I know, ngx-translate does not have such mechanism as `onReady`. Can you paste a reference where this is documented/explained? – metodribic Oct 15 '21 at 08:01
0

You can also do :

await firstValueFrom(this.translate.get('key')).then(t => this.text = t);

or :

await firstValueFrom(this.translate.get('_'));
this.text = this.translate.instant('key');
mbourd
  • 35
  • 4
-2

Just a reminder: Remember to clear the localStorage. It was my mistake.

JaMondo
  • 278
  • 2
  • 8
  • This has nothing to do with the localStorage localStorage are only for storing values and keys and retrive the same the issue is due to to the translation files not being loaded before using the instant method. – kushal Baldev Mar 07 '23 at 04:41