幫助中心

Angular i18next

國際化 (i18n) 對於建立全球性的 Angular 應用程式至關重要。本指南將向你展示如何在 Angular 中實現 i18next,並正確加載資源、初始化及使用模式。

為什麼選擇 i18next 作為 Angular 的解決方案?

i18next 是最受歡迎的 i18n 框架之一,提供強大的功能和靈活性:

  • 框架無關 - 與 Angular、React、Vue 等無縫協作。
  • 強大的插值、複數化和上下文支援。
  • 支援延遲加載和代碼拆分。
  • 提供 TypeScript 支援,並具備類型安全。
  • 擁有龐大的生態系統,包含插件和擴展。

安裝

使用 npm 或 yarn 安裝 i18next:

npm install i18next

就這樣!i18next 是一個獨立的庫,沒有特定於框架的依賴。你不需要 angular-i18next 或任何其他包裝器 - 只需在你的 Angular 應用程式中直接使用 i18next。

加載翻譯資源

在 Angular 中使用 i18next 加載翻譯資源主要有兩種方法:

動態導入 (推薦)

使用動態導入利用 Webpack 的代碼拆分按需加載翻譯。這種方法非常適合擁有多種語言或大型翻譯文件的應用程式。

優勢:

  • 優化包大小 - 只加載所需的翻譯。
  • 更好的性能 - 更快的初始加載時間。
  • 與 SSR 和預渲染完美協作。
  • 不需要額外的 HTTP 請求。

範例:使用動態導入加載

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

這種方法將翻譯文件作為 ES 模塊導入,允許 Webpack 自動將其拆分為單獨的塊,按需加載。

i18next-http-backend

HTTP 後端插件通過 HTTP 請求從伺服器或 CDN 加載翻譯。當你希望在不重建應用程式的情況下更新翻譯時,這非常有用。

優勢:

  • 在不重新部署的情況下更新翻譯。
  • 從外部 CDN 加載翻譯。
  • 對於運行時翻譯管理非常有用。

劣勢:

  • 額外的 HTTP 請求會減慢初始加載速度。
  • 需要網路連接。
  • 更複雜的 SSR 設置。

範例:使用 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,
});

這種方法在運行時從伺服器獲取翻譯文件。注意,這需要額外的 SSR 配置。

選擇哪種方法?

當你:

  • 希望獲得最佳的包大小和性能。
  • 翻譯是你的構建過程的一部分。
  • 需要 SSR/預渲染支援。

當你:

  • 需要在不重新部署的情況下更新翻譯。
  • 翻譯由外部管理。
  • 擁有用於提供翻譯文件的 CDN。

正確的 i18next 初始化

正確初始化 i18next 對於避免運行時錯誤和確保翻譯在應用程式渲染時可用至關重要。

使用 provideAppInitializer

Angular 的 provideAppInitializer 函數(在 Angular 18 中引入)確保在應用程式開始渲染之前完全初始化 i18next。提供的函數在應用程式啟動期間執行,初始化不會完成,直到 Promise 解決。這防止了翻譯鍵出現而不是翻譯文本。

範例:應用程式初始化配置

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

為什麼這很重要:

  • 該函數在應用程式啟動期間的注入上下文中運行。
  • 防止在初始加載時閃爍或顯示翻譯鍵。
  • 確保所有組件從一開始就可以使用翻譯。
  • 與 SSR、預渲染和客戶端渲染正確協作。

i18next 初始化函數

初始化函數處理加載資源和配置 i18next 設置。

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

要點:

  • 僅在瀏覽器中加載當前語言以獲得最佳性能。
  • 在 SSR 期間加載所有語言以支援預渲染。
  • 使用 fallbackLng 優雅地處理缺失的翻譯。
  • 配置命名空間以便更好地組織。

翻譯管道

Angular 翻譯管道提供了一種在模板中使用翻譯的簡潔方法。

管道實現

範例:翻譯管道

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

這個管道標記為純淨:false,以確保在語言變更時進行更新。這很重要,因為語言變更不會修改翻譯鍵本身。

使用翻譯管道

在模板中:

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

插值允許您將動態值插入到翻譯中。您可以傳遞變數,使用 Angular 管道進行格式化,並在單個翻譯字串中組合多個值。

在組件代碼中:

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

對於模板使用,管道語法簡潔易讀。對於程式化訪問,直接調用 t()。這對於顯示通知、設置動態標題或在組件邏輯中處理翻譯非常有用。

Angular i18next 的最佳實踐

使用有意義的鍵

使用反映您應用結構的點符號:'auth:login.title' 而不是 'loginTitle' 或原始文本。

使用命名空間進行組織

將翻譯拆分為邏輯命名空間(例如,common.json、auth.json、dashboard.json),以便更好地維護。

處理複數形式

使用 i18next 的內建複數化支持,正確處理所有語言中的單數/複數形式。

等待初始化

始終確保在渲染您的應用之前初始化 i18next,使用 provideAppInitializer() 在應用啟動期間運行初始化。

翻譯鍵的類型安全

使用 TypeScript 從您的 JSON 文件中直接提取翻譯鍵以獲得完整的類型安全。不要手動維護類型定義,利用 TypeScript 的 typeof 和動態導入自動生成來自您實際翻譯文件的類型。

定義一種類型,使用遞歸的 NestedKeys 助手類型提取 JSON 翻譯文件中所有可能的鍵。

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

將管道的 transform 方法更改為接受 TranslationKey 而不是字符串。這在模板中提供類型安全,確保只能使用有效的翻譯鍵。

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

現在您可以在 TypeScript 代碼和模板中獲得完整的 IntelliSense 自動完成功能和編譯時錯誤,針對無效鍵進行檢查。

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

類型安全翻譯鍵的好處:

  • 在您的 IDE 中自動完成所有可用翻譯鍵,涵蓋所有命名空間。
  • 如果您使用不存在的鍵,則會出現編譯時錯誤,能在運行時之前捕獲錯字。
  • 重構支持 - 安全地在整個代碼庫中重命名鍵。
  • 零運行時開銷 - 類型在編譯過程中完全移除。
  • 從您的實際 JSON 文件自動生成 - 無需手動維護類型。

語言檢測和切換

實施智能語言檢測,並允許用戶在運行時切換語言,並自動加載資源。

通過按優先順序檢查多個來源來確定應用啟動時使用的用戶語言。

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

這種方法首先檢查 localStorage(用戶偏好),然後回退到瀏覽器的語言(navigator.language),最後默認為應用的默認語言。

在運行時切換語言,同時自動按需加載翻譯資源。

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

使用動態導入時,翻譯按需加載。switchLanguage() 方法檢查資源是否已經加載,使用 getResourceBundle()。如果缺少,則動態導入並使用 addResourceBundle() 添加它們。這可以防止冗餘的網絡請求,同時確保所有需要的翻譯都可用。

關鍵實施細節:

  • 首先檢查 localStorage,以恢復用戶的首選語言。
  • 使用 navigator.language 作為瀏覽器語言偏好的回退。
  • 使用 addResourceBundle() 動態加載缺失的資源。

使用 AI 自動化翻譯

在多種語言中手動管理翻譯既耗時又容易出錯。這就是 AI 驅動的本地化的用武之地。

為什麼選擇 l10n.dev?

l10n.dev 是一個專門為 i18next JSON 文件設計的 AI 驅動翻譯服務:

  • 保留 JSON 結構、鍵和嵌套對象。
  • 正確維護佔位符,如 {{name}}、{{count}}。
  • 自動生成所有語言的複數形式。
  • 智能處理上下文和插值。
  • 支持所有 i18next 特性,包括命名空間。

簡單工作流程

  1. 導出您的基礎語言 JSON 文件(例如,en/common.json)
  2. 上傳到 l10n.dev 並選擇目標語言。
  3. AI 進行上下文感知和正確格式的翻譯。
  4. 下載翻譯文件,準備在您的 Angular 應用中使用。

節省數小時的手動翻譯工作,避免常見錯誤,如破損的佔位符或不正確的複數形式。

準備好簡化您的 Angular i18n 工作流程了嗎?