Angular i18n

A note based on serveral documents from the following sites:

  • https://angular.io/guide/i18n
  • http://www.ngx-translate.com/
  • https://github.com/ngx-translate/core
  • https://medium.com/letsboot/translate-angular-4-apps-with-ngx-translate-83302fb6c10d
  • https://medium.com/@TuiZ/how-to-split-your-i18n-file-per-lazy-loaded-module-with-ngx-translate-3caef57a738f

1. Ooops, Angular is Not an Option

Unfortunately, the official Angular i18n as Version 5.0 is still compile-time only. Unless you want to have didicated hosting for each language, it’s not an option. Additionally, it’s pretty heavy becaues of the use of XML-based content translation files.

2. Use ngx-translate

ngx-translate is an i18n liberary for Angular 2+ that allows dynamic locales by handling dynamic content via a service, a directive and a pipe. It has a core module and a http loader module. There are some tools an loaders from third parties. For example, ngx-translate-extract for string extraction and ngx-translate-po-http-loader to load gettext po files.

2.1 Installation

To use the ngx-translate, you need the core and a loader.

npm install @ngx-translate/core --save
npm install @ngx-translate/http-loader --save

2.2 Config

First, import the necessary modules into app.module.ts:

import { HttpClientModule, HttpClient } from '@angular/common/http';
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';

Second, add a factory function that return a TranslateHttpLoader. This is required by AOT. Then import all modules into the @NgModule.

export function HttpLoaderFactory(http: HttpClient) {
    return new TranslateHttpLoader(http);
}

@NgModule({
    ...
    imports: [
        HttpClientModule,
        TranslateModule.forRoot({
            loader: {
                provide: TranslateLoader,
                useFactory: HttpLoaderFactory,
                deps: [HttpClient]
            }
        })
    ],
    ...
})

For a lazy load module, you can use a customized loader. It’s possible to have a different i18n file for a modules. in this case, you need to set isolate: true for both the root module and all lazy load modules. In a lazy loader module, each i18n loader should have a differnt load path as shown below:

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

@NgModule({
    imports: [
        CommonModule,
        ChildRoutingModule,
        TranslateModule.forChild({
            loader: {
                provide: TranslateLoader,
                useFactory: (createTranslateLoader),
                deps: [HttpClient]
            },
            isolate: true
        })
    ],
    declarations: [ChildComponent]
})
export class ChildModule { }

2.3 Inject Service and Set Default Language

Inject the translate service and set default language. Define a switchLanguage method to change the current locale dynamically by calling the use method defined in TranslateService.

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

switchLanguage(language: string) {
    this.translate.use(language);
}

3. Translatioin Files

Create a JSON file for each language under your asset/i18n folder. The file name follows the pattern of locale-name.json. For example, an English file might have the following content:

{
    "HELLO": "hello ",
    "Intro": "Hello I am , I am  years old.",
    "Startpage": {
        "TranslationSections": "Hello World"
    },
     "Aboutpage": {
        "TranslationSections": "We are letsboot"
    }
}

The content supports variables and nested objects.

4. Use Tranlation Values

You can either use the TranslateService, TranslatePipe or the TranslateDirective to get translation values.


// use service
translate.get('HELLO', {value: 'world'}).subscribe((res: string) => {
    console.log(res);
    //=> 'hello world'
});

// use pipe
param = {value: 'world'};
<div>HELLO</div>

// use directive
<div [translate]="'HELLO'" [translateParams]="{value: 'world'}"></div>

// or use the content key directly
<div translate [translateParams]="{value: 'world'}">HELLO</div>
Written on November 9, 2017