国际化(i18n)对于构建全球化的Angular应用至关重要。本指南向您展示如何在Angular中实现i18next,包括正确的资源加载、初始化和使用模式。
i18next是最受欢迎的国际化框架之一,提供强大的功能和灵活性:
使用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国际化工作流程了吗?
在Angular中通过正确的初始化和资源加载策略实现i18next,可确保流畅的国际化体验。
结合像l10n.dev这样的AI驱动翻译服务,您可以更快地构建全球化应用,并减少错误。
立即开始构建多语言Angular应用!