國際化 (i18n) 對於建構全球化的 Angular 應用程式至關重要。本指南將向您展示如何透過正確的資源載入、初始化和使用模式在 Angular 中實作 i18next。
i18next 是最受歡迎的 i18n 框架之一,提供強大的功能與靈活性:
使用 npm 或 yarn 安裝 i18next:
npm install i18next就這樣!i18next 是一個獨立的函式庫,沒有框架特定的依賴關係。您不需要 angular-i18next 或任何其他封裝程式 - 只需在您的 Angular 應用程式中直接使用 i18next 即可。
在 Angular 中使用 i18next 載入翻譯資源主要有兩種方法:
使用動態匯入可利用 Webpack 的程式碼分割功能來按需載入翻譯。此方法非常適合擁有許多語言或大型翻譯檔案的應用程式。
優點:
範例:使用動態匯入載入
private async loadLanguageResources(
lang: string,
): Promise<Record<string, Record<string, unknown>>> {
const languageResources: Record<string, Record<string, unknown>> = {};
// Load all namespaces for the given language in parallel
await Promise.all(
NAMESPACES.map(async (namespace) => {
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 自動將其分割成獨立的區塊,並在需要時載入。
HTTP 後端插件透過伺服器或 CDN 的 HTTP 請求載入翻譯。當您希望在不重新建置應用程式的情況下更新翻譯時,這非常有用。
優點:
缺點:
範例:使用 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 進行額外設定。
正確初始化 i18next 對於避免執行時期錯誤並確保應用程式渲染時翻譯可用至關重要。
Angular 的 provideAppInitializer 函式(於 Angular 18 引入)確保 i18next 在應用程式開始渲染前已完全初始化。提供的函式會在應用程式啟動期間執行,且直到 Promise 解析完成前,初始化不會結束。這可防止翻譯鍵直接顯示在畫面上,而非翻譯後的文字。
範例:App Initializer 設定
export const appConfig: ApplicationConfig = {
providers: [
// ... other providers
provideAppInitializer(initializeI18n),
// ... more providers
],
};為什麼這很重要:
初始化函式負責載入資源並設定 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,
},
});
}關鍵點:
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);
}
}該管線被標記為 pure: 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()。這對於顯示通知、設定動態標題或在元件邏輯中處理翻譯非常有用。
使用反映應用程式結構的點記法:使用 'auth:login.title' 而非 'loginTitle' 或原始文字。
將翻譯拆分為邏輯命名空間(例如 common.json, auth.json, dashboard.json)以提高可維護性。
使用 i18next 內建的複數形式支援,以正確處理所有語言的單數/複數形式。
務必確保在使用 provideAppInitializer() 於應用程式啟動期間執行初始化之前,i18next 已完成初始化。
使用 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 而非 string。這在模板中提供了型別安全,並確保僅能使用有效的翻譯鍵。
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型別安全翻譯鍵的好處:
實作智慧語言偵測,並允許使用者在執行時期切換語言,同時自動載入資源。
透過優先順序檢查多個來源,決定應用程式啟動時使用的語言。
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');
await Promise.all(
NAMESPACES.map(async (namespace) => {
const currentResources = i18next.default.getResourceBundle(
lang,
namespace,
);
// Check if any namespace is missing for this language
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() 動態匯入並新增它們。這可防止冗餘的網路請求,同時確保所有需要的翻譯皆可用。
關鍵實作細節:
手動管理多語言翻譯既耗時又容易出錯。這就是 AI 驅動的本地化發揮作用的地方。
l10n.dev 是一款專為 i18next JSON 檔案設計的 AI 驅動翻譯服務:
節省數小時的手動翻譯工作,並避免常見錯誤,如損壞的佔位符或錯誤的複數形式。
準備好簡化您的 Angular i18n 工作流程了嗎?
在 Angular 中透過正確的初始化和資源載入策略實作 i18next,可確保流暢的國際化體驗。
結合像 l10n.dev 這樣的 AI 驅動翻譯服務,您可以更快地建構真正的全球化應用程式,並減少錯誤。
立即開始建構多語言 Angular 應用程式!