前端国际化 i18n 最佳实践:多语言和 RTL

域名注册
域名注册 正式会员超兽战士 👑年卡会员
发布于 2026-10-07 16:18 ·1 浏览 ·0 回复

前端国际化(i18n)的核心结论:把「文案、数字、日期、布局方向」四件事全部交给标准化的运行时方案处理,而不是靠字符串拼接和左右方向的硬编码。具体做法是——文案用 ICU MessageFormat 描述复数与变量,格式化用原生 Intl API,RTL 用 dir 属性加 CSS 逻辑属性,测试用伪语言(如 en-XA)在开发阶段暴露问题。

i18n 和 l10n 有什么区别?

结论:i18n(internationalization,国际化)是把代码改造成「能适配任意语言」的架构工作,只做一次;l10n(localization,本地化)是往这个架构里填具体某种语言的内容,每加一种语言做一次。

很多人把两者混为一谈,结果代码里塞满了 if (lang === 'zh') 这类分支。正确的边界是:i18n 负责接口、目录结构、格式化规则、方向切换;l10n 负责翻译文件本身。翻译文件建议按命名空间拆分,例如 common.json、checkout.json,配合动态 import 按路由懒加载,避免首屏加载 10 种语言的完整词条。

多语言文案怎么写才不出错?

结论:禁止用字符串拼接,一律使用 ICU MessageFormat,因为它能同时表达变量插值、复数和性别。

典型写法:

{count, plural, one {# 项} other {# 项}}

英文会命中 one(1 item)和 other(2 items),中文、日文只有 other,阿拉伯语则有 zero/one/two/few/many/other 六种形态——这正是拼接法必死的场景。

在框架层面,react-i18next 14.x / i18next 23.x、vue-i18n 9.x、FormatJS 都内置了 ICU 解析。key 命名建议用「模块.语义」,例如 checkout.submit_button,不要用 checkout.text1,因为后者在改文案时会失联。

数字、日期、货币怎么格式化?

结论:不要手写 toFixed 或 yyyy-MM-dd 拼接,统一用 Intl 系列 API,浏览器已内置全部语言的数据。

new Intl.NumberFormat('de-DE').format(1234.56)   // "1.234,56"
new Intl.NumberFormat('zh-CN').format(1234.56)   // "1,234.56"
new Intl.DateTimeFormat('en-US', { dateStyle: 'medium' }).format(d)
new Intl.RelativeTimeFormat('ja', { numeric: 'auto' }).format(-1, 'day') // "昨日"

时间存储一律用 UTC 的 ISO 8601 字符串,展示时再按用户时区格式化;时区来源优先用户设置,其次 Intl.DateTimeFormat().resolvedOptions().timeZone。语言标签必须用 BCP 47 规范,简繁中文写成 zh-Hans 和 zh-Hant,而不是 zh-CN / zh-TW 硬绑地区。

RTL 布局怎么做?

结论:只需要把根元素的 dir 设为 rtl,再用 CSS 逻辑属性替代 left/right,Flex 和 Grid 会自动镜像,不需要写两套样式。

需要 RTL 的语言主要是阿拉伯语(ar)、希伯来语(he)、波斯语(fa)、乌尔都语(ur)。改动清单如下:

  1. margin-left/right → margin-inline-start/end
  2. padding-left/right → padding-inline-start/end
  3. left/right 定位 → inset-inline-start/end
  4. text-align: left/right → text-align: start/end
  5. border-radius 四角 → border-start-start-radius 等逻辑版本

这些逻辑属性在 Chrome 87、Firefox 66、Safari 15 之后全面可用,2024 年后的项目可以放心全量使用。若仍需兼容老浏览器,用 postcss-rtlcss 或 rtlcss 在构建时生成镜像样式表。

RTL 下哪些东西不能镜像?

结论:会翻转的和不会翻转的要分开处理——方向性图标(返回箭头、进度指示)需要镜像,而时钟、播放按钮、品牌 Logo、货币符号不能镜像。

具体做法是给需要镜像的图标加一个工具类:

[dir="rtl"] .icon-directional { transform: scaleX(-1); }

另一个高频坑是双向文本(bidi)混排:当一段 RTL 文字里插入英文或数字时,标点位置会跑到错误的一端。解决办法是给动态内容加 dir="auto" 或包裹 <bdi> 元素,CSS 上用 unicode-bidi: isolate 隔离,避免污染周围文本顺序。

怎么测试国际化做没做对?

结论:用伪语言(pseudo-locale)在开发阶段跑一遍,能在翻译之前就暴露 90% 的布局问题。

伪语言如 en-XA 会把英文字符替换成带变音符号的加长字符(通常加长 30%–50%),用来验证文案变长时按钮是否撑破。补充手段包括:把语言切到 ar 检查 RTL 镜像是否完整;把系统时区改成 Pacific/Kiritimati(UTC+14)验证日期不串天;用 Intl.NumberFormat('ar-EG') 检查阿拉伯语数字是否按预期显示为 ٠١٢٣ 或 1234——两者都合法,取决于产品定位,需要提前定标准。

最后提醒一点:语言检测顺序建议为「URL 前缀(如 /ar/)→ 用户显式设置 → navigator.language → 服务端 Accept-Language → 默认语言」,并且 URL 必须携带语言标识,否则分享链接和 SEO 都会失效。

归纳一下:文案走 ICU、格式走 Intl、方向走 dir + 逻辑属性、测试走伪语言。这四步做扎实,新增一种语言的成本就从「改一轮 UI」降到「加一个 JSON 文件」。

版权声明:本文来自 GJ站长论坛《前端国际化 i18n 最佳实践:多语言和 RTL》
原文链接:https://www.gj0.com/thread-394.html
转载请注明出处并保留本声明;内容仅代表作者观点,与本站立场无关。若本文涉嫌侵权,请联系本站处理。

全部回复 0

还没有回复,来抢沙发~