Table of Contents
- Prerequisites
- What is Angular i18n?
- Setting Up Angular i18n
- Marking Text for Translation
- Extracting Translations
- Managing Translation Files
- Configuring Angular for Multiple Locales
- Runtime vs. Compile-Time Internationalization
- Advanced Translation Scenarios
- Handling Right-to-Left (RTL) Languages
- Testing Internationalized Apps
- Best Practices for Angular i18n
- Conclusion
- References
Prerequisites
Before diving in, ensure you have:
- Node.js (v14+ recommended) and npm installed.
- Angular CLI (v12+ recommended): Install with
npm install -g @angular/cli. - A basic Angular app (new or existing). Create one with
ng new my-i18n-appif starting fresh.
What is Angular i18n?
Angular i18n is a framework-native solution for adapting apps to global audiences. It uses the XLIFF (XML Localization Interchange File Format) standard for translation files and supports:
- Translation of static text in templates.
- Locale-specific formatting (dates, numbers, currencies).
- Pluralization, gender, and dynamic content via ICU (International Components for Unicode) expressions.
- Compile-time or runtime language switching.
Angular’s i18n tooling is built on @angular/localize, a library that handles translation extraction, compilation, and rendering.
Setting Up Angular i18n
Angular’s i18n is enabled by default in new apps, but verify you have the required dependencies:
-
Ensure
@angular/localizeis installed:
Most Angular CLI apps include this, but if not, install it with:npm install @angular/localize --save -
Import
@angular/localize/init(for runtime use, optional):
If you need runtime translation (e.g., dynamic language switching), add this import to yourpolyfills.ts:import '@angular/localize/init';
Marking Text for Translation
The first step is to identify text in your templates that needs translation using the i18n attribute.
Basic Text Translation
Add the i18n attribute to any HTML element with static text:
<!-- src/app/app.component.html -->
<h1 i18n>Welcome to My App</h1>
<p i18n>This is a sample paragraph that will be translated.</p>
Adding Context for Translators
To help translators, add a description and meaning to the i18n attribute (format: i18n="meaning|description@@id"):
meaning: Distinguishes identical text with different contexts (e.g., “table” as furniture vs. data table).description: Notes for translators (e.g., “Greeting on the homepage”).id: A unique identifier (optional, auto-generated if omitted).
Example:
<button i18n="User dashboard|Button to submit form@@submitButton">Submit</button>
Inline Text vs. Attributes
Translate element attributes (e.g., alt, title) using i18n-attribute:
<img src="logo.png" i18n-alt="Company logo@@logoAlt" alt="Company Logo">
Extracting Translations
Once text is marked, extract it into a source translation file using the Angular CLI:
-
Run the extraction command:
ng extract-i18n --output-path src/localeThis generates a
messages.xlffile insrc/locale/. The--output-pathflag specifies where to save the file. -
Understanding the XLIFF File:
messages.xlfis the source locale file (default:en-US). It contains<trans-unit>elements for each marked text, with:<source>: The original text (e.g., “Welcome to My App”).<target>: Empty initially (to be filled by translators).id: The unique identifier (auto-generated or your custom@@id).
Sample
messages.xlf:<?xml version="1.0" encoding="UTF-8"?> <xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2"> <file source-language="en" datatype="plaintext" original="ng2.template"> <body> <trans-unit id="welcomeHeader" datatype="html"> <source>Welcome to My App</source> <context-group purpose="location"> <context context-type="sourcefile">src/app/app.component.html</context> <context context-type="linenumber">1</context> </context-group> </trans-unit> </body> </file> </xliff>
Managing Translation Files
For each target locale (e.g., French, Spanish), create a copy of messages.xlf and translate the <target> elements.
Step 1: Create Locale-Specific Files
Duplicate messages.xlf for each locale (e.g., messages.fr.xlf for French, messages.es.xlf for Spanish) in src/locale/.
Step 2: Translate the Text
Edit the <target> elements in each locale file. For messages.fr.xlf:
<trans-unit id="welcomeHeader" datatype="html">
<source>Welcome to My App</source>
<target>Bienvenue dans mon application</target> <!-- French translation -->
</trans-unit>
Tools for Translation
Manually editing XLIFF files is error-prone. Use tools to simplify:
- Poedit: Open-source XLIFF editor (supports translation memory).
- XLIFF Translator: Online tool (https://xlifftool.com/).
- Angular i18n Editor: VS Code extension for editing XLIFF files.
Configuring Angular for Multiple Locales
Update your app to build separate bundles for each locale by modifying angular.json.
Step 1: Update angular.json
Add a locales section under your project’s i18n options. Define:
sourceLocale: Your app’s default language (e.g.,en-US).locales: A map of locale codes to their translation files.
Example angular.json snippet:
{
"projects": {
"my-i18n-app": {
"i18n": {
"sourceLocale": "en-US",
"locales": {
"fr": "src/locale/messages.fr.xlf", // French
"es": "src/locale/messages.es.xlf" // Spanish
}
},
"architect": {
"build": {
"configurations": {
"fr": {
"localize": ["fr"],
"outputPath": "dist/my-i18n-app/fr" // Output for French
},
"es": {
"localize": ["es"],
"outputPath": "dist/my-i18n-app/es" // Output for Spanish
}
}
}
}
}
}
}
Step 2: Build for a Specific Locale
Build your app for French with:
ng build --configuration=fr
This generates a French-only bundle in dist/my-i18n-app/fr. Serve it locally with:
npx serve dist/my-i18n-app/fr
Runtime vs. Compile-Time Internationalization
Angular supports two i18n approaches:
Compile-Time (Default)
- How it works: Generates separate bundles for each locale during build.
- Pros: Smaller bundle sizes, faster load times, better performance.
- Cons: No dynamic language switching (requires a page reload).
Use this for most apps (e.g., marketing sites, dashboards with fixed locales).
Runtime
- How it works: Loads translations dynamically at runtime, allowing in-app language switching.
- Pros: Switch languages without reloading; single bundle.
- Cons: Larger initial bundle, slightly slower rendering.
Enable runtime i18n with @angular/localize and $localize:
-
Load translations at runtime:
// src/app/app.component.ts import { loadTranslations } from '@angular/localize'; import frTranslations from '../locale/messages.fr.xlf'; loadTranslations(frTranslations); // Load French translations dynamically -
Switch languages on demand:
Use a service to load translations for a new locale and trigger a re-render.
Third-Party Libraries (Legacy)
Older apps may use ngx-translate (now deprecated in favor of Angular’s native i18n). Prefer @angular/localize for new projects.
Advanced Translation Scenarios
Pluralization with ICU Expressions
Use ICU syntax to handle plurals, gender, and dynamic content. Example:
<p i18n>
{count, plural,
=0 {No messages}
=1 {1 message}
other {{{count}} messages}
}
</p>
Here, count is a component property. Translators will see the ICU structure and adapt it to the target language (e.g., Arabic has dual forms).
Date, Number, and Currency Formatting
Angular’s built-in pipes support locale-specific formatting:
- DatePipe:
{{ today | date:'long':'':'fr' }}(French date: “12 août 2024”). - CurrencyPipe:
{{ price | currency:'EUR':'symbol':'':'fr' }}(French euro: “10,50 €”). - DecimalPipe:
{{ rating | number:'1.2-2':'es' }}(Spanish decimal: “4,50”).
Gender and Select Statements
Use select for gender or categories:
<p i18n>
{user.gender, select,
male {His name is {{user.name}}}
female {Her name is {{user.name}}}
other {Their name is {{user.name}}}
}
</p>
Handling Right-to-Left (RTL) Languages
Languages like Arabic, Hebrew, or Persian require RTL layout. Angular simplifies this:
1. Enable RTL in angular.json
Add an RTL configuration in angular.json:
"configurations": {
"ar-rtl": {
"localize": ["ar"],
"outputPath": "dist/my-i18n-app/ar",
"i18nMissingTranslation": "error",
"rtl": true // Enables RTL processing
}
}
2. Dynamically Set Direction
Add dir attribute to your root element and update it based on the locale:
<!-- src/index.html -->
<html [dir]="isRtl ? 'rtl' : 'ltr'">
In your component:
isRtl = this.localeService.currentLocale === 'ar'; // Check current locale
3. RTL Styling
Use Angular’s RTL-aware CSS mixins or :host-context for locale-specific styles:
/* src/app/app.component.scss */
:host-context([dir="rtl"]) .header {
padding-left: 0;
padding-right: 16px;
}
Testing Internationalized Apps
1. Unit Testing with Locales
Use Angular’s LOCALE_ID token to test components with specific locales:
// src/app/app.component.spec.ts
import { LOCALE_ID } from '@angular/core';
describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [AppComponent],
providers: [{ provide: LOCALE_ID, useValue: 'fr' }] // Test in French
}).compileComponents();
});
it('should render translated title', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('h1')?.textContent).toContain('Bienvenue dans mon application');
});
});
2. End-to-End Testing
Use Cypress or Playwright to test locale-specific builds:
// cypress/e2e/fr.spec.cy.ts
describe('French locale', () => {
it('displays French text', () => {
cy.visit('/fr'); // Visit the French build
cy.contains('Bienvenue dans mon application').should('exist');
});
});
Best Practices for Angular i18n
1. Keep Translations DRY
Reuse translations with unique ids to avoid duplication:
<!-- Reuse the same translation -->
<p i18n="@@common.greeting">Hello</p>
<button i18n="@@common.greeting">Hello</button>
2. Avoid Hardcoded Text
Never hardcode text in components or services. Use $localize for TypeScript strings:
// src/app/app.component.ts
import { $localize } from '@angular/localize';
title = $localize`:@@app.title:My i18n App`;
3. Automate Extraction
Add extraction to your CI/CD pipeline to catch untranslated text early:
ng extract-i18n --output-path src/locale --format xlf2
4. Optimize Bundle Size
- Use compile-time i18n for smaller bundles.
- Lazy-load non-critical locales.
5. Test Locale-Specific Formatting
Verify dates, numbers, and currencies render correctly for target locales (e.g., 1,000.50 in en-US vs 1 000,50 in fr-FR).
Conclusion
Internationalizing your Angular app unlocks global audiences and improves user experience. By following this guide, you’ve learned to:
- Mark text for translation with
i18n. - Extract and manage XLIFF translation files.
- Configure Angular for multiple locales.
- Handle advanced scenarios like pluralization and RTL.
Angular’s built-in i18n tools simplify the process, whether you need compile-time or runtime translation. Start small (translate key pages) and expand to full localization!