国际化(i18n)多语言支持
当你的产品走向全球市场,国际化就从"锦上添花"变成了"必须具备"。但 i18n 远不只是"把文字翻译成英文"——它涉及路由策略、语言检测、日期/数字格式化、RTL 布局、SEO 优化、翻译工作流等一系列复杂问题。Next.js App Router 重塑了 i18n 的实现方式,本章从架构到细节,系统讲解如何在 Next.js 中构建生产级的多语言支持。
1. 国际化的本质问题
1.1 i18n vs L10n
两个经常混淆的概念:
| 概念 | 全称 | 含义 | 类比 |
|---|---|---|---|
| i18n | Internationalization | 让应用能够支持多语言的技术架构 | 造一辆左右舵都能开的车 |
| L10n | Localization | 将应用适配到特定语言/文化的过程 | 把方向盘装到右边(日本市场) |
i18n 是开发者的工作(技术架构),L10n 是翻译者/运营的工作(内容适配)。两者需要同时进行但职责分明。
1.2 国际化的五个维度
| 维度 | 内容 | 示例 |
|---|---|---|
| 文本翻译 | UI 文案、按钮、标签 | "Submit" → "提交" |
| 日期/时间 | 日期格式、时区 | "4/12/2025" vs "2025-04-12" |
| 数字/货币 | 千分位、小数点、货币符号 | "$1,234.56" vs "¥1,234.56" |
| 布局方向 | 从左到右(LTR)vs 从右到左(RTL) | 英语 LTR,阿拉伯语 RTL |
| 文化适配 | 颜色含义、图标意义、内容合规 | 红色在中国是喜庆,在西方可能是警告 |
1.3 Next.js App Router 的 i18n 变化
Next.js Pages Router 有内置的 i18n 路由支持(next.config.js 中的 i18n 配置)。但 App Router 移除了这个内置功能——取而代之的是更灵活的基于文件系统的方案。
| 特性 | Pages Router | App Router |
|---|---|---|
| 内置 i18n 路由 | ✅ next.config.js 配置 | ❌ 需要手动实现或用库 |
| 语言前缀路由 | 自动处理 | 用 [locale] 动态段 |
| 语言检测 | 自动(Accept-Language) | Middleware 手动实现 |
| Server Components | N/A | ✅ 完全支持 |
| 翻译加载 | 客户端为主 | 服务端加载(零客户端 JS) |
好消息:App Router 的 i18n 虽然没有内置,但与 Server Components 的结合让翻译可以完全在服务端完成——零额外客户端 JavaScript,这在性能上是巨大的提升。
2. 方案选型
2.1 主流 i18n 方案对比
| 方案 | 类型 | App Router 支持 | RSC 支持 | 特点 |
|---|---|---|---|---|
| next-intl | 专为 Next.js | ⭐⭐⭐⭐⭐ | ✅ 原生 | 2025 年首选,API 最优雅 |
| next-i18next | Next.js 适配 | ⭐⭐⭐ | ⚠️ 需要适配 | Pages Router 时代的标准 |
| react-i18next | React 通用 | ⭐⭐⭐ | ⚠️ 需要适配 | 生态最大,但不专为 Next.js |
| lingui | 编译时 | ⭐⭐⭐⭐ | ✅ | 编译时提取,ICU 标准 |
| Paraglide | 编译时 | ⭐⭐⭐⭐ | ✅ | 极小 Bundle,Treeshaking |
| 自建 | — | ✅ | ✅ | 简单场景够用 |
2.2 选型建议
新项目 + App Router? → next-intl(2025 年无脑选)
已有 react-i18next? → 继续用,逐步迁移
追求最小 Bundle? → Paraglide
需要 ICU MessageFormat? → lingui
只有 2-3 种语言 + 简单文案? → 可以自建
为什么 next-intl 是首选:
- 专为 App Router 设计——不是从 Pages Router 迁移过来的
- Server Components 原生支持——翻译在服务端完成,零客户端 JS
- 类型安全——翻译键自动补全
- 路由国际化内置——
[locale]路由 + Middleware 语言检测 - ICU MessageFormat——支持复数、性别、日期等复杂格式
- 活跃维护——作者 amannn 持续更新
3. 路由策略
3.1 三种路由模式
| 模式 | URL 示例 | 优点 | 缺点 |
|---|---|---|---|
| 路径前缀 | /en/about, /zh/about | SEO 最友好,最明确 | URL 变长 |
| 子域名 | en.example.com, zh.example.com | 域名级别隔离 | DNS 配置复杂 |
| Cookie/Header | /about(根据 Cookie 切换语言) | URL 简洁 | SEO 不友好,缓存困难 |
推荐:路径前缀模式。原因:
- 搜索引擎可以明确区分不同语言的页面
- 用户可以通过 URL 知道当前语言
- 方便分享特定语言的链接
- CDN 缓存友好(不同语言是不同 URL)
3.2 默认语言的 URL 处理
一个常见的选择:默认语言是否需要前缀?
| 策略 | 默认语言(中文)URL | 其他语言(英文)URL | 适用场景 |
|---|---|---|---|
| 全部有前缀 | /zh/about | /en/about | 一致性强,推荐 |
| 默认无前缀 | /about | /en/about | URL 简洁,但需要重定向 |
推荐全部有前缀——虽然 URL 多了一层,但逻辑统一,不需要处理"有前缀"和"无前缀"两套路由。
3.3 App Router 中的路由实现
使用 [locale] 动态段实现语言路由:
app/
├── [locale]/
│ ├── layout.tsx ← 设置 html lang 属性
│ ├── page.tsx ← 首页
│ ├── about/
│ │ └── page.tsx ← 关于页
│ └── blog/
│ ├── page.tsx ← 博客列表
│ └── [slug]/
│ └── page.tsx ← 博客详情
└── not-found.tsx
所有页面都在 [locale] 目录下,locale 参数自动传递给每个页面组件。
3.4 Middleware 语言检测
Middleware 负责两件事:
- 语言检测:根据
Accept-Language头、Cookie、URL 判断用户语言 - 路由重定向:将无前缀的 URL 重定向到正确的语言版本
语言检测优先级(从高到低):
1. URL 中的语言前缀(/en/about → 英文)
2. Cookie 中保存的语言偏好
3. Accept-Language 请求头
4. 默认语言(fallback)
4. 翻译文件管理
4.1 文件组织结构
按语言组织(推荐):
messages/
├── zh.json ← 中文
├── en.json ← 英文
├── ja.json ← 日文
└── ko.json ← 韩文
按模块 + 语言组织(大型项目):
messages/
├── zh/
│ ├── common.json
│ ├── auth.json
│ ├── dashboard.json
│ └── settings.json
├── en/
│ ├── common.json
│ ├── auth.json
│ ├── dashboard.json
│ └── settings.json
4.2 翻译键的命名规范
| 规范 | 示例 | 适用场景 |
|---|---|---|
| 嵌套结构 | auth.login.title | 模块化清晰 |
| 扁平结构 | auth_login_title | 简单项目 |
| 页面级别 | HomePage.hero.title | 按页面组织 |
推荐嵌套结构:
{
"common": {
"submit": "提交",
"cancel": "取消",
"loading": "加载中..."
},
"auth": {
"login": {
"title": "登录",
"email": "邮箱",
"password": "密码",
"forgot": "忘记密码?"
}
},
"dashboard": {
"welcome": "欢迎回来,{name}",
"stats": {
"users": "{count, plural, =0 {没有用户} =1 {1 个用户} other {# 个用户}}"
}
}
}4.3 ICU MessageFormat
ICU MessageFormat 是国际化文本格式的国际标准,支持:
变量插值:
"welcome": "欢迎,{name}"
→ 欢迎,张三
复数处理:
"items": "{count, plural, =0 {没有项目} =1 {1 个项目} other {# 个项目}}"
→ 没有项目 / 1 个项目 / 5 个项目
性别处理:
"pronoun": "{gender, select, male {他} female {她} other {Ta}}"
日期/数字格式:
"date": "发布于 {date, date, medium}"
"price": "价格:{price, number, currency}"
为什么推荐 ICU MessageFormat:
- 国际标准(Unicode CLDR)
- 翻译人员熟悉
- 处理各语言的复数规则(英语 2 种,阿拉伯语 6 种)
- 工具链支持好(翻译平台、Lint 工具)
4.4 翻译文件的类型安全
没有类型安全的翻译键是灾难——改了 JSON 中的键名,代码中不会报错,直到运行时才发现翻译缺失。
next-intl 提供类型安全:
- 定义翻译消息的类型
- 在
global.d.ts中声明 - 使用
useTranslations时自动补全键名
这意味着:
- 翻译键拼写错误 → 编译时报错
- 重命名翻译键 → IDE 自动重构
- 缺失翻译 → TypeScript 提示
5. Server Components 中的 i18n
5.1 零客户端 JS 的翻译
App Router 最大的 i18n 优势:翻译可以完全在服务端完成。
Pages Router 时代:
翻译 JSON → 打包到客户端 → 运行时加载 → 渲染
问题:翻译文件增加 Bundle 大小
App Router 时代:
翻译 JSON → Server Component 直接读取 → 服务端渲染 → 发送 HTML
优势:翻译 JSON 不会出现在客户端 Bundle 中
性能影响:对于一个有 1000 个翻译键、支持 5 种语言的应用,Pages Router 需要将翻译文件发送到客户端(可能 50-100KB),App Router 中这些完全是零客户端开销。
5.2 Server vs Client Components 中的翻译
| 场景 | 组件类型 | 翻译方式 |
|---|---|---|
| 页面标题、段落文本 | Server Component | 直接调用翻译函数 |
| 表单标签、静态文案 | Server Component | 直接调用翻译函数 |
| 交互反馈(Toast、确认框) | Client Component | 通过 Provider 获取翻译 |
| 动态内容(搜索建议) | Client Component | 通过 Provider 获取翻译 |
原则:尽量在 Server Components 中完成翻译,只在需要交互的 Client Components 中使用客户端翻译。
5.3 generateMetadata 的国际化
SEO 相关的 metadata 也需要国际化:
/zh/about → <title>关于我们</title>
/en/about → <title>About Us</title>
在 generateMetadata 中根据 locale 参数加载对应语言的翻译,生成正确的 title、description 和 Open Graph 信息。
同时需要设置 alternates.languages 告诉搜索引擎不同语言版本的 URL 关系:
<link rel="alternate" hreflang="zh" href="https://example.com/zh/about" />
<link rel="alternate" hreflang="en" href="https://example.com/en/about" />
<link rel="alternate" hreflang="x-default" href="https://example.com/zh/about" />6. 日期、数字与格式化
6.1 日期格式化
不同地区的日期格式差异巨大:
| 地区 | 格式 | 示例 |
|---|---|---|
| 中国 | yyyy年M月d日 | 2025年4月12日 |
| 美国 | M/d/yyyy | 4/12/2025 |
| 英国 | d/M/yyyy | 12/4/2025 |
| 日本 | yyyy/M/d | 2025/4/12 |
| 德国 | d.M.yyyy | 12.4.2025 |
不要手动拼接日期字符串——使用 Intl.DateTimeFormat 或 i18n 库的格式化功能,它们会根据 locale 自动选择正确的格式。
6.2 数字与货币格式化
| 地区 | 数字格式 | 货币格式 |
|---|---|---|
| 中国 | 1,234.56 | ¥1,234.56 |
| 美国 | 1,234.56 | $1,234.56 |
| 德国 | 1.234,56 | 1.234,56 € |
| 印度 | 1,23,456.78 | ₹1,23,456.78 |
注意德国用逗号做小数点、点做千分位,印度的千分位分隔规则也不同。
6.3 相对时间
"3 分钟前"、"昨天"、"上周" 在不同语言中的表达:
| 语言 | 3 分钟前 | 昨天 |
|---|---|---|
| 中文 | 3 分钟前 | 昨天 |
| 英文 | 3 minutes ago | yesterday |
| 日文 | 3 分前 | 昨日 |
| 韩文 | 3분 전 | 어제 |
使用 Intl.RelativeTimeFormat 或 i18n 库的相对时间功能。
7. RTL(从右到左)支持
7.1 哪些语言需要 RTL
| 语言 | 方向 | 使用人口 |
|---|---|---|
| 阿拉伯语 | RTL | ~4 亿 |
| 希伯来语 | RTL | ~0.9 亿 |
| 波斯语 | RTL | ~1.1 亿 |
| 乌尔都语 | RTL | ~2.3 亿 |
如果你的产品面向中东市场,RTL 支持是必须的。
7.2 RTL 适配要点
| 要点 | 说明 | Tailwind 实现 |
|---|---|---|
| 文本方向 | direction: rtl | dir="rtl" on <html> |
| 布局翻转 | flexbox/grid 自动翻转 | 大部分自动处理 |
| 内边距 | padding-left → padding-right | ps-4 / pe-4(logical properties) |
| 外边距 | margin-left → margin-right | ms-4 / me-4 |
| 图标方向 | 箭头等方向性图标需要翻转 | rtl:rotate-180 |
| 圆角 | border-radius 左右互换 | rounded-s-lg / rounded-e-lg |
7.3 CSS Logical Properties
CSS Logical Properties 是 RTL 支持的基础——用"逻辑方向"(start/end)替代"物理方向"(left/right):
| 物理属性 | 逻辑属性 | LTR 效果 | RTL 效果 |
|---|---|---|---|
margin-left | margin-inline-start | 左侧 | 右侧 |
margin-right | margin-inline-end | 右侧 | 左侧 |
padding-left | padding-inline-start | 左侧 | 右侧 |
text-align: left | text-align: start | 左对齐 | 右对齐 |
float: left | float: inline-start | 左浮动 | 右浮动 |
Tailwind CSS v4 默认支持 logical properties:ps-4(padding-start)、me-2(margin-end)。
8. 翻译工作流
8.1 开发者 vs 翻译者的职责边界
| 职责 | 开发者 | 翻译者 |
|---|---|---|
| 设计翻译键结构 | ✅ | ❌ |
| 编写默认语言文案 | ✅ | ❌ |
| 翻译其他语言 | ❌ | ✅ |
| Review 翻译质量 | 协助 | ✅ |
| 处理上下文不足 | 提供截图/说明 | 反馈疑问 |
8.2 翻译平台
| 平台 | 特点 | 价格 | 适用场景 |
|---|---|---|---|
| Crowdin | 最主流、GitHub 集成好 | 免费(开源) | 开源项目 |
| Lokalise | 开发者体验好 | 付费 | 商业项目 |
| Phrase | 企业级、工作流完善 | 付费 | 大型团队 |
| Transifex | 社区翻译支持好 | 免费层 | 社区翻译 |
| AI 翻译 | GPT-4 / DeepL API | 按用量 | 初始翻译 + 人工校对 |
8.3 翻译工作流自动化
开发者添加/修改翻译键
↓
PR 合并到 main
↓
CI 自动同步到翻译平台(Crowdin/Lokalise)
↓
翻译者在平台中翻译
↓
翻译完成 → 平台自动创建 PR(包含翻译文件更新)
↓
开发者 Review + 合并
↓
部署上线
这个流程实现了:
- 开发者不需要手动管理翻译文件
- 翻译者不需要接触代码
- 翻译进度可追踪
- 缺失翻译自动检测
8.4 AI 辅助翻译策略
2025 年的实际做法:AI 初始翻译 + 人工校对。
| 步骤 | 工具 | 质量 |
|---|---|---|
| 初始翻译 | GPT-4 / DeepL API | 80-90% 可用 |
| 术语统一 | 术语表(Glossary) | 确保专业词汇一致 |
| 人工校对 | 翻译者/母语者 | 修正语境和语气 |
| 持续维护 | AI + 人工混合 | 新增文案用 AI,定期人工审查 |
9. SEO 与搜索引擎优化
9.1 多语言 SEO 清单
| 检查项 | 说明 | 实现方式 |
|---|---|---|
✅ hreflang 标签 | 告诉搜索引擎不同语言版本 | <link rel="alternate"> |
✅ lang 属性 | HTML 文档语言声明 | <html lang="zh"> |
| ✅ 语言化 URL | 不同语言使用不同 URL | /zh/about vs /en/about |
| ✅ 翻译 metadata | title、description 需要翻译 | generateMetadata |
| ✅ Open Graph 翻译 | 社交分享卡片需要翻译 | og:title, og:description |
| ✅ Sitemap 多语言 | 每种语言的 URL 都要包含 | sitemap.xml 生成 |
| ✅ 规范 URL | 避免重复内容 | canonical + hreflang |
9.2 hreflang 详解
hreflang 告诉搜索引擎:"这个页面有多个语言版本,请根据用户语言展示正确的版本。"
<!-- 在 /zh/about 页面中 -->
<link rel="alternate" hreflang="zh" href="https://example.com/zh/about" />
<link rel="alternate" hreflang="en" href="https://example.com/en/about" />
<link rel="alternate" hreflang="ja" href="https://example.com/ja/about" />
<link rel="alternate" hreflang="x-default" href="https://example.com/zh/about" />x-default 指定当用户语言不匹配任何版本时的默认页面。
9.3 多语言 Sitemap
<url>
<loc>https://example.com/zh/about</loc>
<xhtml:link rel="alternate" hreflang="zh" href="https://example.com/zh/about" />
<xhtml:link rel="alternate" hreflang="en" href="https://example.com/en/about" />
</url>
<url>
<loc>https://example.com/en/about</loc>
<xhtml:link rel="alternate" hreflang="zh" href="https://example.com/zh/about" />
<xhtml:link rel="alternate" hreflang="en" href="https://example.com/en/about" />
</url>10. 常见陷阱与最佳实践
10.1 常见陷阱
| 陷阱 | 问题 | 解决方案 |
|---|---|---|
| 硬编码文本 | 代码中直接写中文/英文 | 所有文本都走翻译系统 |
| 拼接翻译 | t('hello') + name | 用 ICU 变量 {name} |
| 假设文本长度 | 翻译后可能变长/变短 | 弹性布局,不固定宽度 |
| 忽略复数规则 | 英语 2 种,阿拉伯语 6 种 | 用 ICU plural |
| 未翻译的图片 | 图片中的文字无法翻译 | 用 CSS/SVG 而非图片中的文字 |
| 忽略时区 | 服务端和客户端时区不一致 | 始终用 UTC 存储,展示时转换 |
10.2 最佳实践
- 一开始就考虑 i18n——后期改造成本是前期的 10 倍
- 翻译键用英文描述而非编号——
auth.login.title而非key_001 - 提供翻译上下文——给翻译者截图或说明,避免歧义
- 不要过度翻译——品牌名、产品名、技术术语可以保留原文
- 测试极端情况——德语单词特别长,日语没有空格分词
- 自动检测缺失翻译——CI 中检查所有语言的翻译完整性
本章小结
- 方案选型:2025 年 next-intl 是 Next.js App Router 的首选 i18n 方案
- 路由策略:路径前缀模式(
/zh/about)SEO 最友好,推荐所有语言都有前缀 - Server Components 优势:翻译在服务端完成,零客户端 JS 开销
- 翻译文件管理:嵌套 JSON 结构 + ICU MessageFormat + TypeScript 类型安全
- 格式化:日期、数字、货币使用 Intl API,不要手动拼接
- RTL 支持:使用 CSS Logical Properties(
ps-4/me-2),Tailwind v4 原生支持 - 翻译工作流:AI 初始翻译 + 人工校对 + 翻译平台自动化同步
- SEO 关键:
hreflang标签 + 翻译 metadata + 多语言 Sitemap - 核心原则:一开始就设计 i18n 架构,后期改造成本极高