Angular | Working with I18N
In this post, you will learn how to get started with Angular I18n using ngx-translate, the internationalization (i18n) library for Angular. We will cover the following topics:
- setup new angular app
- install required dependencies
- add bootstrap as ui framework
- create app with demo page and translation services
This will be the final result (click to show video). Source code for this post is on GitHub.
Setup new Angular app
➜ ng new app ➜ cd app
Add required modules
➜ npm install @ngx-translate/core @ngx-translate/http-loader rxjs --save
Add Bootstrap as UI framework
We need the libraries for Bootstrap and flag icons. Download the required files into your asset/vendor/bootstrap/5.3.1
folder:
Flags Icons need a CSS file with the corresponding images for the flags, so we use the archive from GitHub.
- Flags Icons Archiv: https://github.com/lipis/flag-icons
Download and extract the archive into the folder assets/vendor/flag-icons
Current folder structure
This is our current folder structure:
Setup Application
We choose the following structure for the HTML architecture.
index.html
contains the requiredcss
andjs
files. Also, the<app-root>
component, which loads our appapp.component.html
contains the main structure of every page. This includes header, navigation, place for main content and footer- The main content is inserted via the
<router-outlet>
index.html
We will add the corresponding file in the main index.html
in our project.
<!doctype html> <html lang="en" class="h-100"> <head> <meta charset="utf-8"> <title>I18N</title> <base href="/"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" type="image/x-icon" href="favicon.ico"> <link rel="stylesheet" href="/assets/vendor/bootstrap.min.css"> <link rel="stylesheet" href="/assets/vendor/flag-icons.min.css"> <link rel="stylesheet" href="/assets/vendor/bootstrap/5.1.3/css/bootstrap.min.css"> <link rel="stylesheet" href="/assets/vendor/flag-icons/css/flag-icons.min.css"> <link rel="stylesheet" href="/assets/css/default.css"> </head> <body class="d-flex flex-column h-100"> <app-root class="h-100"></app-root> <script src="assets/vendor/bootstrap/5.1.3/js/bootstrap.bundle.min.js"></script> </body> </html>
app.component.html
We borrow the main structure from the bootstrap example ‘Sticky Footer with Navbar‘ with some changes in the navigation bar.
<header> <nav class="navbar navbar-expand-md navbar-dark fixed-top bg-primary"> <div class="container-fluid"> <a class="navbar-brand" href="#"> <img src="assets/img/logo-angular.png" height="40px" width="auto"> </a> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarCollapse"> <ul class="navbar-nav me-auto mb-2 mb-md-0"> <li class="nav-item"> <a class="nav-link active" aria-current="page" href="#">Home</a> </li> </ul> <div class="btn-group dropstart"> <button class="btn btn-primary dropdown-toggle" type="button" id="dropdownFlags" data-bs-toggle="dropdown" aria-expanded="false"> <span class="flag-icon flag-icon-{{currentlang}}"></span> </button> <ul class="dropdown-menu"> <li *ngFor="let lang of languages" [value]="lang" (click)="useLanguage(lang)"> <a class="dropdown-item" [ngClass]="{'active': currentlang == lang}"> <span class="flag-icon flag-icon-{{lang}}"></span> {{lang | uppercase}} </a> </li> </ul> </div> </div> </div> </nav> </header>
<main class="flex-shrink-0"> <div class="container"> <router-outlet></router-outlet> </div> </main>
<footer class="footer mt-auto py-3 bg-dark"> <div class="container"> <span class="text-muted">(C) Ralph Göstenmeier</span> </div> </footer>
app.component.ts
import { Component } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import defaultLanguage from '../assets/i18n/de.json'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'], }) export class AppComponent { title = 'I18N'; languages = ['us', 'de', 'fr', 'sp']; currentlang = 'us'; constructor(private translate: TranslateService) { this.currentlang = 'de'; translate.setTranslation(this.currentlang, defaultLanguage); translate.setDefaultLang(this.currentlang); } ngOnInit(): void {} useLanguage(language: string): void { this.currentlang = language; this.translate.use(language.toLowerCase()); } }
app.module.ts
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { HttpClient, HttpClientModule } from '@angular/common/http'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { TranslateHttpLoader } from '@ngx-translate/http-loader'; import { HomePageComponent } from './pages/home/component'; import { DemoPageComponent } from './pages/demo/component'; @NgModule({ declarations: [AppComponent, HomePageComponent, DemoPageComponent], imports: [ BrowserModule, AppRoutingModule, HttpClientModule, TranslateModule.forRoot({ loader: { provide: TranslateLoader, useFactory: HttpLoaderFactory, deps: [HttpClient], }, }), ], providers: [], bootstrap: [AppComponent], }) export class AppModule {} // required for AOT compilation export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader { return new TranslateHttpLoader(http); }
How the app works
The translation is done with the ngx-translate component.
Translation works with different JSON files (for each language a separate file), containing the required translation for each text to be displayed. Each text is addressed with a name within the JSON file.
So, the base structure of each JSON file is the following:
Translation files
assets/i18n/de.json
{ "i18n-demo-header": "I18N Demo", "header": "I18N Funktionalität in Angular" }
assets/i18n/us.json
{ "i18n-demo-header": "I18N Example", "header": "I18N Functionality in Angular" }
These translations could be used in a html file by using the translate pipe:
<h1>{{'header' | translate }}</h1>
More information and examples are here.
Changing the language is done with the help of the TranslateService
Inject your app with the TranslateService (in app.component.ts
)
constructor(private translate: TranslateService) { translate.setDefaultLang('de'); }
Change Language
useLanguage(language: string): void { this.translate.use(language.toLowerCase()); }
Integrate in our UI
To easy switching the language, we have to do a few steps
Add a dropdown menu to our navigation bar.
And switching the language is done by calling useLanguage
within each menu item:
<div class="btn-group dropstart"> <button class="btn btn-primary dropdown-toggle" type="button" id="dropdownFlags" data-bs-toggle="dropdown" aria-expanded="false"><span class="flag-icon flag-icon-{{currentlang}}"></span> </button> <ul class="dropdown-menu"> <li *ngFor="let lang of languages" [value]="lang" (click)="useLanguage(lang)"> <a class="dropdown-item" [ngClass]="{'active': currentlang == lang}"> <span class="flag-icon flag-icon-{{lang}}"></span> {{lang | uppercase}} </a> </li> </ul> </div>
Setup a list for all menu items:
<ul class="dropdown-menu">
Add a list item for each language
Add items (*ngFor="let lang of languages"
) containing a link and an event handler for the click event.
<li *ngFor="let lang of languages" [value]="lang" (click)="useLanguage(lang)">