Subscribe to my email list now at http://jauyeung.net/subscribe/
Follow me on Twitter at https://twitter.com/AuMayeung
Many more articles at https://medium.com/@hohanga
With the modular structure of Angular apps, adding localization and translation features to it is easy. The most popular Angular add-on is ngx-translate
. Angular 2+ is supported by ngx-translate
.
Given an existing Angular app, we can install ngx-translate
by running npm i ngx-translate
.
After installing the package, make sure you have the following in your Angular module file. For example, in app.module.ts
, we have:
export function HttpLoaderFactory(http: HttpClient) {
return new TranslateHttpLoader(http, './assets/i18n/', '.json');
}
This allow us to load our JSON translation files from the ./assets/i18n/
. The JSON file should be named according to standard language codes listed in the ‘639–1’ column in the table of https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes. ngx-translate
will load it into the Angular app, with its HTTP loader via a GET request.
app.module.ts
should look something this, given all of these components and libraries are included in the app:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { RouterModule, Routes } from '@angular/router';
import { PaginationModule } from 'ngx-bootstrap/pagination';
import { AppComponent } from './app.component';
import { AccountPageComponent } from './account-page/account-page.component';
import { LoginPageComponent } from './login-page/login-page.component';
import { RegisterPageComponent } from './register-page/register-page.component';
import { MenuPageComponent } from './menu-page/menu-page.component';
import { OrdersPageComponent } from './orders-page/orders-page.component';
import { CategoryPageComponent } from './category-page/category-page.component';
import { AboutPageComponent } from './about-page/about-page.component';
import { HomePageComponent } from './home-page/home-page.component';
import { OrderService } from './order.service';
import { MenuService } from './menu.service';
import { UserService } from './user.service';
import { LocationService } from './location.service';
import { CategoryService } from './category.service';
import { RestaurantService } from './restaurant.service';
import { RestaurantsService } from './restaurants.service';
import { IsLoggedInGuard } from './is-logged-in.guard';
import { BottomBarComponent } from './bottom-bar/bottom-bar.component';
import { CartPageComponent } from './cart-page/cart-page.component';
import { VerifyAccountPageComponent } from './verify-account-page/verify-account-page.component';
import { SearchPageComponent } from './search-page/search-page.component';
import { RestaurantPageComponent } from './restaurant-page/restaurant-page.component';
import { ItemPageComponent } from './item-page/item-page.component';
import { ForgotPasswordPageComponent } from './forgot-password-page/forgot-password-page.component';
import { ResetPasswordPageComponent } from './reset-password-page/reset-password-page.component';
import { HttpClientModule, HttpClient } from '@angular/common/http';
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
export function HttpLoaderFactory(http: HttpClient) {
return new TranslateHttpLoader(http, './assets/i18n/', '.json');
}
const appRoutes: Routes = [
{ path: 'category/:category', component: CategoryPageComponent },
{ path: 'login', component: LoginPageComponent },
{ path: 'verifyaccount/:userId/:verifyToken', component: VerifyAccountPageComponent },
{ path: 'register', component: RegisterPageComponent, },
{ path: 'resetpassword/:userId/:resetToken', component: ResetPasswordPageComponent, },
{ path: 'cart', component: CartPageComponent, },
{ path: 'forgotpassword', component: ForgotPasswordPageComponent, },
{ path: 'restaurant/:id', component: RestaurantPageComponent },
{ path: 'item/:restaurantId/:menuId/:itemId', component: ItemPageComponent },
{ path: 'menu', component: MenuPageComponent, canActivate: [IsLoggedInGuard] },
{ path: 'orders', component: OrdersPageComponent, canActivate: [IsLoggedInGuard] },
{ path: 'about', component: AboutPageComponent, },
{ path: 'search/:address', component: SearchPageComponent, },
{ path: 'account', component: AccountPageComponent, canActivate: [IsLoggedInGuard] },
{ path: '**', component: HomePageComponent }
];
@NgModule({
declarations: [
AppComponent,
AccountPageComponent,
LoginPageComponent,
RegisterPageComponent,
MenuPageComponent,
OrdersPageComponent,
CategoryPageComponent,
AboutPageComponent,
HomePageComponent,
BottomBarComponent,
CartPageComponent,
VerifyAccountPageComponent,
SearchPageComponent,
RestaurantPageComponent,
ItemPageComponent,
ForgotPasswordPageComponent,
ResetPasswordPageComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule,
RouterModule.forRoot(
appRoutes, { useHash: true }
),
PaginationModule.forRoot(),
HttpClientModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient]
}
})
],
providers: [
RestaurantService,
RestaurantsService,
OrderService,
MenuService,
UserService,
LocationService,
CategoryService,
IsLoggedInGuard
],
bootstrap: [AppComponent]
})
export class AppModule { }
The JSON should be something like this (en.json
):
{
"Chiyoda": "Chiyoda",
"Chūō": "Chūō",
"Minato": "Minato",
"Shinjuku": "Shinjuku",
"Bunkyō": "Bunkyō",
"Taitō": "Taitō",
"Sumida": "Sumida",
"Kōtō": "Kōtō",
"Shinagawa": "Shinagawa",
"Meguro": "Meguro",
"Ōta": "Ōta",
"Setagaya": "Setagaya",
"Shibuya": "Shibuya",
"Nakano": "Nakano",
"Suginami": "Suginami",
"Toshima": "Toshima",
"Kita": "Kita",
"Arakawa": "Arakawa",
"Itabashi": "Itabashi",
"Nerima": "Nerima",
"Adachi": "Adachi",
"Katsushika": "Katsushika",
"Edogawa": "Edogawa",
"Select a Location": "Select a Location",
"City": "City",
"Ward": "Ward",
"Submit": "Submit",
"Buffet": "Buffet",
"Office Packages": "Office Packages",
"Family Event": "Family Event",
"Seminar": "Seminar",
"High tea and dessert table": "High tea and dessert table",
"Christmas": "Christmas",
"Kids Party": "Kids Party",
"Wedding": "Wedding",
"Become a partner": "Become a partner",
"About": "About"
}
and foreign language file (jp.json
):
{
"Chiyoda": "千代田区",
"Chūō": "中央区",
"Minato": "港区",
"Shinjuku": "新宿区",
"Bunkyō": "文京区",
"Taitō": "台東区",
"Sumida": "墨田区",
"Kōtō": "江東区",
"Shinagawa": "品川区",
"Meguro": "目黒区",
"Ōta": "大田区",
"Setagaya": "世田谷区",
"Shibuya": "渋谷区",
"Nakano": "中野区",
"Suginami": "杉並区",
"Toshima": "豊島区",
"Kita": "北区",
"Arakawa": "荒川区",
"Itabashi": "板橋区",
"Nerima": "練馬区",
"Adachi": "足立区",
"Katsushika": "葛飾区",
"Edogawa": "江戸川区",
"Select a Location": "地域を選択",
"City": "都市",
"Ward": "市区町村",
"Submit": "検索",
"Buffet": "ビュッフェ",
"Office Packages": "お弁当",
"Family Event": "家族行事",
"Seminar": "セミナー",
"High tea and dessert table": "デザート",
"Christmas": "クリスマス",
"Kids Party": "子供向けパーティー",
"Wedding": "ウェディング",
"Become a partner": "パートナー提携",
"About": "私たちについて"
}
You can also nest your translations in a lower level in your JSON file, like so:
{
"text":{
"Chiyoda": "千代田区",
"Chūō": "中央区",
"Minato": "港区",
"Shinjuku": "新宿区",
"Bunkyō": "文京区",
"Taitō": "台東区",
"Sumida": "墨田区",
"Kōtō": "江東区",
"Shinagawa": "品川区",
"Meguro": "目黒区",
"Ōta": "大田区",
"Setagaya": "世田谷区",
"Shibuya": "渋谷区",
"Nakano": "中野区",
"Suginami": "杉並区",
"Toshima": "豊島区",
"Kita": "北区",
"Arakawa": "荒川区",
"Itabashi": "板橋区",
"Nerima": "練馬区",
"Adachi": "足立区",
"Katsushika": "葛飾区",
"Edogawa": "江戸川区",
"Select a Location": "地域を選択",
"City": "都市",
"Ward": "市区町村",
"Submit": "検索",
"Buffet": "ビュッフェ",
"Office Packages": "お弁当",
"Family Event": "家族行事",
"Seminar": "セミナー",
"High tea and dessert table": "デザート",
"Christmas": "クリスマス",
"Kids Party": "子供向けパーティー",
"Wedding": "ウェディング",
"Become a partner": "パートナー提携",
"About": "私たちについて"
}
}
Then in your template, you use the keys with the translate
filter in to get translated text:
{{'Select a Location' | translate}}
If the translations in JSON is nested in another property, you can display them like so:
{{'text.About' | translate}}
Note that you cannot have spaces in your keys if you use the dot notation.
With this, the Select a Location
key is looked up in the JSON, and depending on the language you set, it’ll get the right text from the value gotten from the key. If the value does not existing for the given key, then it will display the translate key.
Language can be changed in the code by injecting the TranslateService
into your code. For example, in your component code, you do the following:
constructor(private translate: TranslateService)
Then you can do:
this.translate.use('jp');
to set the app to display Japanese.
You can also display the default language like so:
this.translate.setDefaultLang('jp');
Translations in Angular is straightforward. It is just a matter of installing a new package, adding the translation, setting the language, and make sure that it displays in the template.