React 国际化 i18n 完整方案
React 国际化 i18n 完整方案:从文案抽取到动态语言切换
引言
国际化(i18n)是面向全球用户的 React 应用的「隐形工程」:不只是翻译几个按钮文案,还涉及日期、数字、货币、复数、时区、排版方向(RTL)与搜索引擎优化。本文将从 i18n 的复杂度模型讲起,系统梳理 React 国际化的标准流程:文案组织、react-i18next 接入、动态语言切换、服务端渲染的国际化、本地化(l10n)细节与自动化工具链。
一、国际化的复杂度模型
「把字符串抽出来翻译」只是国际化的冰山一角。一个完整的 i18n 体系需要处理四类差异:文本、数字、日期与排版。
国际化差异全景
flowchart TB
classDef i1 fill:#e3f2fd,stroke:#1976d2,color:#0d47a1
classDef i2 fill:#f3e5f5,stroke:#8e24aa,color:#4a148c
classDef i3 fill:#e0f7fa,stroke:#00838f,color:#004d40
A[i18n 差异模型] --> B[文本翻译<br/>文案资源]
A --> C[数字格式化<br/>千分位/小数点]
A --> D[日期时间<br/>时区/历法]
A --> E[复数规则<br/>单复数形态]
A --> F[排版方向<br/>LTR / RTL]
B --> B1[多语言资源文件]
C --> C1[Intl.NumberFormat]
D --> D1[Intl.DateTimeFormat]
E --> E1[plural 规则映射]
F --> F1[dir 属性切换]
click C1 "https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat" "Intl 文档"
class A i1
class B i1
class C i2
class D i2
class E i2
class F i2
class B1 i3
class C1 i3
class D1 i3
class E1 i3
class F1 i3
常见的国际化误区
| 误区 | 后果 | 正确做法 |
|---|---|---|
| 硬编码字符串 | 无法翻译 | 全部走 i18n 资源 |
| 拼接句子「您有 N 条消息」 | 语序无法调整 | 用插值 + 复数 |
| 只翻 UI 不翻日期 | 体验割裂 | Intl 格式化 |
| 忽略 RTL | 阿拉伯语排版错乱 | dir 属性 + 逻辑样式 |
二、文案组织:资源文件的设计
i18n 的基石是资源文件(翻译键值对)。组织方式直接影响维护成本与协作效率。
资源结构设计
flowchart LR
classDef r1 fill:#e3f2fd,stroke:#1976d2,color:#0d47a1
classDef r2 fill:#f3e5f5,stroke:#8e24aa,color:#4a148c
classDef r3 fill:#e0f7fa,stroke:#00838f,color:#004d40
A[locales 目录] --> B[zh-CN 中文]
A --> C[en-US 英文]
A --> D[ja-JP 日文]
B --> E[common.json<br/>通用文案]
B --> F[posts.json<br/>文章模块]
B --> G[admin.json<br/>控制台]
E --> H[命名空间 key 化]
click H "https://react.i18next.com/latest/using-with-hooks" "i18next 使用文档"
class A r1
class B r2
class C r2
class D r2
class E r3
class F r3
class G r3
class H r2
资源文件示例
json123456789101112// locales/zh-CN/common.json { "actions": { "save": "保存", "cancel": "取消", "delete": "删除" }, "notifications": { "count": "您有 {{count}} 条消息", "count_plural": "您有 {{count}} 条消息" } }
设计原则:按模块分文件(命名空间)、按功能分 key、插值用 {{var}} 占位、复数用 _plural 后缀——这些约定让翻译协作可控。
三、react-i18next 接入实战
react-i18next 是 React 生态最成熟的 i18n 方案,基于 i18next 核心,提供 useTranslation Hook 与 Trans 组件。
初始化流程
flowchart TB
classDef e1 fill:#e3f2fd,stroke:#1976d2,color:#0d47a1
classDef e2 fill:#f3e5f5,stroke:#8e24aa,color:#4a148c
classDef e3 fill:#e0f7fa,stroke:#00838f,color:#004d40
A[i18n 初始化] --> B[加载资源文件]
B --> C[设置默认语言]
C --> D{语言检测}
D -->|浏览器| E[languageDetector]
D -->|URL 参数| F[路由参数]
D -->|存储| G[localStorage]
E --> H[实例化 i18next]
F --> H
G --> H
H --> I[I18nextProvider 包裹]
I --> J[组件 useTranslation]
click E "https://github.com/i18next/i18next-browser-languageDetector" "语言检测插件"
class A e1
class B e1
class C e1
class D e2
class E e3
class F e3
class G e3
class H e2
class I e2
class J e3
组件中使用
javascript1234567891011121314151617import { useTranslation } from "react-i18next"; function Notification({ count }) { const { t } = useTranslation("common"); return ( <div> <span>{t("notifications.count", { count })}</span> <button>{t("actions.save")}</button> </div> ); } // 带嵌套翻译的复合文案 function Welcome({ name }) { const { t } = useTranslation(); return <p>{t("welcome.message", { name })}</p>; }
四、动态语言切换与持久化
语言切换不是「换一套文案」这么简单——它需要同时更新:当前语言、UI 渲染、日期格式、文档方向(dir)与 SEO 链接。
语言切换流程
sequenceDiagram
participant U as 用户
participant S as 切换按钮
participant I as i18next
participant D as 文档
U->>S: 点击「English」
S->>I: changeLanguage('en-US')
I->>I: 加载 en-US 资源
I->>I: 触发语言变化事件
I->>D: 更新 lang 属性
I->>D: 更新 dir 属性(若 RTL)
D->>D: 重渲染全部消费组件
Note over D: 更新本地存储与 URL
持久化与 SEO 的配合
flowchart LR
classDef s1 fill:#e3f2fd,stroke:#1976d2,color:#0d47a1
classDef s2 fill:#f3e5f5,stroke:#8e24aa,color:#4a148c
classDef s3 fill:#e0f7fa,stroke:#00838f,color:#004d40
A[语言选择] --> B[localStorage 持久化]
B --> C[下次访问直接命中]
A --> D[URL 路径前缀<br/>/zh-cn/ /en/]
D --> E[SEO 友好 可分享]
D --> F[浏览器语言兜底]
click D "https://www.i18next.com/principles/design" "i18next 设计原则"
class A s1
class B s2
class C s3
class D s2
class E s3
class F s3
实践建议:SSR 应用用「URL 前缀 + 服务端协商」方案(如 /zh-cn/posts),对 SEO 与缓存最友好;纯 SPA 用 localStorage + 浏览器检测即可。
五、数字、日期与复数的本地化
即使文案全部翻译,日期与数字格式的错误同样会让用户感到「这不是给我的应用」。Intl API 是本地化的标准答案。
Intl 格式化
flowchart TD
classDef f1 fill:#e3f2fd,stroke:#1976d2,color:#0d47a1
classDef f2 fill:#f3e5f5,stroke:#8e24aa,color:#4a148c
classDef f3 fill:#e0f7fa,stroke:#00838f,color:#004d40
A[本地化格式化] --> B[数字<br/>Intl.NumberFormat]
A --> C[日期<br/>Intl.DateTimeFormat]
A --> D[货币<br/>Intl.NumberFormat 货币样式]
A --> E[复数<br/>i18next plural]
B --> B1[中文 1,234.5<br/>德语 1.234,5]
C --> C1[中文 2024年1月1日<br/>英文 Jan 1, 2024]
D --> D1[¥1,234 / $1,234]
E --> E1[1 条消息 / 2 条消息]
click C1 "https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat" "DateTimeFormat 文档"
class A f1
class B f1
class C f1
class D f1
class E f1
class B1 f3
class C1 f3
class D1 f3
class E1 f3
React 中的格式化组件
javascript123456789101112131415161718192021import { useTranslation } from "react-i18next"; function Price({ value, currency }) { const { i18n } = useTranslation(); const formatter = new Intl.NumberFormat(i18n.language, { style: "currency", currency, }); return <span>{formatter.format(value)}</span>; } function PostDate({ date }) { const { i18n } = useTranslation(); return ( <time dateTime={date}> {new Intl.DateTimeFormat(i18n.language, { year: "numeric", month: "long", day: "numeric", }).format(new Date(date))} </time> ); }
六、RTL 排版与语言切换的视觉适配
阿拉伯语、希伯来语等从右向左排版。RTL 的正确实现依赖逻辑属性而非物理属性。
RTL 适配策略
flowchart TB
classDef t1 fill:#e3f2fd,stroke:#1976d2,color:#0d47a1
classDef t2 fill:#f3e5f5,stroke:#8e24aa,color:#4a148c
classDef t3 fill:#e0f7fa,stroke:#00838f,color:#004d40
A[RTL 适配] --> B[文档级<br/>html dir=rtl]
A --> C[样式级<br/>逻辑属性]
A --> D[组件级<br/>图标与箭头方向]
B --> B1[浏览器自动镜像布局]
C --> C1[margin-inline-start 替代 margin-left]
C --> C2[inset-inline 替代 left/right]
D --> D1[箭头图标按 dir 翻转]
click C1 "https://developer.mozilla.org/zh-CN/docs/Web/CSS/margin-inline-start" "逻辑属性文档"
class A t1
class B t1
class C t2
class D t2
class B1 t3
class C1 t3
class C2 t3
class D1 t3
要点:布局尽量使用逻辑属性(inline/block 系);图标方向性(箭头、chevron)用 CSS 按 [dir="rtl"] 翻转;文本对齐用 text-align: start。
七、自动化工具链
i18n 工程化的最后一块拼图是自动化:文案抽取、缺失检查、翻译协作与 CI 门禁。
工具链架构
flowchart LR
classDef a1 fill:#e3f2fd,stroke:#1976d2,color:#0d47a1
classDef a2 fill:#f3e5f5,stroke:#8e24aa,color:#4a148c
classDef a3 fill:#e0f7fa,stroke:#00838f,color:#004d40
A[源码中的翻译调用] --> B[自动抽取<br/>i18next-parser]
B --> C[资源文件更新]
C --> D{缺失检查}
D -->|翻译缺失| E[CI 拦截]
D -->|齐全| F[构建通过]
C --> G[翻译平台同步<br/>Crowdin / Lokalise]
click B "https://github.com/i18next/i18next-parser" "i18next-parser 文档"
class A a1
class B a1
class C a2
class D a2
class E a3
class F a3
class G a2
推荐实践:i18next-parser 自动从源码抽取 key;CI 中检查「新增 key 是否所有语言都有翻译」;翻译平台(Crowdin 等)对接外部译者工作流。
八、总结
React 国际化的完整体系可以浓缩为五层:
- 文案层:资源文件按命名空间组织,插值与复数约定清晰;
- 接入层:react-i18next 初始化 + useTranslation 使用;
- 切换层:changeLanguage + 持久化 + URL 前缀(SSR/SEO);
- 本地化层:Intl 格式化数字日期货币 + RTL 逻辑属性;
- 工程化层:自动抽取、缺失门禁、翻译平台协作。
实践建议:国际化要「早做」,不要等项目写完再补——抽文案的成本随项目膨胀指数增长;从第一天就用 t() 包裹所有用户可见文本。下一篇我们将探讨 React 无障碍访问 a11y 的完整实践。
