Implémentez un système i18n robuste dans Angular : extraction, traduction, gestion des dates/devise et workflow équipe.
Configuration i18n Angular
Angular intègre un système i18n natif basé sur les fichiers XLIFF ou XMB. Contrairement à des librairies tierces, il produit un bundle distinct par locale au build — zéro overhead runtime.
# angular.json — ajouter les locales cibles
{
"i18n": {
"sourceLocale": "fr",
"locales": {
"en": {
"translation": "src/locale/messages.en.xlf",
"baseHref": "/en/"
},
"es": {
"translation": "src/locale/messages.es.xlf",
"baseHref": "/es/"
}
}
}
}
Enregistrer les données de locale pour les pipes (date, number, currency) :
// main.ts
import { registerLocaleData } from '@angular/common';
import localeFr from '@angular/common/locales/fr';
import localeEn from '@angular/common/locales/en';
registerLocaleData(localeFr);
registerLocaleData(localeEn);
ng build --localize génère un dossier distinct (dist/fr/, dist/en/…) pour chaque locale. Chaque bundle est optimal car les traductions sont inlinées à la compilation.
Marquage des messages dans les templates
Angular utilise l'attribut i18n pour marquer les textes à traduire dans les templates HTML, et la fonction $localize pour le code TypeScript.
<!-- Template — texte simple -->
<h1 i18n="@@page.title">Bienvenue sur notre application</h1>
<!-- Texte avec variable -->
<p i18n="@@greeting">Bonjour {{ userName }}, vous avez {{ count }} messages.</p>
<!-- Pluriel avec ICU expression -->
<p i18n="@@messages.count">
{count, plural,
=0 { Aucun message }
=1 { Un message }
other { {{ count }} messages }
}
</p>
<!-- Attribut HTML -->
<img src="logo.png" i18n-alt="@@logo.alt" alt="Logo de l'application">
Dans le code TypeScript :
import { Component } from '@angular/core';
@Component({ ... })
export class MonComponent {
readonly titre = $localize`:@@app.title:Tableau de bord`;
readonly erreur = $localize`:@@error.required:Ce champ est obligatoire`;
}
Convention de nommage des IDs
- Utilise des IDs explicites (
@@page.title) plutôt que les IDs générés automatiquement — ils changent si le texte source est modifié. - Structure les IDs par feature :
@@auth.login.title,@@dashboard.header…
Extraction et workflow de traduction
La commande ng extract-i18n scanne l'application et génère le fichier source de messages.
# Générer le fichier source XLIFF 2.0
ng extract-i18n --format xliff2 --out-file src/locale/messages.xlf
# Structure du fichier généré
<unit id="page.title">
<segment>
<source>Bienvenue sur notre application</source>
</segment>
</unit>
Workflow recommandé avec une équipe de traduction :
- Le développeur marque les messages avec
i18net exécuteng extract-i18n. - Le fichier
messages.xlfest envoyé au traducteur (ou service CAT comme Phrase, Lokalise). - Les fichiers traduits (
messages.en.xlf,messages.es.xlf) sont intégrés danssrc/locale/. - Le CI lance
ng build --localizepour générer tous les bundles.
Locale data : dates, nombres, devises
Les pipes Angular (date, number, currency, percent) utilisent les données de locale pour formater correctement selon la région.
<!-- Date localisée -->
<p>{{ today | date:'longDate' }}</p>
<!-- fr: 13 avril 2026 | en: April 13, 2026 -->
<!-- Nombre avec séparateurs locaux -->
<p>{{ 1234567.89 | number:'1.2-2' }}</p>
<!-- fr: 1 234 567,89 | en: 1,234,567.89 -->
<!-- Devise -->
<p>{{ prix | currency:'EUR':'symbol':'1.2-2' }}</p>
<!-- fr: 29,99 € | en: €29.99 -->
LOCALE_ID pour lire la locale active dans le code TypeScript : const locale = inject(LOCALE_ID);. Utile pour les logiques de tri ou de formatage manuel.
Chargement lazy des traductions
Si le build multi-locale n'est pas adapté (app SaaS avec switcheur de langue dynamique), la librairie @ngx-translate/core offre un chargement lazy à la demande.
// Installation
// npm install @ngx-translate/core @ngx-translate/http-loader
// app.config.ts
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
import { HttpClient } from '@angular/common/http';
export function createTranslateLoader(http: HttpClient) {
return new TranslateHttpLoader(http, './assets/i18n/', '.json');
}
export const appConfig: ApplicationConfig = {
providers: [
importProvidersFrom(
TranslateModule.forRoot({
defaultLanguage: 'fr',
loader: {
provide: TranslateLoader,
useFactory: createTranslateLoader,
deps: [HttpClient]
}
})
)
]
};
Utilisation dans les templates :
<!-- pipe translate -->
<h1>{{ 'PAGE.TITLE' | translate }}</h1>
<!-- Changement de langue à la volée -->
export class HeaderComponent {
private translate = inject(TranslateService);
switchLang(lang: string): void {
this.translate.use(lang);
}
}
Checklist i18n
- Définir les IDs de messages explicites (
@@) dès le début — ne pas faire confiance aux IDs générés automatiquement. - Enregistrer les données de locale (
registerLocaleData) pour tous les pipes de formatage. - Automatiser l'extraction (
ng extract-i18n) dans la CI pour détecter les nouveaux messages non traduits. - Tester l'interface dans au moins deux locales avec des textes longs (allemand, russe) pour détecter les débordements CSS.
- Gérer les pluriels avec les expressions ICU — ne pas concatener des chaînes avec des conditions.
- Prévoir le sens de lecture RTL (arabe, hébreu) si ces locales sont dans la roadmap —
dir="rtl"sur<html>. - Versionner les fichiers de traduction avec le code source — un commit = un état cohérent app + traductions.
- Pour les URLs, utiliser
baseHrefpar locale (/en/,/es/) plutôt qu'un paramètre query string.