国际化应统一用data-i18n标记文本、模块化JSON拆分、占位符处理动态内容、DOM批量更新、重建Intl实例、同步三方库locale、保留用户状态、服务端优先判定语言、规避localStorage主判定、适配复数/RTL/嵌套结构并人工校对机器翻译。

用 data-i18n + 模块化 JSON 分离翻译与逻辑
硬编码文本或把翻译塞进 JS 字符串,一改全崩。真实业务里按钮文案、表单提示、错误消息、动态拼接语句(如“已选 {count} 项”)混在一起,靠人工维护极易漏改、错配语言键。
- 所有可译文本统一用
data-i18n标记,例如<button data-i18n="form.submit">提交</button> - 按功能域拆分 JSON:
common.json(通用词)、form.json(表单字段/校验)、error.json(后端返回码映射) - 动态内容不拼字符串,用占位符:
"selected.count": "已选 {count} 项",替换时用replace('{count}', value)或更安全的模板函数 - 避免在 JS 里写
if (lang === 'zh') {...} else {...}—— 这类分支会随语言增加指数级膨胀
切换语言时不重载页面,但要同步三方状态
用户点个语言切换就 window.location.reload(),表单数据清空、滚动位置跳顶、第三方组件(如 react-datepicker 或原生 <input type="date">)语言滞后——这不是国际化,是用户体验灾难。
- 只触发一次 DOM 批量更新:遍历所有
[data-i18n]元素,查当前语言包更新textContent或innerHTML(后者需确保值已 HTML 转义) - 时间/数字格式必须重建:不能复用旧的
Intl.DateTimeFormat实例,得重新new Intl.DateTimeFormat(lang) - 调用第三方库的 locale 方法:如
moment.locale(lang)、dayjs.locale(lang);若用 Ant Design,需触发ConfigProvider的 locale 切换 - 保留输入框值、
scrollTo位置、路由参数 —— 这些和语言无关,不该被重置
服务端和前端共用语言判定逻辑,避免首次渲染错乱
navigator.language 在隐私模式下不可靠,纯前端检测又无法 SSR 渲染首屏。结果就是用户看到中文页,但 document.documentElement.lang 是 en,SEO 抓取到的是英文内容,或者接口返回的错误消息语言和页面不一致。
- 后端解析
Accept-Language请求头(用accepts库,别手写正则),注入全局变量:window.__LOCALE__ = 'zh-CN'或设置<html lang="zh-CN"> - 前端初始化时优先读
document.documentElement.lang,fallback 到window.__LOCALE__,再 fallback 到navigator.language,最后兜底为en - 用户手动切换语言后,必须发请求到
/api/locale同步服务端偏好,否则后续 API 错误码、邮件模板、短信内容仍用旧语言 - 不要用
localStorage做主判定源 —— 它可能过期,且新设备首次访问没值,会覆盖掉服务端更可靠的判断
复数、RTL、嵌套结构这些坑,别等上线才踩
英文只有单复数两态,阿拉伯语有六种复数形式;中文无复数,但“1 个”和“2 个”在 UI 上可能需要不同样式;阿拉伯语从右往左(RTL),不只是文字翻转,整个布局(padding-left → padding-inline-start)都得响应式处理。
立即学习“前端免费学习笔记(深入)”;
- 简单复数用
{count, plural, one {# 项} other {# 项}}语法(ICU MessageFormat),别自己写if (count === 1) {...} - RTL 布局用 CSS 逻辑属性:
padding-inline-start替代padding-left,配合dir="auto"或html[dir="rtl"]触发样式切换 - 嵌套翻译(如
<span data-i18n="login.error"><strong>密码错误</strong>,请重试</span>)必须保证 JSON 值含对应 HTML 标签,且前端用innerHTML更新 —— 但得白名单过滤,只允许<strong>、<em>等安全标签 - 机器翻译 API 只用于 UGC 预填充,正式发布前必须人工校对 —— 自动翻译把“登录失败”翻成“登陆失败”,这种错字在生产环境会持续被搜索引擎收录
复杂点不在“怎么切语言”,而在“怎么让语言切换这件事不干扰任何其他逻辑”。每个环节都得默认它会发生,而不是当作一次性的初始化动作。



















