Hilfezentrum

Angular i18next

Internationalisierung (i18n) ist entscheidend für den Aufbau globaler Angular-Anwendungen. Dieser Leitfaden zeigt Ihnen, wie Sie i18next in Angular mit ordnungsgemäßer Ressourcenladung, Initialisierung und Nutzungsmustern implementieren.

Warum i18next für Angular?

i18next ist eines der beliebtesten i18n-Frameworks und bietet leistungsstarke Funktionen und Flexibilität:

  • Framework-unabhängig - funktioniert nahtlos mit Angular, React, Vue und anderen.
  • Leistungsstarke Interpolation, Pluralisierung und Unterstützung für Kontexte.
  • Unterstützung für Lazy Loading und Code-Splitting.
  • TypeScript-Unterstützung mit Typsicherheit.
  • Großes Ökosystem mit Plugins und Erweiterungen.

Installation

Installieren Sie i18next mit npm oder yarn:

npm install i18next

Das war's! i18next ist eine eigenständige Bibliothek ohne framework-spezifische Abhängigkeiten. Sie benötigen kein angular-i18next oder einen anderen Wrapper - verwenden Sie einfach i18next direkt in Ihrer Angular-Anwendung.

Laden von Übersetzungsressourcen

Es gibt zwei Hauptansätze zum Laden von Übersetzungsressourcen in Angular mit i18next:

Dynamische Importe (Empfohlen)

Die Verwendung dynamischer Importe nutzt das Code-Splitting von Webpack, um Übersetzungen nach Bedarf zu laden. Dieser Ansatz ist ideal für Anwendungen mit vielen Sprachen oder großen Übersetzungsdateien.

Vorteile:

  • Optimierung der Bundle-Größe - nur benötigte Übersetzungen laden.
  • Bessere Leistung - schnellere Ladezeit.
  • Funktioniert perfekt mit SSR und Vor-Rendering.
  • Keine zusätzlichen HTTP-Anfragen erforderlich.

Beispiel: Laden mit dynamischen Importen

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`);
}

Dieser Ansatz importiert Übersetzungsdateien als ES-Module, sodass Webpack sie automatisch in separate Chunks aufteilen kann, die nach Bedarf geladen werden.

i18next-http-backend

Das HTTP-Backend-Plugin lädt Übersetzungen über HTTP-Anfragen von einem Server oder CDN. Dies ist nützlich, wenn Sie Übersetzungen aktualisieren möchten, ohne Ihre App neu zu erstellen.

Vorteile:

  • Übersetzungen ohne erneute Bereitstellung aktualisieren.
  • Übersetzungen von externem CDN laden.
  • Nützlich für das Management von Übersetzungen zur Laufzeit.

Nachteile:

  • Zusätzliche HTTP-Anfragen verlangsamen das anfängliche Laden.
  • Benötigt Netzwerkverbindung.
  • Komplexeres SSR-Setup.

Beispiel: Laden mit HTTP-Backend

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,
});

Dieser Ansatz ruft Übersetzungsdateien zur Laufzeit von einem Server ab. Beachten Sie, dass dies zusätzliche Konfigurationen für SSR erfordert.

Welchen Ansatz wählen?

Verwenden Sie dynamische Importe, wenn:

  • Sie eine optimale Bundle-Größe und Leistung wünschen.
  • Übersetzungen Teil Ihres Build-Prozesses sind.
  • Sie Unterstützung für SSR/Vor-Rendering benötigen.

Verwenden Sie das HTTP-Backend, wenn:

  • Sie Übersetzungen ohne erneute Bereitstellung aktualisieren müssen.
  • Übersetzungen extern verwaltet werden.
  • Sie ein CDN zum Bereitstellen von Übersetzungsdateien haben.

Ordnungsgemäße i18next-Initialisierung

Die korrekte Initialisierung von i18next ist entscheidend, um Laufzeitfehler zu vermeiden und sicherzustellen, dass Übersetzungen verfügbar sind, wenn Ihre App gerendert wird.

Verwendung von provideAppInitializer

Die provideAppInitializer-Funktion von Angular (eingeführt in Angular 18) stellt sicher, dass i18next vollständig initialisiert ist, bevor die Anwendung mit dem Rendern beginnt. Die bereitgestellte Funktion wird während des App-Bootstraps ausgeführt, und die Initialisierung wird nicht abgeschlossen, bis das Promise aufgelöst ist. Dies verhindert, dass Übersetzungsschlüssel anstelle von übersetztem Text angezeigt werden.

Beispiel: App-Initializer-Konfiguration

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

Warum das wichtig ist:

  • Die Funktion wird im Injektionskontext während des Anwendungsstarts ausgeführt.
  • Verhindert Flackern oder das Anzeigen von Übersetzungsschlüsseln beim ersten Laden.
  • Stellt sicher, dass Übersetzungen von Anfang an in allen Komponenten verfügbar sind.
  • Funktioniert korrekt mit SSR, Vor-Rendering und clientseitigem Rendering.

i18next-Initialisierungsfunktion

Die Initialisierungsfunktion kümmert sich um das Laden von Ressourcen und die Konfiguration der i18next-Einstellungen.

Beispiel: i18next-Initialisierung

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,
    },
  });
}

Wichtige Punkte:

  • Laden Sie nur die aktuelle Sprache im Browser für optimale Leistung.
  • Laden Sie alle Sprachen während SSR für Unterstützung beim Vor-Rendering.
  • Verwenden Sie fallbackLng, um fehlende Übersetzungen elegant zu behandeln.
  • Konfigurieren Sie Namespaces für eine bessere Organisation.

Übersetzungs-Pipe

Die Angular-Übersetzungs-Pipe bietet eine saubere Möglichkeit, Übersetzungen in Vorlagen zu verwenden.

Pipe-Implementierung

Beispiel: Übersetzungs-Pipe

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);
  }
}

Die Pipe ist als rein: false markiert, um sicherzustellen, dass sie aktualisiert wird, wenn sich die Sprache ändert. Dies ist wichtig, da Sprachänderungen den Übersetzungsschlüssel selbst nicht modifizieren.

Verwendung der Übersetzungspipe

In Vorlagen:

<!-- 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}}." -->

Interpolation ermöglicht es dir, dynamische Werte in deine Übersetzungen einzufügen. Du kannst Variablen übergeben, Angular-Pipes zur Formatierung verwenden und mehrere Werte in einer einzigen Übersetzungszeichenfolge kombinieren.

Im Komponenten-Code:

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' });
}

Für die Verwendung in Vorlagen ist die Pipe-Syntax sauber und lesbar. Für den programmgesteuerten Zugriff rufe t() direkt auf. Dies ist nützlich, um Benachrichtigungen anzuzeigen, dynamische Titel festzulegen oder Übersetzungen in der Logik der Komponente zu behandeln.

Best Practices für Angular i18next

Verwende aussagekräftige Schlüssel

Verwende die Punktnotation, die die Struktur deiner App widerspiegelt: 'auth:login.title' anstelle von 'loginTitle' oder rohem Text.

Organisiere mit Namensräumen

Teile Übersetzungen in logische Namensräume (z.B. common.json, auth.json, dashboard.json) für bessere Wartbarkeit auf.

Behandle Pluralisierung

Nutze die eingebaute Pluralisierungsunterstützung von i18next, um Singular-/Pluralformen in allen Sprachen korrekt zu behandeln.

Warte auf die Initialisierung

Stelle immer sicher, dass i18next initialisiert ist, bevor du deine App renderst, indem du provideAppInitializer() verwendest, um die Initialisierung während des App-Bootstraps auszuführen.

Typensicherheit für Übersetzungsschlüssel

Verwende TypeScript, um Übersetzungsschlüssel direkt aus deinen JSON-Dateien für vollständige Typensicherheit zu extrahieren. Anstatt Typdefinitionen manuell zu pflegen, nutze TypeScripts typeof und dynamische Importe, um Typen automatisch aus deinen tatsächlichen Übersetzungsdateien zu generieren.

Definiere einen Typ, der alle möglichen Schlüssel aus deinen JSON-Übersetzungsdateien mithilfe eines rekursiven NestedKeys-Hilfstypen extrahiert.

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>}`;

Ändere die Transformationsmethode der Pipe, um TranslationKey anstelle von string zu akzeptieren. Dies bietet Typensicherheit in Vorlagen und stellt sicher, dass nur gültige Übersetzungsschlüssel verwendet werden können.

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);
  }
}

Jetzt erhältst du vollständige IntelliSense-Autovervollständigung und Kompilierfehler für ungültige Schlüssel sowohl im TypeScript-Code als auch in Vorlagen.

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

Vorteile von typensicheren Übersetzungsschlüsseln:

  • Autovervollständigung in deiner IDE für alle verfügbaren Übersetzungsschlüssel über alle Namensräume hinweg.
  • Kompilierfehler, wenn du einen nicht existierenden Schlüssel verwendest, wodurch Tippfehler vor der Laufzeit erkannt werden.
  • Unterstützung für Refactoring - sichere Umbenennung von Schlüsseln in deinem gesamten Code.
  • Null Laufzeitüberhead - Typen werden während der Kompilierung vollständig entfernt.
  • Automatisch aus deinen tatsächlichen JSON-Dateien generiert - keine manuelle Typwartung erforderlich.

Spracherkennung und -wechsel

Implementiere intelligente Spracherkennung und ermögliche es Benutzern, die Sprache zur Laufzeit mit automatischem Laden von Ressourcen zu wechseln.

Bestimme die Sprache des Benutzers, die beim Start deiner App verwendet werden soll, indem du mehrere Quellen in priorisierter Reihenfolge überprüfst.

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;
}

Dieser Ansatz überprüft zuerst localStorage (Benutzervoreinstellung), fällt dann auf die Sprache des Browsers (navigator.language) zurück und fällt schließlich auf die Standardsprache der App zurück.

Wechsle die Sprachen zur Laufzeit, während du Übersetzungsressourcen automatisch nach Bedarf lädst.

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);
  }
}

Bei der Verwendung von dynamischen Importen werden Übersetzungen nach Bedarf geladen. Die Methode switchLanguage() überprüft, ob Ressourcen bereits geladen sind, indem sie getResourceBundle() verwendet. Wenn sie fehlen, importiert sie sie dynamisch und fügt sie mit addResourceBundle() hinzu. Dies verhindert redundante Netzwerkaufrufe und stellt sicher, dass alle benötigten Übersetzungen verfügbar sind.

Wichtige Implementierungsdetails:

  • Überprüfe zuerst localStorage, um die bevorzugte Sprache des Benutzers wiederherzustellen.
  • Verwende navigator.language als Fallback für die Spracheinstellung des Browsers.
  • Lade fehlende Ressourcen dynamisch mit addResourceBundle().

Automatisiere Übersetzungen mit KI

Die manuelle Verwaltung von Übersetzungen über mehrere Sprachen hinweg ist zeitaufwendig und fehleranfällig. Hier kommt die KI-gestützte Lokalisierung ins Spiel.

Warum l10n.dev?

l10n.dev ist ein KI-gestützter Übersetzungsdienst, der speziell für i18next JSON-Dateien entwickelt wurde:

  • Bewahrt die JSON-Struktur, Schlüssel und verschachtelte Objekte.
  • Erhält Platzhalter wie {{name}}, {{count}} korrekt.
  • Generiert automatisch Pluralformen für alle Sprachen.
  • Handhabt Kontext und Interpolation intelligent.
  • Unterstützt alle i18next-Funktionen, einschließlich Namensräume.

Einfacher Workflow

  1. Exportiere deine JSON-Datei der Ausgangssprache (z.B. en/common.json)
  2. Lade sie zu l10n.dev hoch und wähle die Zielsprache aus.
  3. KI übersetzt mit Kontextbewusstsein und ordnungsgemäßer Formatierung.
  4. Lade die übersetzten Dateien herunter, die bereit zur Verwendung in deiner Angular-App sind.

Spare Stunden manueller Übersetzungsarbeit und vermeide gängige Fehler wie gebrochene Platzhalter oder falsche Pluralformen.

Bereit, deinen Angular i18n-Workflow zu optimieren?