最轻量可控的i18n方案是用data-i18n标记+按功能域拆分JSON语言包(如common.json、form.json),键名扁平化(如"form.email.label")、无空格中文,HTML中需区分textContent/属性/innerHTML场景,并同步更新document.documentElement.lang及各语义标签lang属性。

直接用 data-i18n 标记 + 按模块拆分的 JSON 文件是最轻量、最可控的方案,不用引入框架也能跑得稳;但路径错、键名漏、lang 属性没同步,三处一错,翻译就只在控制台里“显示”。
JSON 语言包怎么组织才不散乱
别把所有翻译塞进一个 en.json 或 zh.json。大项目一开,查个按钮文案得 Ctrl+F 扫半分钟。
- 按功能域拆:比如
common.json(登录/导航)、form.json(表单字段)、error.json(验证提示),避免重复键名冲突 - 嵌套结构要扁平:用点号分隔,如
"form.email.label": "邮箱地址",而不是{"form": {"email": {"label": "邮箱地址"}}—— 前者 JS 查表快,后者容易写错层级 - 键名别带空格或中文:
"submit_btn"可以,"提交按钮"或"submit btn"会触发解析失败或大小写混淆 - 所有语言包保持键名完全一致:少一个键,对应元素就留白;多一个键,纯属冗余,还可能被误读
HTML 中 data-i18n 怎么用才不出错
它不是万能胶,只管 textContent,对 placeholder、alt、title 这些属性完全无效 —— 这是 90% 翻译失效的根源。
- 基础文本:直接写
<h2 data-i18n="home.title">首页</h2>,运行时替换整个文本内容 - 属性文本:必须显式加后缀,例如
<input data-i18n-placeholder="search.hint" placeholder="Search...">,否则 placeholder 永远不变 - 带 HTML 的文案(如“请阅读服务条款”):要用
innerHTML替换,但语言包里对应值必须是已转义的可信 HTML 片段,不能拼接用户输入 - 别给
<script>或<style>加data-i18n—— 它们不渲染文本,加了也没用
切换语言时哪些 DOM 必须手动处理
静态页面加载完翻译一次,不代表万事大吉。AJAX 插入、动态组件、懒加载区块里的 data-i18n 全是“哑标签”,没人理它们。
立即学习“前端免费学习笔记(深入)”;
- 弹窗、抽屉、模态框:插入 DOM 后立即调用翻译函数遍历内部
data-i18n元素,不能等下次全局刷新 - 表格分页加载新
<tr>:每批数据渲染完,单独对该<tbody>范围执行翻译,避免全页重刷 -
document.documentElement.lang必须同步改:只更新文本不改 lang,标点间距、字体回退、屏幕阅读器语音都会错 - 日期/数字/货币字段:光翻文字不够,还得重建
Intl.DateTimeFormat实例,否则格式仍是旧语言
最容易被忽略的是 lang 属性的粒度 —— 不是设在 <html> 上就结束,每个含文本的语义标签(<p>、<section>、<footer>)都得显式带上,否则中英文混排时顿号变空格、代码块字体崩掉,连 CSS [lang="zh"] 选择器都匹配不到。



















