AngularJS 2.0 From The Beginning - Localization - Day Twenty Five

In this article, we will discuss about localization in Angular 2.0.

In this Angular 2.0 article series, we have already discussed about different types of basic and advanced concepts or features of AngularJS 2.0 like data binding, directives, pipes, service, route, http modules, change detection, state management, lazy loading of the modules etc. Now in this article, we will discuss how to implement localization of i18n in Angular framework. In case you have not had a look at the previous articles of this series, go through the links mentioned below.

English is not the first language for a sizable population of the world; therefore building an app that just renders in English may not be a very wise idea. Imagine, with just some content localization, how much we can gain in terms of application reach. Internationalization is all about designing our app so that anyone in any region of the world can use the app in their native locale. Before we dive deeper into this topic, why not address a common confusion between what internationalization (I18n) and what localization (L10n) is.

Internationalization (I18n) is the process of making sure that the application has elements that can help it render locale-specific content. In other words, the application is adaptable to different locales. Localization (L10n) on the other hand, is the actual process of adapting parts of the application to specific locales. These could be the text shown, the date, the time, the current formatting, and other such content that is affected by locale change.

It is a normal tendency to just overlook this topic and believe that the app we are building does not need to handle I18n concerns. But even apps targeted at an English language audience need to manage locale variations that are country-specific. We may be able to work with the text content, but we still need to handle date, number, and currency variations.

For example, en-us (language code, English: United States) and en-gb (English: United Kingdom) have variations. The US date format is MM/DD/YY whereas for the UK, it is DD/MM/YY. The same holds true for currency formatting too.

What is the purpose of i18n?

Internationalization (or i18n, which stands for i--n) is the process of adapting software and web applications to multiple languages, allowing their application to be used by multiple language-speaking users. By ensuring that your application supports multiple languages, you can reach a broader audience and ensure a smooth user experience for multilingual users.

Process & Roles

In order for an application to successfully support multiple languages, multiple team members must closely dedicate their efforts to working together. Translators must be introduced to the team as they will be the ones translating the content and will need guidance on the intent and meaning of the words being used. Using Angular's i18n facilities, the process would look like this.

  • Mark text messages in your templates for translation.
  • Use an Angular i18n tool to extract the messages into an industry standard translation source file.
  • Provide the industry standard translation source files to a translator, who will translate the files and give them back to you.
  • Import the completed translation files using the Angular compiler.

Unlike Angular 1.x, Angular 2.0 does not have its own readymade localization. But, we can achieve the goal by creating the translation service.

For doing this, create the below files as mentioned.

English.ts (This file is used basically as dictionary for the English language)
  1. export const LANG_EN_NAME = 'en';  
  2. export const LANG_EN_CURRENCY = 'INR';  
  3. export const LANG_EN_DATEFORMAT = 'mm/dd/yyyy';  
  4. export const LANG_EN_TRANS = {  
  5.     "HEADLINE": 'Hello there, This is my awesome app!',  
  6.     "INTRO_TEXT": 'And it has i18n support!',  
  7.     "BUTTON_TEXT_EN": 'english',  
  8.     "BUTTON_TEXT_DE": 'german'  

German.ts (This file is used basically as dictionary for the German language) 
  1. export const LANG_EN_DE_NAME = 'de';  
  2. export const LANG_EN_DE_CURRENCY = 'DEM';  
  3. export const LANG_EN_DE_DATEFORMAT = 'dd/mm/yyyy';  
  4. export const LANG_EN_DE_TRANS = {  
  5.     "HEADLINE": 'Hey, das ist meine großartige App!',  
  6.     "INTRO_TEXT": 'Und sie untersützt mehrere Sprachen!',  
  7.     "BUTTON_TEXT_EN": 'englisch',  
  8.     "BUTTON_TEXT_DE": 'deutsch'  

translatepipe.ts (This file is used basically for transalate any key at HTML part) 
  1. import { Pipe, PipeTransform } from '@angular/core';  
  2. import { TranslateService } from './translateservice.js';  
  3.   
  4. @Pipe({  
  5.     name: 'translate',  
  6.     pure: false  
  7. })  
  8.   
  9. export class TranslatePipe implements PipeTransform {  
  10.   
  11.     constructor(private _translate: TranslateService) { }  
  12.   
  13.     transform(value: string, args: any[]): any {  
  14.         if (!value) return;  
  15.         return this._translate.instant(value);  
  16.     }  

translateservice.ts (This service file is used to load or save the language key) 
  1. import { Injectable, Inject } from '@angular/core';  
  2. import { Translation, CurrencySymbol, DateFormat } from './translations.js';  
  3.   
  4. @Injectable()  
  5. export class TranslateService {  
  6.     currentLang: string = 'en';  
  7.   
  8.     constructor( @Inject(Translation) private _translations: any, @Inject(CurrencySymbol) private _currency: any,  
  9.         @Inject(DateFormat) private _dateFormat: any) {  
  10.     }  
  11.   
  12.     public setLocale(key: string): void {  
  13.         this.currentLang = key;  
  14.     }  
  15.   
  16.     public getLocale(): string {  
  17.         return this.currentLang;  
  18.     }  
  19.   
  20.     private translate(key: string): string {  
  21.         let translation = key;  
  22.         if (this._translations[this.currentLang] && this._translations[this.currentLang][key]) {  
  23.             translation = this._translations[this.currentLang][key];  
  24.         }  
  25.         return translation;  
  26.     }  
  27.   
  28.     public instant(key: string) {  
  29.         return this.translate(key);  
  30.     }  
  31.   
  32.     public getLocaleCurrency(): string {  
  33.         let currency = 'USD';  
  34.         if (this._currency[this.currentLang]) {  
  35.             return this._currency[this.currentLang];  
  36.         }  
  37.         return currency;  
  38.     }  
  39.   
  40.     public getLocaleDateFormat(): string {  
  41.         let currency = 'mm/dd/yyyy';  
  42.         if (this._dateFormat[this.currentLang]) {  
  43.             return this._dateFormat[this.currentLang];  
  44.         }  
  45.         return currency;  
  46.     }  

translations.ts (This file is used as constant file to load different language translation providers)  
  1. import { OpaqueToken } from '@angular/core';  
  2. import { LANG_EN_NAME, LANG_EN_TRANS, LANG_EN_CURRENCY, LANG_EN_DATEFORMAT } from './english.js';  
  3. import { LANG_EN_DE_NAME, LANG_EN_DE_TRANS, LANG_EN_DE_CURRENCY, LANG_EN_DE_DATEFORMAT } from './german.js';  
  4.   
  5. export const Translation = new OpaqueToken('translations');  
  6. export const CurrencySymbol = new OpaqueToken('translations');  
  7. export const DateFormat = new OpaqueToken('translations');  
  8.   
  9. const languageDictionary = {  
  10.     [LANG_EN_NAME]: LANG_EN_TRANS,  
  11.     [LANG_EN_DE_NAME]: LANG_EN_DE_TRANS  
  12. };  
  13.   
  14. const currencyDictionary = {  
  15.     [LANG_EN_NAME]: LANG_EN_CURRENCY,  
  16.     [LANG_EN_DE_NAME]: LANG_EN_DE_CURRENCY  
  17. }  
  18.   
  19. const dateformatDictionary = {  
  20.     [LANG_EN_NAME]: LANG_EN_DATEFORMAT,  
  21.     [LANG_EN_DE_NAME]: LANG_EN_DE_DATEFORMAT  
  22. }  
  23.   
  24. export const TRANSLATION_PROVIDERS = [  
  25.     { provide: Translation, useValue: languageDictionary },  
  26.     { provide: CurrencySymbol, useValue: currencyDictionary },  
  27.     { provide: DateFormat, useValue: dateformatDictionary },  
  28. ]; 
app.component.homepage.html 
  1. <div>  
  2.     <h1>Localization</h1>  
  3.     <div>  
  4.         <button (click)="changeLanguage('en')">English</button>  
  5.         <button (click)="changeLanguage('de')">German</button>  
  6.     </div>  
  7.     <div>  
  8.         <h2>{{ 'HEADLINE' | translate }}</h2>  
  9.         <p>{{ 'INTRO_TEXT' | translate }}</p>  
  10.     </div>  
  11.     <div>  
  12.         Number : {{106.89 | number:'2.2-2'}}  
  13.         <br />  
  14.         Currency :  {{1123.00 | currency}}  
  15.         <br />  
  16.         Today's Date :- {{_todayDate | date : "fullDate"}} (Full Date)<br />  
  17.         {{_todayDate | date : "longDate"}} (Long Date)<br />  
  18.         {{_todayDate | date : "shortDate"}} (Short Date)<br />  
  19.     </div>  
  20. </div> 
app.component.homepage.ts
  1. import { Component, OnInit, ViewChild } from '@angular/core';  
  2. import { Http, Response } from '@angular/http';  
  3. import { TranslateService } from '../locale/translateservice.js';  
  4.   
  5. @Component({  
  6.     moduleId: module.id,  
  7.     selector: 'home-page',  
  8.     templateUrl: 'app.component.homepage.html'  
  9. })  
  10.   
  11. export class HomePageComponent implements OnInit {  
  12.   
  13.     private _todayDate: Date;  
  14.   
  15.     constructor(private _transService: TranslateService) {  
  16.     }  
  17.   
  18.     ngOnInit(): void {  
  19.         this._todayDate = new Date();  
  20.     }  
  21.   
  22.     changeLanguage(locale: string): void {  
  23.         debugger;  
  24.         this._transService.setLocale(locale);  
  25.   
  26.     }  
  27. }   
app.module.ts 
  1. import { NgModule, NO_ERRORS_SCHEMA, LOCALE_ID } from '@angular/core';  
  2. import { BrowserModule } from '@angular/platform-browser';  
  3. import { ReactiveFormsModule } from "@angular/forms";  
  4.   
  5. import { TranslateService } from './locale/translateservice.js'  
  6. import { Translation, TRANSLATION_PROVIDERS } from './locale/translations.js';  
  7. import { TranslatePipe } from './locale/translatepipe.js';  
  8.   
  9. import { HomePageComponent } from './src/app.component.homepage';  
  10.   
  11. @NgModule({  
  12.     imports: [BrowserModule, ReactiveFormsModule],  
  13.     declarations: [HomePageComponent, TranslatePipe],  
  14.     exports: [TranslatePipe],  
  15.     bootstrap: [HomePageComponent],  
  16.     providers: [TRANSLATION_PROVIDERS, { provide: TranslateService, useClass: TranslateService },  
  17.         { provide: LOCALE_ID, deps: [TranslateService], useFactory: (TranslateService) => TranslateService.getLocale() }  
  18.     ]  
  19. })  
  20.   
  21. export class AppModule { } 
main.ts 
  1. import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';  
  2.   
  3. import { AppModule } from './app.module';  
  4.   
  5. const platform = platformBrowserDynamic();  
  6. platform.bootstrapModule(AppModule); 
Index.html
  1. <!DOCTYPE html>  
  2. <html>  
  3. <head>  
  4.     <title>Angular2 - Localisation </title>  
  5.     <meta charset="UTF-8">  
  6.     <meta name="viewport" content="width=device-width, initial-scale=1">  
  7.     <link href="../resources/style/bootstrap.css" rel="stylesheet" />  
  8.     <link href="../resources/style/style1.css" rel="stylesheet" />  
  9.     <!-- Polyfill(s) for older browsers -->  
  10.     <script src="../resources/js/jquery-2.1.1.js"></script>  
  11.     <script src="../resources/js/bootstrap.js"></script>  
  12.   
  13.     <script src="../node_modules/core-js/client/shim.min.js"></script>  
  14.     <script src="../node_modules/zone.js/dist/zone.js"></script>  
  15.     <script src="../node_modules/reflect-metadata/Reflect.js"></script>  
  16.     <script src="../node_modules/systemjs/dist/system.src.js"></script>  
  17.     <script src="../systemjs.config.js"></script>  
  18.     <script>  
  19.         System.import('app').catch(function (err) { console.error(err); });  
  20.     </script>  
  21.     <!-- Set the base href, demo only! In your app: <base href="/"> -->  
  22.     <script>document.write('<base href="' + document.location + '" />');</script>  
  23. </head>  
  24. <body>  
  25.     <home-page>Loading</home-page>  
  26. </body>  
  27. </html> 
Now, run the program. The output is shown below.