国际化(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>> = {};
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 自动将其拆分为按需加载的独立块。
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 解析之前不会完成。这可以防止翻译键在翻译文本出现之前显示。
示例:应用初始化配置
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);
}
}管道标记为纯: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 的内置复数支持,正确处理所有语言中的单数/复数形式。
始终确保在渲染应用之前初始化 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类型安全翻译键的好处:
实现智能语言检测,并允许用户在运行时切换语言,自动加载资源。
通过按优先顺序检查多个来源,确定应用启动时使用的用户语言。
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() 添加。这可以防止冗余的网络请求,同时确保所有所需的翻译都可用。
关键实现细节:
在多种语言中手动管理翻译既耗时又容易出错。这就是 AI 驱动的本地化的用武之地。
l10n.dev 是一个专门为 i18next JSON 文件设计的 AI 驱动翻译服务:
节省数小时的手动翻译工作,避免常见错误,如占位符损坏或复数形式不正确。
准备好简化您的 Angular i18n 工作流程了吗?
在 Angular 中实施 i18next,并采用适当的初始化和资源加载策略,确保顺利的国际化体验。
结合像 l10n.dev 这样的 AI 驱动翻译服务,您可以更快地构建真正全球化的应用程序,并减少错误。
今天就开始构建多语言 Angular 应用!