前端国际化 (i18n) 最佳实践:从零搭建多语言应用
为什么需要国际化?
随着产品走向全球,支持多语言已成为前端开发的标配需求。但很多团队在初期只考虑单一语言,后期再添加国际化时往往面临代码重构、翻译管理混乱、性能下降等问题。本文将从选型、架构、实现到自动化,分享一套经过实践检验的国际化方案。
方案选型
目前主流的前端国际化库有:
- i18next:功能强大,生态丰富,支持 React/Vue/Angular 等框架,插件化程度高。
- react-intl (FormatJS):React 官方推荐,基于组件化 API,但仅限 React。
- vue-i18n:Vue 生态首选,与 Vue 深度集成。
我的选择:i18next。原因:
- 框架无关,可复用同一套配置到不同项目。
- 支持嵌套翻译、复数、上下文、ICU MessageFormat 等高级功能。
- 强大的插件系统,如自动检测语言、缓存、后端加载等。
项目初始化
以 React + TypeScript 为例,搭建一个多语言应用。
1. 安装依赖
npm install i18next react-i18next i18next-browser-languagedetector i18next-http-backend
2. 配置 i18n
创建 i18n.ts:
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import Backend from 'i18next-http-backend';
i18n
.use(Backend) // 加载翻译文件
.use(LanguageDetector) // 自动检测用户语言
.use(initReactI18next) // 绑定 React
.init({
fallbackLng: 'en', // 默认语言
debug: process.env.NODE_ENV === 'development',
interpolation: {
escapeValue: false, // React 已经安全
},
backend: {
loadPath: '/locales/{{lng}}/{{ns}}.json', // 翻译文件路径
},
detection: {
order: ['querystring', 'cookie', 'localStorage', 'navigator', 'htmlTag'],
caches: ['localStorage', 'cookie'],
},
});
export default i18n;
3. 翻译文件结构
public/
locales/
en/
common.json
home.json
zh/
common.json
home.json
en/common.json 示例:
{
"app": {
"title": "My App",
"description": "Welcome to my app"
},
"button": {
"submit": "Submit",
"cancel": "Cancel"
}
}
在 React 中使用
1. 使用 useTranslation Hook
import { useTranslation } from 'react-i18next';
function HomePage() {
const { t, i18n } = useTranslation('home');
const changeLanguage = (lng: string) => {
i18n.changeLanguage(lng);
};
return (
<div>
<h1>{t('welcome')}</h1>
<p>{t('intro', { name: 'John' })}</p>
<button onClick={() => changeLanguage('zh')}>中文</button>
<button onClick={() => changeLanguage('en')}>English</button>
</div>
);
}
home.json:
{
"welcome": "Welcome to the Home Page",
"intro": "Hello, {{name}}!"
}
2. 使用 Trans 组件处理富文本
import { Trans } from 'react-i18next';
function MyComponent() {
return (
<Trans i18nKey="description">
Read <a href="/docs">the docs</a> for more info.
</Trans>
);
}
翻译文件:
{
"description": "Read <1>the docs</1> for more info."
}
高级实践
1. 命名空间管理
将翻译文件按模块拆分(如 common, home, about),避免单个文件过大。在 useTranslation 中指定命名空间:
const { t } = useTranslation(['common', 'home']);
// 跨命名空间访问:t('common:button.submit')
2. 动态加载翻译
使用 i18next-http-backend 按需加载,减少初始包体积。配置 loadPath 为动态路径,并在切换语言时自动加载。
3. 类型安全
为翻译键生成 TypeScript 类型,避免拼写错误。使用 i18next-typescript 或自定义脚本:
// i18n.d.ts
declare module 'i18next' {
interface CustomTypeOptions {
defaultNS: 'common';
resources: {
common: typeof import('../public/locales/en/common.json');
home: typeof import('../public/locales/en/home.json');
};
}
}
4. SEO 优化
对于 SSR 框架(如 Next.js),需要根据语言设置 属性和正确 URL。示例:
// Next.js _app.tsx
import { appWithTranslation } from 'next-i18next';
function MyApp({ Component, pageProps }) {
return (
<>
<Head>
<html lang={i18n.language} />
</Head>
<Component {...pageProps} />
</>
);
}
export default appWithTranslation(MyApp);
自动化工作流
1. 提取翻译键
使用 i18next-scanner 或 babel-plugin-i18next-extract 自动扫描代码中的 t() 调用,生成翻译模板。
安装:
npm install --save-dev i18next-scanner
配置文件 i18next-scanner.config.js:
module.exports = {
input: ['src/**/*.{js,jsx,ts,tsx}', '!src/**/*.spec.*'],
output: './public/locales',
options: {
debug: true,
func: {
list: ['t'],
extensions: ['.js', '.jsx', '.ts', '.tsx'],
},
lngs: ['en', 'zh'],
ns: ['common', 'home'],
defaultLng: 'en',
defaultNs: 'common',
resource: {
loadPath: '{{lng}}/{{ns}}.json',
savePath: '{{lng}}/{{ns}}.json',
jsonIndent: 2,
},
},
};
运行:
npx i18next-scanner
2. 翻译管理平台
对接 Lokalise、POEditor 等平台,通过 API 同步翻译文件。示例使用 Lokalise CLI:
lokalise2 file upload --token <token> --project-id <id> --file ./public/locales/en/common.json --lang-iso en
常见坑与解决方案
❌ 问题1:翻译键重复
解决方案:命名空间 + 层级命名,如 home.welcome,避免全局扁平化。
❌ 问题2:动态内容不更新
解决方案:确保组件在语言切换后重新渲染。使用 useTranslation 的 t 函数是响应式的。
❌ 问题3:日期/数字格式化
解决方案:使用 Intl 对象或 i18next 的 format 函数。
i18n.services.formatter.add('date', (value, lng) => {
return new Intl.DateTimeFormat(lng).format(value);
});
// 使用:t('dateKey', { date: new Date(), formatParams: { date: { format: 'date' } } })
❌ 问题4:性能问题
解决方案:
- 按需加载翻译文件
- 避免在大型列表中使用
Trans组件 - 使用
t函数替代Trans尽量静态化
总结
本文从零搭建了一套完整的前端国际化方案,涵盖:
- 选型 i18next 作为核心库
- 配置自动检测语言和动态加载
- 在 React 中使用 hook 和组件
- 类型安全、SEO 优化等高级实践
- 自动化提取翻译键和管理平台集成
下一步建议:
- 探索 i18next 的 ICU MessageFormat 支持复数、性别等
- 结合 CI/CD 自动化翻译同步
- 考虑使用
i18next-resources-for-backend实现服务端渲染
希望这篇教程能帮助你构建健壮的多语言应用。如有问题,欢迎留言讨论!