Philosophie i18n d'Angular
Vous construisez une application Angular. Maintenant, vous devez prendre en charge plusieurs langues.
Vous avez deux choix :
-
@angular/localize (solution officielle Angular)
- Traduction au moment de la compilation (build différent par langue)
- Performances d'exécution les plus rapides
- Plus de complexité de build
-
ngx-translate (bibliothèque communautaire)
- Traduction à l'exécution (build unique, changement dynamique)
- Déploiement plus simple
- Exécution légèrement plus lente
Ce guide couvre @angular/localize (recommandé par l'équipe Angular). Si vous avez besoin de changer de langue dynamiquement dans la même instance d'application, utilisez ngx-translate à la place.
Ce que vous allez construire
À la fin de ce tutoriel, vous aurez une application Angular qui :
- ✅ Compile des bundles séparés par langue
- ✅ Utilise les API i18n natives d'Angular
- ✅ Gère la pluralisation correctement (Anglais, Espagnol, Polonais, Arabe, etc.)
- ✅ Formate les dates, nombres, devises par locale
- ✅ Se déploie avec un routage basé sur la locale (
/en/,/es/,/fr/) - ✅ S'intègre avec CI/CD pour une traduction automatisée
Stack technique :
- Angular 17+
- @angular/localize
- Angular CLI
- TypeScript
Étape 1 : Installer @angular/localize
Si vous commencez un nouveau projet Angular :
Terminalng new my-app cd my-app ng add @angular/localize
Cela ajoute @angular/localize à votre projet et configure angular.json.
Pour les projets existants :
Terminalng add @angular/localize
Ce que cela fait :
- Installe le paquet
@angular/localize - Ajoute le polyfill à
polyfills.ts - Met à jour
angular.jsonavec la configuration i18n
Étape 2 : Marquer les chaînes pour la traduction
Traduction de base (attribut i18n)
Utilisez l'attribut i18n sur n'importe quel élément HTML :
HTML1<!-- Avant : Anglais codé en dur --> 2<h1>Welcome to our app</h1> 3<button>Sign up</button> 4 5<!-- Après : Marqué pour la traduction --> 6<h1 i18n>Welcome to our app</h1> 7<button i18n>Sign up</button>
C'est tout. Angular extraira ces chaînes pendant le build.
Attributs (i18n-*)
Traduisez les attributs des éléments :
HTML1<!-- Traduire le placeholder --> 2<input 3 type="text" 4 placeholder="Enter your email" 5 i18n-placeholder 6/> 7 8<!-- Traduire le title --> 9<img 10 src="logo.png" 11 title="Company logo" 12 i18n-title 13/> 14 15<!-- Traduire aria-label pour l'accessibilité --> 16<button 17 aria-label="Close dialog" 18 i18n-aria-label 19> 20 ✕ 21</button>
TypeScript du Composant (Template Literals)
Pour les chaînes dans TypeScript, utilisez $localize :
TypeScript1import { Component } from '@angular/core'; 2 3@Component({ 4 selector: 'app-root', 5 template: ` 6 <h1>{{ title }}</h1> 7 `, 8}) 9export class AppComponent { 10 // Marquer la chaîne pour la traduction 11 title = $localize`Welcome to our app`; 12 13 showMessage() { 14 // Peut être utilisé dans le code 15 alert($localize`Settings saved successfully!`); 16 } 17}
Note : $localize est au moment de la compilation, donc il est remplacé au moment du build par la chaîne traduite.
Ajout de Contexte (Sens & Description)
Aidez les traducteurs à comprendre le contexte :
HTML1<!-- Ajouter sens et description --> 2<h1 i18n="site header|Welcome message for homepage@@homeTitle"> 3 Welcome to our app 4</h1> 5 6<!-- Format : [sens]|[description]@@[id] -->
- Sens : Catégorie ou contexte ("site header")
- Description : Explication ("Welcome message for homepage")
- ID : Identifiant unique (@@homeTitle). Recommandé pour le suivi
Pourquoi utiliser des ID ?
- Empêche les extractions en double
- Permet de changer le texte source sans casser les traductions
- Rend les mises à jour plus claires
Exemple avec ID :
HTML1<button i18n="@@signupButton">Sign up</button> 2 3<!-- Plus tard, vous changez le texte : --> 4<button i18n="@@signupButton">Create account</button>
Les traducteurs voient une mise à jour de signupButton, pas une nouvelle chaîne.
Étape 3 : Extraire les Chaînes de Traduction
Exécutez l'outil d'extraction d'Angular :
Terminalng extract-i18n --output-path src/locale
Cela crée :
src/locale/
└── messages.xlf (ou .json, .xmb selon le format)
Le format par défaut est XLIFF (.xlf). Il ressemble à :
XML1<?xml version="1.0" encoding="UTF-8" ?> 2<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2"> 3 <file source-language="en" datatype="plaintext"> 4 <body> 5 <trans-unit id="homeTitle" datatype="html"> 6 <source>Welcome to our app</source> 7 <context-group purpose="location"> 8 <context context-type="sourcefile">src/app/app.component.html</context> 9 </context-group> 10 <note priority="1" from="description">Welcome message for homepage</note> 11 <note priority="1" from="meaning">site header</note> 12 </trans-unit> 13 </body> 14 </file> 15</xliff>
Formats Alternatifs
JSON (plus facile à lire) :
Terminalng extract-i18n --format json
Sortie :
JSON1{ 2 "locale": "en", 3 "translations": { 4 "homeTitle": { 5 "translation": "Welcome to our app", 6 "description": "Welcome message for homepage" 7 } 8 } 9}
Nous recommandons JSON pour une édition humaine plus facile et une intégration TMS.
Étape 4 : Traduire les Fichiers Extraits
Traduction Manuelle (Petits Projets)
Copiez le fichier de base pour chaque langue :
Terminalcp src/locale/messages.xlf src/locale/messages.es.xlf cp src/locale/messages.xlf src/locale/messages.fr.xlf
Éditez messages.es.xlf :
XML1<trans-unit id="homeTitle"> 2 <source>Welcome to our app</source> 3 <target>Bienvenido a nuestra aplicación</target> 4</trans-unit>
Pour le format JSON :
JSON1{ 2 "locale": "es", 3 "translations": { 4 "homeTitle": { 5 "translation": "Bienvenido a nuestra aplicación" 6 } 7 } 8}
Traduction Automatisée (IntlPull)
Pour les applications en production, utilisez un TMS :
Terminalnpm install -D @intlpullhq/cli
Configurer (.intlpull.json) :
JSON1{ 2 "projectId": "proj_abc123", 3 "sourceLanguage": "en", 4 "targetLanguages": ["es", "fr", "de"], 5 "format": "xlf", 6 "outputDir": "src/locale" 7}
Workflow :
Terminal1# 1. Extraire 2ng extract-i18n --output-path src/locale 3 4# 2. Uploader vers IntlPull pour traduction 5npx @intlpullhq/cli upload 6 7# 3. Télécharger les traductions 8npx @intlpullhq/cli download 9 10# 4. Construire avec traductions 11ng build --localize
Résultat : Pipeline de traduction automatisé dans CI/CD.
Étape 5 : Configurer angular.json pour Plusieurs Locales
Éditez angular.json :
JSON1{ 2 "projects": { 3 "my-app": { 4 "i18n": { 5 "sourceLocale": "en", 6 "locales": { 7 "es": { 8 "translation": "src/locale/messages.es.xlf", 9 "baseHref": "/es/" 10 }, 11 "fr": { 12 "translation": "src/locale/messages.fr.xlf", 13 "baseHref": "/fr/" 14 }, 15 "de": { 16 "translation": "src/locale/messages.de.xlf", 17 "baseHref": "/de/" 18 } 19 } 20 }, 21 "architect": { 22 "build": { 23 "configurations": { 24 "production": { 25 "localize": true 26 }, 27 "es": { 28 "localize": ["es"] 29 }, 30 "fr": { 31 "localize": ["fr"] 32 } 33 } 34 }, 35 "serve": { 36 "configurations": { 37 "es": { 38 "browserTarget": "my-app:build:development,es" 39 }, 40 "fr": { 41 "browserTarget": "my-app:build:development,fr" 42 } 43 } 44 } 45 } 46 } 47 } 48}
Ce que cela fait :
- Définit les locales prises en charge
- Définit baseHref pour chacune (URL :
/en/,/es/,/fr/) - Configure le build pour compiler des bundles séparés
Étape 6 : Construire et Servir
Développement (Locale Unique)
Exécutez l'application dans une locale spécifique :
Terminalng serve --configuration=es
Ouvre l'App en espagnol à http://localhost:4200/
Production (Toutes les Locales)
Construire toutes les locales :
Terminalng build --localize
Sortie :
dist/my-app/
├── en/ (Build anglais)
│ ├── index.html
│ ├── main.js
│ └── ...
├── es/ (Build espagnol)
│ ├── index.html
│ ├── main.js
│ └── ...
├── fr/ (Build français)
│ └── ...
Chaque dossier est un build d'application complet, traduit.
Déployer
Hébergement statique (Vercel, Netlify, Firebase) :
Déployez le dossier dist/my-app/. Routes serveur :
/en/*→dist/my-app/en//es/*→dist/my-app/es//fr/*→dist/my-app/fr/
Exemple Nginx :
NGINX1server { 2 listen 80; 3 4 location /en/ { 5 alias /var/www/my-app/en/; 6 try_files $uri $uri/ /en/index.html; 7 } 8 9 location /es/ { 10 alias /var/www/my-app/es/; 11 try_files $uri $uri/ /es/index.html; 12 } 13 14 location /fr/ { 15 alias /var/www/my-app/fr/; 16 try_files $uri $uri/ /fr/index.html; 17 } 18 19 # Rediriger la racine vers la langue par défaut 20 location = / { 21 return 302 /en/; 22 } 23}
Étape 7 : Pluralisation & Messages ICU
Pluralisation
Les différentes langues ont des règles de pluriel différentes :
- Anglais : 1 élément, 2 éléments (2 formes)
- Espagnol : pareil (2 formes)
- Polonais : 1, 2-4, 5+ (3 formes)
- Arabe : 0, 1, 2, 3-10, 11-99, 100+ (6 formes !)
La syntaxe ICU d'Angular gère cela :
HTML1<span i18n> 2 {count, plural, 3 =0 {No items} 4 =1 {One item} 5 other {{{count}} items} 6 } 7</span>
Dans le composant :
TypeScriptexport class CartComponent { count = 5; }
Sortie :
count = 0: "No items"count = 1: "One item"count = 5: "5 items"
Traduction (Espagnol) :
XML1<trans-unit id="..."> 2 <source>{count, plural, =0 {No items} =1 {One item} other {{{count}} items}}</source> 3 <target>{count, plural, =0 {Sin artículos} =1 {Un artículo} other {{{count}} artículos}}</target> 4</trans-unit>
Genre (Select)
HTML1<span i18n> 2 {gender, select, 3 male {He liked your post} 4 female {She liked your post} 5 other {They liked your post} 6 } 7</span>
Composant :
TypeScriptexport class NotificationComponent { gender = 'female'; // ou 'male', 'other' }
Interpolation
Passer des variables dans les traductions :
HTML<p i18n> Hello {{ userName }}, you have {{ messageCount }} new messages. </p>
Traduction :
XML<source>Hello {{ userName }}, you have {{ messageCount }} new messages.</source> <target>Hola {{ userName }}, tienes {{ messageCount }} mensajes nuevos.</target>
Les variables ({{ userName }}) restent inchangées. Les traducteurs n'y touchent pas.
Étape 8 : Formater les Dates, Nombres, Devises
Angular fournit des pipes intégrés :
Formatage de Date
HTML<p>{{ releaseDate | date:'fullDate' }}</p>
Adaptation automatique à la locale :
- Anglais : "Monday, January 15, 2026"
- Espagnol : "lunes, 15 de enero de 2026"
- Allemand : "Montag, 15. Januar 2026"
Fournir la locale dans l'app :
TypeScript1import { LOCALE_ID } from '@angular/core'; 2import { registerLocaleData } from '@angular/common'; 3import localeEs from '@angular/common/locales/es'; 4import localeFr from '@angular/common/locales/fr'; 5 6registerLocaleData(localeEs); 7registerLocaleData(localeFr); 8 9@NgModule({ 10 providers: [ 11 { provide: LOCALE_ID, useValue: 'es' } // ou détecter dynamiquement 12 ] 13}) 14export class AppModule {}
Formatage de Nombre
HTML<p>{{ price | number:'1.2-2' }}</p>
Sortie :
- Anglais : "1,234.56"
- Allemand : "1.234,56"
- Français : "1 234,56"
Formatage de Devise
HTML<p>{{ price | currency:'USD' }}</p>
Sortie :
- Anglais : "$1,234.56"
- Espagnol (Espagne) : "1.234,56 US$"
Utiliser une devise spécifique à la locale :
HTML<p>{{ price | currency:currencyCode }}</p>
TypeScriptexport class ProductComponent { currencyCode = 'EUR'; // ou 'USD', 'GBP', etc. }
Étape 9 : Détection de Locale à l'Exécution
Problème : @angular/localize d'Angular est au moment de la compilation, donc vous devez servir le bon build.
Solution : Détecter la locale de l'utilisateur et rediriger vers le bon build.
Détecter depuis l'URL
L'utilisateur visite /es/products → charger le build espagnol.
C'est automatique si vous configurez baseHref dans angular.json.
Détecter depuis le Navigateur
Si l'utilisateur visite la racine /, rediriger en fonction de la langue du navigateur :
index.html :
HTML1<script> 2 // Détecter la langue du navigateur 3 const lang = navigator.language.split('-')[0]; // 'en', 'es', 'fr', etc. 4 const supportedLocales = ['en', 'es', 'fr', 'de']; 5 6 // Rediriger vers la bonne locale 7 if (supportedLocales.includes(lang)) { 8 window.location.href = `/${lang}/`; 9 } else { 10 window.location.href = '/en/'; // Default 11 } 12</script>
Détection Côté Serveur (Recommandé)
Utiliser le serveur pour rediriger en fonction de l'en-tête Accept-Language :
Exemple Nginx :
NGINX1location = / { 2 # Détecter la langue depuis l'en-tête Accept-Language 3 set $lang "en"; 4 if ($http_accept_language ~* "^es") { 5 set $lang "es"; 6 } 7 if ($http_accept_language ~* "^fr") { 8 set $lang "fr"; 9 } 10 11 return 302 /$lang/; 12}
Étape 10 : Sélecteur de Langue
Créez un composant sélecteur de langue :
TypeScript1import { Component } from '@angular/core'; 2 3@Component({ 4 selector: 'app-language-switcher', 5 template: ` 6 <select (change)="changeLanguage($event)"> 7 <option value="en">English</option> 8 <option value="es">Español</option> 9 <option value="fr">Français</option> 10 <option value="de">Deutsch</option> 11 </select> 12 `, 13}) 14export class LanguageSwitcherComponent { 15 changeLanguage(event: Event) { 16 const select = event.target as HTMLSelectElement; 17 const locale = select.value; 18 19 // Rediriger vers la nouvelle locale 20 const currentPath = window.location.pathname.split('/').slice(2).join('/'); 21 window.location.href = `/${locale}/${currentPath}`; 22 } 23}
Ce que cela fait :
- L'utilisateur sélectionne "Español"
- Redirige de
/en/productsvers/es/products - Angular sert le build espagnol
Avancé : Intégration CI/CD
Exemple GitHub Actions
.github/workflows/i18n-deploy.yml :
YAML1name: Build & Deploy with i18n 2 3on: 4 push: 5 branches: [main] 6 7jobs: 8 build-deploy: 9 runs-on: ubuntu-latest 10 steps: 11 - uses: actions/checkout@v3 12 13 - name: Setup Node 14 uses: actions/setup-node@v3 15 with: 16 node-version: '18' 17 18 - name: Install dependencies 19 run: npm ci 20 21 - name: Extract i18n strings 22 run: ng extract-i18n --output-path src/locale 23 24 - name: Upload to IntlPull 25 run: npx @intlpullhq/cli upload 26 env: 27 INTLPULL_API_KEY: ${{ secrets.INTLPULL_API_KEY }} 28 29 - name: Download translations 30 run: npx @intlpullhq/cli download 31 32 - name: Build all locales 33 run: ng build --localize --configuration production 34 35 - name: Deploy to Vercel 36 uses: amondnet/vercel-action@v20 37 with: 38 vercel-token: ${{ secrets.VERCEL_TOKEN }} 39 vercel-org-id: ${{ secrets.VERCEL_ORG_ID }} 40 vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }} 41 working-directory: ./dist/my-app
Résultat : Chaque commit déclenche la synchronisation des traductions + le déploiement.
