How to use i18t for texts in the source code of Angular project?

Viewed 2115

I am using the i18n features of Angular 4 and building the project successfully with angular-cli in the target language. HTML templates are properly translated. However I got some texts in the javascript code.

What's the recommended way of localizing strings used in js source for things like validations? Can we expect i18n to come with a solution?

Currently I am using the locale to determine which translation to use. The Locale gets set from the ng serve --locale fr or ng build --locale fr

Building/serving like this:

ng serve --aot --locale fr  ...

and using the locale in the code like this:

import { LOCALE_ID } from '@angular/core';
// ...
constructor (@Inject(LOCALE_ID) locale: string) {
}

(I was following the great hints on http://blog.danieleghidoli.it/2017/01/15/i18n-angular-cli-aot/)

3 Answers

We're now (early 2019) using Angular 7 and it seems there's still no support for localizing static strings in TypeScript source files.

So we come up with a simple (if "hacky") way of doing it:

  1. Add a hidden HTML element to the template with its content set to the static string from JavaScript code:
<!-- string table -->
<span #toolTipText hidden i18n="Tool tip text|Text displayed on the tooltip@@TOOLTIP_TEXT">Do not click this or bad things will happen.</span>
<span #errorOccured hidden i18n="Error notification|Error message displayed when the user ignores the warning on the tooltip@@ERROR_MESSAGE">A very bad thing just happened.</span>
  1. Reference this hidden element in the code with the @ViewChild() decorator:
@Component({
  // ...
})
export class BadThingsComponent implements OnInit {

  // "string table" elements for i18n
  @ViewChild('toolTipText') warningMsg;
  @ViewChild('errorOccurred') errorMsg;

  // ...
  1. Get the string value where it was previously a static string in the code by addressing the decorated class member's .nativeElement.innerText:
onTryToPreventBadThings(event: CustomEvent) {
    // ...

    this.preventer.messageToDisplay = this.warningMsg.nativeElement.innerText;

    // ...
  }

  onBadThingDidHappen(event: CustomEvent) {
    // ...

    this.notifier.message = this.errorMsg.nativeElement.innerText;

    // ...
  }

It seems that Angular is not supporting this yet. I use a mixture of the default Angular i18n approach because the toolchain is quite nice and a 3rd party tool like angular-l10n. The benefit of later is that it is automatically doing what is common for fetching resource files with translations, i.e. falling back from fr-FR -> fr -> en if no translation is available and offering other convenient functions like placeholders in translations. A manual approach does not offer this.

I'm using this in my service like the following:

constructor(translationService: TranslationService) {}
...
foo() {
    this.translationService.translate('httpService.pagingError')
}

In the app starter app.module.ts I modified the setup a bit to load the default locale from the app. Note the constructor.

import { L10nConfig, L10nLoader, TranslationModule, StorageStrategy, ProviderType } from 'angular-l10n';

const l10nConfig: L10nConfig = {
    locale: {
        languages: [
            { code: 'en', dir: 'ltr' },
            { code: 'de', dir: 'ltr' }
        ],
        language: 'en',
        storage: StorageStrategy.Cookie
    },
    translation: {
        providers: [
            { type: ProviderType.Static, prefix: './assets/locale-' }
        ],
        caching: true,
        missingValue: 'No key'
    }
};

@NgModule({
    imports: [
        BrowserModule,
        HttpClientModule,
        TranslationModule.forRoot(l10nConfig)
    ],
    declarations: [AppComponent, HomeComponent],
    bootstrap: [AppComponent]
})
export class AppModule {

    constructor(public l10nLoader: L10nLoader, public localeService: LocaleService,
              @Inject(LOCALE_ID) locale: string) {
        this.l10nLoader.load().then(() => this.localeService.setDefaultLocale(locale));
    }

}
Related