Angular i18n Explained — Internationalization and Localization
In this tutorial, you will learn about Angular i18n Explained. We cover key concepts, practical examples, and best practices to help you master this topic.
Angular i18n (Internationalization) lets you build applications that support multiple languages, regional formats, and cultural conventions using Angular's built-in translation pipeline.
What You'll Learn
- The difference between i18n and l10n
- How to mark text for translation in templates
- How to generate and manage translation files
- How to handle pluralization and dates
- How to build and deploy for multiple locales
Why It Matters
Internationalization opens your application to global audiences. A translated app increases user engagement in non-English markets and is often required for enterprise or government contracts.
Real-World Use
Durga Antivirus Pro supports 12 languages. The security dashboard, threat alerts, and settings panels automatically display in the user's preferred language based on browser settings or account preferences.
flowchart LR
A[Source Templates] --> B[Extract Messages]
B --> C[XLIFF / JSON Files]
C --> D[Translators]
D --> E[Translated Files]
E --> F[Build per Locale]
F --> G[en-US / es / fr / ja / ...]
style A fill:#f97316,color:#fff
Marking Text for Translation
Use the i18n attribute to mark translatable text:
import { Component } from "@angular/core";
@Component({
selector: "app-welcome",
standalone: true,
template: `
<h1 i18n="Welcome header|Greeting message@@welcomeHeader">
Welcome to our application!
</h1>
<p i18n="Intro paragraph@@introText">
This application helps you manage your tasks efficiently.
</p>
<p i18n>
This text has a simple marker without description or ID.
</p>
`
})
export class WelcomeComponent {}
Expected output: Angular extracts the marked text into translation files. The i18n attribute value can include a description, meaning, and unique ID.
The format is: i18n="{meaning}|{description}@@{id}". The @@id is an optional unique identifier. If omitted, Angular generates one from the text content.
Handling Interpolations
Translate text with dynamic values:
@Component({
selector: "app-user-greeting",
standalone: true,
template: `
<p i18n>
Hello {{ username }}! You have {{ taskCount }} tasks pending.
</p>
<!-- ICU Message Format for pluralization -->
<p i18n>
{taskCount, plural,
=0 {You have no tasks pending.}
=1 {You have one task pending.}
other {You have {{taskCount}} tasks pending.}
}
</p>
`
})
export class UserGreetingComponent {
username = "Alice";
taskCount = 3;
}
Expected output: "Hello Alice! You have 3 tasks pending." In plural form: "You have 3 tasks pending." If taskCount were 1: "You have one task pending."
Angular uses ICU Message Format for pluralization and gender rules. Translators get the full ICU expression to adapt to their language's plural rules (some languages have 2, 3, or 4 plural forms).
Generating Translation Files
Extract messages and create translation files:
# Extract messages from templates
ng extract-i18n --output-path src/locale
# This generates messages.xlf in src/locale/
Expected output: An XLIFF (.xlf) file containing all translatable texts from templates.
The XLIFF file looks like:
<trans-unit id="welcomeHeader" datatype="html">
<source>Welcome to our application!</source>
<target state="new">Welcome to our application!</target>
<note priority="1" from="description">Greeting message</note>
<note priority="1" from="meaning">Welcome header</note>
</trans-unit>
Translators edit the <target> elements to provide translations.
Building for Different Locales
Configure and build for multiple languages:
// angular.json (partial)
{
"projects": {
"my-app": {
"i18n": {
"sourceLocale": "en-US",
"locales": {
"es": { "translation": "src/locale/messages.es.xlf" },
"fr": { "translation": "src/locale/messages.fr.xlf" },
"ja": { "translation": "src/locale/messages.ja.xlf" }
}
},
"architect": {
"build": {
"configurations": {
"production": {
"localize": true
}
}
}
}
}
}
}
Build all locales:
ng build --localize
Expected output: Angular creates separate output directories for each locale: dist/my-app/en-US/, dist/my-app/es/, dist/my-app/fr/, etc.
Each locale build has the translated templates and the correct locale data (date/number formats). You deploy these separately or use a CDN to serve the appropriate locale based on the user's language.
Locale-Specific Pipes
Dates, numbers, and currencies adapt to locale:
import { Component, Inject, LOCALE_ID } from "@angular/core";
import { CommonModule } from "@angular/common";
@Component({
selector: "app-locale-demo",
standalone: true,
imports: [CommonModule],
template: `
<p>Date: {{ today | date:"fullDate" }}</p>
<p>Number: {{ value | number:"1.2-2" }}</p>
<p>Currency: {{ price | currency }}</p>
<p>Percent: {{ percent | percent }}</p>
<p>Current locale: {{ currentLocale }}</p>
`
})
export class LocaleDemoComponent {
today = new Date();
value = 1234567.8912;
price = 99.99;
percent = 0.758;
currentLocale = "";
constructor(@Inject(LOCALE_ID) locale: string) {
this.currentLocale = locale;
}
}
Expected output: In the Spanish locale, the date appears as "domingo, 28 de junio de 2026". Currency shows as "99,99 EUR" with European formatting.
Angular ships locale data for over 120 locales. Register additional locales in angular.json or import them manually.
Runtime Locale Switching
For dynamic locale switching without reload:
import { Injectable } from "@angular/core";
import { BehaviorSubject } from "rxjs";
@Injectable({ providedIn: "root" })
export class LocaleService {
private currentLocale = new BehaviorSubject<string>("en-US");
locale$ = this.currentLocale.asObservable();
setLocale(locale: string) {
this.currentLocale.next(locale);
localStorage.setItem("preferred_locale", locale);
}
getLocale(): string {
return this.currentLocale.value;
}
}
For true runtime switching without rebuild, use the $localize tag with lazy-loaded translation files. More commonly, apps use the built locale Strategy where each locale is a separate deploy.
Common Mistakes
Forgetting to extract translations — New texts added after the initial extraction are not translated until you re-run
ng extract-i18n.Missing ICU plural rules — English has 3 plural forms (0, 1, other). Other languages have different rules. Always use ICU format, not
if/elselogic.Not translating dynamic texts in TypeScript —
i18nonly works in templates. Use$localizetag for TypeScript strings:$localize`Hello ${name}`.Assuming translated text fits the UI — German and Russian text can be 30-50% longer than English. Design flexible layouts that accommodate variable text lengths.
Translating technical terms — Some terms like "API", "GET", "POST", or brand names should not be translated. Mark these with
i18nbut provide translator notes.
Practice Questions
What attribute marks text for translation in Angular?
i18nattribute in the template. For example:<h1 i18n>Hello</h1>.What is ICU Message Format? A standard syntax for handling pluralization, gender, and select rules in translations.
How do you generate translation files? Run
ng extract-i18nto extract alli18n-marked texts into an XLIFF file.What does
--localizedo when building? It builds separate bundles for each configured locale with translated templates and locale data.How do you access the current locale in code? Inject
LOCALE_IDtoken:constructor(@Inject(LOCALE_ID) public locale: string).
Challenge
Build a MultiLangCheckoutComponent with: product name, price, quantity, total, "Add to Cart" button, and a summary section. Mark all user-facing text for translation. Use ICU pluralization for cart item count. Extract the XLIFF file. Translate to Spanish and French (even if using Google Translate). Build and verify both locale outputs.
FAQ
Mini Project
Create an Internationalized DashboardComponent that displays: a welcome message with user name, today's date (full format), currency amounts (user's salary), percentage (completion rate), and a notification count with pluralization. Configure 3 locales: en-US, es (Spanish), and de (German). Extract translations, provide translated XLF files (use Google Translate if needed), build with --localize, and verify the output structure has separate directories for each locale.
What's Next
Continue with state management with NgRx:
Angular State Management, Angular Signals, Angular Standalone
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro