Installation
First, add the Angular localize package to your Angular project:
ng add @angular/localize
This command will:
- Install the
@angular/localize
package - Add necessary imports to your
polyfills.ts
- Update your
angular.json
configuration
Angular Configuration
Configure your Angular project for internationalization in angular.json
:
{
"projects": {
"your-app": {
"i18n": {
"sourceLocale": "en-US",
"locales": {
"pl": {
"translation": "src/locale/messages.pl.xlf",
"baseHref": ""
},
"es": {
"translation": "src/locale/messages.es.xlf",
"baseHref": ""
},
"fr": {
"translation": "src/locale/messages.fr.xlf",
"baseHref": ""
}
}
},
"architect": {
"build": {
"configurations": {
"production": {
"aot": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true
},
"pl": {
"aot": true,
"outputPath": "dist/pl/",
"i18nFile": "src/locale/messages.pl.xlf",
"i18nFormat": "xlf",
"i18nLocale": "pl"
},
"es": {
"aot": true,
"outputPath": "dist/es/",
"i18nFile": "src/locale/messages.es.xlf",
"i18nFormat": "xlf",
"i18nLocale": "es"
},
"fr": {
"aot": true,
"outputPath": "dist/fr/",
"i18nFile": "src/locale/messages.fr.xlf",
"i18nFormat": "xlf",
"i18nLocale": "fr"
}
}
}
}
}
}
}
Usage Examples
Basic Text Translation
// app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<h1 i18n="@@homepage.title">Welcome to our website</h1>
<p i18n="@@homepage.description">This is a multilingual Angular application</p>
`
})
export class AppComponent {
title = 'my-angular-app';
}
Component with Variables
// product.component.ts
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-product',
template: `
<div class="product-card">
<h3>{{ product.name }}</h3>
<p i18n="@@product.price">Price: {{ product.price | currency:'USD':'symbol':'1.2-2' }}</p>
<p i18n="@@product.available">Available since: {{ product.availableDate | date:'mediumDate' }}</p>
<p i18n="@@product.count" [attr.data-count]="product.count">
You have {{ product.count }} {product.count, plural, =0 {items} =1 {item} other {items}} in your cart
</p>
</div>
`
})
export class ProductComponent {
@Input() product = {
name: 'Sample Product',
price: 29.99,
availableDate: new Date(),
count: 0
};
}
Navigation Component
// navigation.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-navigation',
template: `
<nav>
<a routerLink="/" i18n="@@navigation.home">Home</a>
<a routerLink="/about" i18n="@@navigation.about">About</a>
<a routerLink="/contact" i18n="@@navigation.contact">Contact</a>
</nav>
`
})
export class NavigationComponent {}
Language Switcher
// language-switcher.component.ts
import { Component, Inject, LOCALE_ID } from '@angular/core';
import { DOCUMENT } from '@angular/common';
@Component({
selector: 'app-language-switcher',
template: `
<div class="language-switcher">
<button *ngFor="let locale of locales"
[class.active]="locale.code === currentLocale"
(click)="switchLanguage(locale.code)">
{{ locale.name }}
</button>
</div>
`
})
export class LanguageSwitcherComponent {
currentLocale: string;
locales = [
{ code: 'en-US', name: 'English' },
{ code: 'pl', name: 'Polski' },
{ code: 'es', name: 'Español' },
{ code: 'fr', name: 'Français' }
];
constructor(@Inject(LOCALE_ID) locale: string, @Inject(DOCUMENT) private document: Document) {
this.currentLocale = locale;
}
switchLanguage(locale: string) {
const url = this.document.location.origin;
this.document.location.href = `${url}/${locale}`;
}
}
Service with Translations
// notification.service.ts
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class NotificationService {
getWelcomeMessage(userName: string): string {
return $localize`:@@welcome.message:Welcome back, ${userName}!`;
}
getItemCountMessage(count: number): string {
return $localize`:@@cart.items:You have ${count}:INTERPOLATION: {count, plural, =0 {items} =1 {item} other {items}} in your cart`;
}
}
Pipe with Localization
// relative-time.pipe.ts
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'relativeTime'
})
export class RelativeTimePipe implements PipeTransform {
transform(date: Date): string {
const now = new Date();
const diffInSeconds = Math.floor((now.getTime() - date.getTime()) / 1000);
if (diffInSeconds < 60) {
return $localize`:@@time.justNow:Just now`;
} else if (diffInSeconds < 3600) {
const minutes = Math.floor(diffInSeconds / 60);
return $localize`:@@time.minutesAgo:${minutes}:INTERPOLATION: minutes ago`;
} else if (diffInSeconds < 86400) {
const hours = Math.floor(diffInSeconds / 3600);
return $localize`:@@time.hoursAgo:${hours}:INTERPOLATION: hours ago`;
} else {
const days = Math.floor(diffInSeconds / 86400);
return $localize`:@@time.daysAgo:${days}:INTERPOLATION: days ago`;
}
}
}
Translation Extraction and Build
Extract translatable text from your application:
# Extract messages to XLIFF format
ng extract-i18n
# Build for specific locale
ng build --configuration=pl
# Build for all locales
ng build --localize
SimpleLocalize Configuration
To manage translations with SimpleLocalize, you need to set up the CLI tool.
# macOS / Linux / Windows (WSL)
curl -s https://get.simplelocalize.io/2.9/install | bash
# Windows (PowerShell)
. { iwr -useb https://get.simplelocalize.io/2.9/install-windows } | iex;
# npm
npm install @simplelocalize/cli
# macOS / Linux / Windows (WSL)
curl -s https://get.simplelocalize.io/2.9/install | bash
# Windows (PowerShell)
. { iwr -useb https://get.simplelocalize.io/2.9/install-windows } | iex;
# npm
npm install @simplelocalize/cli
Create simplelocalize.yml
in your project root:
apiKey: YOUR_PROJECT_API_KEY
uploadFormat: xliff
uploadPath: ./src/locale/messages.xlf
uploadLanguageKey: en
uploadOptions:
- REPLACE_TRANSLATION_IF_FOUND
downloadPath: ./src/locale/messages.{lang}.xlf
downloadLanguageKeys: ['pl', 'es', 'fr']
downloadFormat: xliff
Work with the CLI to manage your translations:
# Extract translatable text
ng extract-i18n
# Upload extracted messages to SimpleLocalize
simplelocalize upload
# Download translations for all languages
simplelocalize download
# Build for all locales
ng build --localize
Deployment Strategy
Multiple Build Approach
# Build for all locales (creates separate bundles)
ng build --localize
# Deploy each locale to different paths
# dist/en-US/ -> /
# dist/pl/ -> /pl/
# dist/es/ -> /es/
# dist/fr/ -> /fr/
Server Configuration (nginx example)
server {
listen 80;
server_name example.com;
# Default to English
location / {
root /var/www/dist/en-US;
try_files $uri $uri/ /index.html;
}
# Polish version
location /pl/ {
alias /var/www/dist/pl/;
try_files $uri $uri/ /pl/index.html;
}
# Spanish version
location /es/ {
alias /var/www/dist/es/;
try_files $uri $uri/ /es/index.html;
}
# French version
location /fr/ {
alias /var/www/dist/fr/;
try_files $uri $uri/ /fr/index.html;
}
}
Key Features
- Official Angular Solution: Built and maintained by the Angular team
- Compile-time Optimization: Translations are processed at build time for optimal performance
- AOT Compilation: Full Ahead-of-Time compilation support
- Tree Shaking: Unused translations are removed from the bundle
- ICU Message Format: Support for pluralization, gender, and complex formatting
- Separate Bundles: Each locale gets its own optimized bundle
Best Practices
1. Use Meaningful IDs
// Good
<p i18n="@@user.welcome.message">Welcome, {{ userName }}!</p>
// Avoid
<p i18n>Welcome, {{ userName }}!</p>
2. Provide Context
// Include description and meaning
<button i18n="@@button.save|Save button in the form">Save</button>
3. Handle Pluralization
// Use ICU expressions for plurals
<span i18n="@@item.count">
{count, plural, =0 {no items} =1 {one item} other {{{count}} items}}
</span>
Resources
- Angular i18n Documentation
- Angular CLI i18n Commands
- ICU Message Format
- SimpleLocalize CLI Documentation
Was this helpful?