Centre d'Aide

Angular i18next

L'internationalisation (i18n) est essentielle pour construire des applications Angular globales. Ce guide vous montre comment implémenter i18next dans Angular avec un chargement de ressources approprié, une initialisation et des modèles d'utilisation.

Pourquoi i18next pour Angular ?

i18next est l'un des frameworks i18n les plus populaires, offrant des fonctionnalités puissantes et de la flexibilité :

  • Indépendant du framework - fonctionne sans problème avec Angular, React, Vue, et d'autres.
  • Interpolation puissante, pluralisation et support de contexte.
  • Support du chargement paresseux et de la séparation du code.
  • Support TypeScript avec sécurité de type.
  • Écosystème large avec plugins et extensions.

Installation

Installez i18next en utilisant npm ou yarn :

npm install i18next

C'est tout ! i18next est une bibliothèque autonome sans dépendances spécifiques au framework. Vous n'avez pas besoin d'angular-i18next ou d'un autre wrapper - utilisez simplement i18next directement dans votre application Angular.

Chargement des ressources de traduction

Il existe deux approches principales pour charger des ressources de traduction dans Angular avec i18next :

Imports dynamiques (recommandé)

L'utilisation des imports dynamiques exploite la séparation de code de Webpack pour charger les traductions à la demande. Cette approche est idéale pour les applications avec de nombreuses langues ou de grands fichiers de traduction.

Avantages :

  • Optimisation de la taille du bundle - ne chargez que les traductions nécessaires.
  • Meilleure performance - temps de chargement initial plus rapide.
  • Fonctionne parfaitement avec SSR et le pré-rendu.
  • Aucune requête HTTP supplémentaire requise.

Exemple : Chargement avec des imports dynamiques

private async loadLanguageResources(
  lang: string,
): Promise<Record<string, Record<string, unknown>>> {
  const languageResources: Record<string, Record<string, unknown>> = {};

  for (const namespace of NAMESPACES) {
    const translationModule = await this.loadJson(lang, namespace);
    languageResources[namespace] = translationModule.default;
  }
  return languageResources;
}

private async loadJson(
  language: string,
  namespace: string,
): Promise<{ default: Record<string, unknown> }> {
  // Webpack automatically code-splits this into separate chunks
  return import(`../../i18n/${language}/${namespace}.json`);
}

Cette approche importe des fichiers de traduction en tant que modules ES, permettant à Webpack de les diviser automatiquement en morceaux séparés qui sont chargés à la demande.

i18next-http-backend

Le plugin HTTP backend charge les traductions via des requêtes HTTP depuis un serveur ou un CDN. Cela est utile lorsque vous souhaitez mettre à jour les traductions sans reconstruire votre application.

Avantages :

  • Mettez à jour les traductions sans redéploiement.
  • Chargez les traductions depuis un CDN externe.
  • Utile pour la gestion des traductions à l'exécution.

Inconvénients :

  • Des requêtes HTTP supplémentaires ralentissent le chargement initial.
  • Nécessite une connectivité réseau.
  • Configuration SSR plus complexe.

Exemple : Chargement avec le backend HTTP

import HttpBackend from 'i18next-http-backend';

await init({
  lng: initialLanguage,
  fallbackLng: DEFAULT_LANGUAGE,
  backend: {
    loadPath: '/assets/i18n/{{lng}}/{{ns}}.json',
  },
  use: [HttpBackend],
  ns: NAMESPACES,
  defaultNS: DEFAULT_NAMESPACE,
});

Cette approche récupère des fichiers de traduction depuis un serveur à l'exécution. Notez que cela nécessite une configuration supplémentaire pour SSR.

Quelle approche choisir ?

Utilisez des imports dynamiques lorsque :

  • Vous voulez une taille de bundle et une performance optimales.
  • Les traductions font partie de votre processus de construction.
  • Vous avez besoin de support SSR/pré-rendu.

Utilisez le backend HTTP lorsque :

  • Vous devez mettre à jour les traductions sans redéploiement.
  • Les traductions sont gérées de manière externe.
  • Vous avez un CDN pour servir les fichiers de traduction.

Initialisation correcte d'i18next

Initialiser i18next correctement est crucial pour éviter les erreurs d'exécution et garantir que les traductions sont disponibles lorsque votre application se rend.

Utilisation de provideAppInitializer

La fonction provideAppInitializer d'Angular (introduite dans Angular 18) garantit qu'i18next est entièrement initialisé avant que l'application ne commence à se rendre. La fonction fournie est exécutée pendant le démarrage de l'application, et l'initialisation ne se termine pas tant que la promesse n'est pas résolue. Cela empêche les clés de traduction d'apparaître à la place du texte traduit.

Exemple : Configuration de l'initialiseur d'application

export const appConfig: ApplicationConfig = {
  providers: [
    // ... other providers
    provideAppInitializer(initializeI18n),
    // ... more providers
  ],
};

Pourquoi cela compte :

  • La fonction s'exécute dans le contexte d'injection pendant le démarrage de l'application.
  • Empêche le scintillement ou l'affichage des clés de traduction lors du chargement initial.
  • Garantit que les traductions sont disponibles dans tous les composants dès le départ.
  • Fonctionne correctement avec SSR, pré-rendu et rendu côté client.

Fonction d'initialisation d'i18next

La fonction d'initialisation gère le chargement des ressources et la configuration des paramètres d'i18next.

Exemple : Initialisation d'i18next

public async initializeI18n(): Promise<void> {
  const initialLanguage = this.getInitialLanguage();
  const resources: Record<string, Record<string, Record<string, unknown>>> = {};

  if (isPlatformBrowser(this.platformId)) {
    // Browser: Only load the specific language needed
    const languageResources = await this.loadLanguageResources(initialLanguage);
    resources[initialLanguage] = languageResources;
  } else {
    // SSR: Load all languages for prerendering
    for (const lang of SUPPORTED_LANGUAGES) {
      const languageResources = await this.loadLanguageResources(lang);
      resources[lang] = languageResources;
    }
  }

  await init({
    lng: initialLanguage,
    fallbackLng: DEFAULT_LANGUAGE,
    resources,
    ns: NAMESPACES,
    defaultNS: DEFAULT_NAMESPACE,
    interpolation: {
      escapeValue: false,
    },
  });
}

Points clés :

  • Chargez uniquement la langue actuelle dans le navigateur pour une performance optimale.
  • Chargez toutes les langues pendant le SSR pour un support de pré-rendu.
  • Utilisez fallbackLng pour gérer les traductions manquantes avec élégance.
  • Configurez les espaces de noms pour une meilleure organisation.

Pipe de traduction

Le pipe de traduction d'Angular fournit un moyen propre d'utiliser des traductions dans les modèles.

Implémentation du pipe

Exemple : Pipe de traduction

import { Pipe, PipeTransform } from '@angular/core';
import { t } from 'i18next';

@Pipe({
  name: 't',
  standalone: true,
  pure: false, // Need to update when language changes
})
export class TranslatePipe implements PipeTransform {
  transform(key: string, options?: Record<string, unknown>): string {
    return t(key, options);
  }
}

Le pipe est marqué comme pur : false pour s'assurer qu'il se met à jour lorsque la langue change. C'est important car les changements de langue ne modifient pas la clé de traduction elle-même.

Utiliser le Pipe de Traduction

Dans les Modèles :

<!-- With namespace -->
<p>{{ "common:welcomeMessage" | t }}</p>

<!-- With nested keys -->
<p>{{ "dashboard.title" | t }}</p>

<!-- Simple interpolation -->
<p>{{ "greeting" | t: { name: userName } }}</p>
<!-- Result: "Hello, John!" from greeting: "Hello, {{name}}!" -->

<!-- With Angular pipes -->
<p>{{ "price" | t: { amount: 29.99 | currency } }}</p>
<!-- Result: "Price: $29.99" from: "Price: {{amount}}" -->

<!-- Multiple variables -->
<p>{{ "updated" | t: { date: lastModified | date, user: currentUser } }}</p>
<!-- Result: "Updated on Jan 15, 2024 by Alice" from: "Updated on {{date}} by {{user}}." -->

L'interpolation vous permet d'insérer des valeurs dynamiques dans vos traductions. Vous pouvez passer des variables, utiliser des pipes Angular pour le formatage, et combiner plusieurs valeurs dans une seule chaîne de traduction.

Dans le Code du Composant :

import { Component } from '@angular/core';
import { t } from 'i18next';

@Component({
  selector: 'app-dashboard',
  template: `
    <h1>{{ title }}</h1>
    <p>{{ welcomeMsg }}</p>
  `,
})
export class DashboardComponent {
  title = t('dashboard.title');
  welcomeMsg = t('greeting', { name: 'John' });
}

Pour l'utilisation dans les modèles, la syntaxe du pipe est claire et lisible. Pour un accès programmatique, appelez t() directement. Cela est utile pour afficher des notifications, définir des titres dynamiques, ou gérer des traductions dans la logique du composant.

Meilleures Pratiques pour Angular i18next

Utilisez des Clés Significatives

Utilisez une notation par points qui reflète la structure de votre application : 'auth:login.title' au lieu de 'loginTitle' ou de texte brut.

Organisez avec des Espaces de Noms

Divisez les traductions en espaces de noms logiques (par exemple, common.json, auth.json, dashboard.json) pour une meilleure maintenabilité.

Gérez la Pluralisation

Utilisez le support de pluralisation intégré d'i18next pour gérer correctement les formes singulières/plurielle dans toutes les langues.

Attendez l'Initialisation

Assurez-vous toujours qu'i18next est initialisé avant de rendre votre application en utilisant provideAppInitializer() pour exécuter l'initialisation pendant le démarrage de l'application.

Sécurité de Type pour les Clés de Traduction

Utilisez TypeScript pour extraire les clés de traduction directement de vos fichiers JSON pour une sécurité de type complète. Au lieu de maintenir manuellement les définitions de type, tirez parti de typeof de TypeScript et des imports dynamiques pour générer automatiquement des types à partir de vos fichiers de traduction réels.

Définissez un type qui extrait toutes les clés possibles de vos fichiers de traduction JSON en utilisant un type d'aide NestedKeys récursif.

import common from './en/common.json'; // defaultNS: DEFAULT_NAMESPACE,
import auth from './en/auth.json'; // namespaces

type NestedKeys<T, Prefix extends string = ''> = T extends object
  ? {
      [K in keyof T & (string | number)]: T[K] extends object
        ?
            | NestedKeys<T[K], `${Prefix}${K & (string | number)}.`>
            | `${Prefix}${K & (string | number)}`
        : `${Prefix}${K & (string | number)}`;
    }[keyof T & (string | number)]
  : never;

export type TranslationKey =
  | `common:${NestedKeys<typeof common>}`
  | `auth:${NestedKeys<typeof auth>}`;

Changez la méthode transform du pipe pour accepter TranslationKey au lieu de string. Cela fournit une sécurité de type dans les modèles et garantit que seules des clés de traduction valides peuvent être utilisées.

import { Pipe, PipeTransform } from '@angular/core';
import { t } from 'i18next';

import { TranslationKey } from '../../i18n/translation-key';

@Pipe({
  name: 't',
  standalone: true,
  pure: false, // Need to update when language changes
})
export class TranslatePipe implements PipeTransform {
  transform(key: TranslationKey, options?: Record<string, unknown>): string {
    return t(key, options);
  }
}

Vous obtenez maintenant un autocomplétion IntelliSense et des erreurs de compilation pour les clés invalides dans le code TypeScript et les modèles.

t('common:greeting');       // ✅ Autocomplete
t('auth:login.title');      // ✅ Valid
t('invalid.key');           // ❌ IDE shows error

{{ "common:greeting" | t }} // ✅ Valid in templates
{{ "invalid.key" | t }}     // ❌ IDE shows error

Avantages des Clés de Traduction Sécurisées :

  • Autocomplétion dans votre IDE pour toutes les clés de traduction disponibles dans tous les espaces de noms.
  • Erreurs de compilation si vous utilisez une clé inexistante, attrapant les fautes de frappe avant l'exécution.
  • Support de refactorisation - renommez les clés en toute sécurité dans l'ensemble de votre code.
  • Aucun surcoût d'exécution - les types sont complètement supprimés pendant la compilation.
  • Généré automatiquement à partir de vos fichiers JSON réels - aucune maintenance manuelle des types requise.

Détection et Changement de Langue

Implémentez une détection de langue intelligente et permettez aux utilisateurs de changer de langue à l'exécution avec un chargement automatique des ressources.

Déterminez la langue de l'utilisateur à utiliser lorsque votre application démarre en vérifiant plusieurs sources par ordre de priorité.

private getInitialLanguage(): string {
  if (isPlatformBrowser(this.platformId)) {
    // Try to get language from localStorage (user preference)
    const savedLang = localStorage.getItem('language');
    if (savedLang) {
      return savedLang;
    }

    // Fall back to browser language
    return navigator.language ?? DEFAULT_LANGUAGE;
  }

  // Default language for SSR
  return DEFAULT_LANGUAGE;
}

Cette approche vérifie d'abord localStorage (préférence de l'utilisateur), puis se rabat sur la langue du navigateur (navigator.language), et enfin se fixe à la langue par défaut de l'application.

Changez de langue à l'exécution tout en chargeant automatiquement les ressources de traduction à la demande.

public async switchLanguage(
  language: string
): Promise<void> {
  // In browser, check if we need to load the translation first
  if (isPlatformBrowser(this.platformId)) {
    const i18next = await import('i18next');

    // Check if any namespace is missing for this language
    for (const namespace of NAMESPACES) {
      const currentResources = i18next.default.getResourceBundle(lang, namespace);

      if (!currentResources) {
        // Translation not loaded yet, load it dynamically
        const translationModule = await this.loadJson(lang, namespace);
        i18next.default.addResourceBundle(
          lang,
          namespace,
          translationModule.default
        );
      }
    }
  }

  // Switch to the new language
  await changeLanguage(lang);

  // Save preference to localStorage
  if (isPlatformBrowser(this.platformId)) {
    localStorage.setItem('language', lang);
  }
}

Lors de l'utilisation des imports dynamiques, les traductions sont chargées à la demande. La méthode switchLanguage() vérifie si les ressources sont déjà chargées en utilisant getResourceBundle(). Si elles manquent, elle les importe dynamiquement et les ajoute en utilisant addResourceBundle(). Cela empêche les requêtes réseau redondantes tout en s'assurant que toutes les traductions nécessaires sont disponibles.

Points Clés de Mise en Œuvre :

  • Vérifiez d'abord localStorage pour restaurer la langue préférée de l'utilisateur.
  • Utilisez navigator.language comme solution de secours pour la préférence de langue du navigateur.
  • Chargez les ressources manquantes dynamiquement avec addResourceBundle().

Automatiser la Traduction avec l'IA

Gérer les traductions manuellement dans plusieurs langues est chronophage et sujet à erreurs. C'est là que la localisation alimentée par l'IA entre en jeu.

Pourquoi l10n.dev ?

l10n.dev est un service de traduction alimenté par l'IA spécifiquement conçu pour les fichiers JSON i18next :

  • Préserve la structure JSON, les clés et les objets imbriqués.
  • Maintient correctement les espaces réservés comme {{name}}, {{count}}.
  • Génère automatiquement des formes plurielles pour toutes les langues.
  • Gère le contexte et l'interpolation de manière intelligente.
  • Supporte toutes les fonctionnalités d'i18next y compris les espaces de noms.

Flux de Travail Simple

  1. Exportez votre fichier JSON de langue de base (par exemple, en/common.json)
  2. Téléchargez sur l10n.dev et sélectionnez les langues cibles.
  3. L'IA traduit avec une conscience du contexte et un formatage approprié.
  4. Téléchargez les fichiers traduits prêts à être utilisés dans votre application Angular.

Économisez des heures de travail de traduction manuelle et évitez des erreurs courantes comme des espaces réservés cassés ou des formes plurielles incorrectes.

Prêt à rationaliser votre flux de travail Angular i18n ?