data-i18n属性需JS运行时解析,浏览器不识别其语义;必须在DOM加载后执行翻译逻辑,语言包应按lang标识动态加载,避免localStorage存整份JSON;服务端渲染需预翻译HTML,客户端补全仅作fallback。

data-i18n 属性必须配合 JS 运行时才能生效
HTML 本身不解析 data-i18n,它只是个标记;真正起作用的是你写的 JS 逻辑——比如遍历所有带该属性的元素,再从语言包里取值填进去。浏览器不会自动做这件事,也不认这个属性名的语义。
常见错误是只加了 data-i18n="login.title" 就以为页面会自动翻译,结果刷新后还是英文。这时候要检查:JS 是否已加载?语言包是否已载入?t() 函数是否定义并能正确返回字符串?
- 必须在 DOM 加载完成后执行替换(如用
DOMContentLoaded或框架的 mounted 钩子) - 如果用了服务端渲染(SSR),
data-i18n只适合客户端补全,不能依赖它做首屏渲染 - 避免在 innerHTML 赋值后动态插入带
data-i18n的新节点,除非手动调用翻译函数重新扫描
localStorage 存的是语言偏好,不是翻译内容
用户切换语言后,应该只存 localStorage.setItem('lang', 'zh-CN') 这类标识,而不是把整份中文 JSON 塞进去。前者轻量、可预测;后者浪费空间,且更新语言包时旧缓存无法自动失效。
读取时也别直接从 localStorage 拿翻译文本——那会卡死维护节奏。正确做法是:取 lang 值 → 拼出资源 URL(如 /locales/zh-CN.json)→ fetch 加载 → 缓存到内存对象里。
立即学习“前端免费学习笔记(深入)”;
- localStorage 容量有限(通常 5MB),存大 JSON 易触发 QuotaExceededError
- 不同语言包版本升级后,本地存的旧 JSON 不会自动更新,导致界面错乱
- 敏感操作(如登录页)建议禁用 localStorage 读写,防止跨标签页干扰
dataset.profile = JSON.stringify(...) 是反模式
把整个配置对象序列化后塞进 dataset.profile,看似方便,实则埋雷:DOM 属性只能存字符串,dataset 赋值不会真正写入 HTML,且命名不规范时(比如含大写字母或数字)会静默失败。
更糟的是,一旦你用 el.dataset.profile = '{"name":"Alice"}',后续通过 getAttribute('data-profile') 读出来是原始字符串,但 dataset.profile 却可能是 undefined——因为 dataset 对连字符转驼峰有强制规则,且不处理非法命名。
- 写入一律用
setAttribute('data-profile', JSON.stringify(obj)) - 读取一律用
getAttribute('data-profile'),再手动JSON.parse() - data-* 属性只适合存轻量元数据(如 ID、状态码、简单 flag),别当 mini 数据库用
后端返回 HTML 片段时,i18n 必须预渲染完成
如果你的接口返回的是带文本的 HTML 字符串(如 { content: '<h2 data-i18n="welcome"></h2>' }),那前端 JS 再去扫描 data-i18n 就晚了——这段 HTML 已经被 innerHTML 插入,事件监听器、翻译逻辑都丢失了。
正确做法是:后端模板引擎(如 Thymeleaf、Jinja2)在吐出 HTML 前,就根据请求头 Accept-Language 或参数 ?lang=ja 查好对应语言文本,直接渲染成最终 HTML(<h2>ようこそ</h2>)。这样既安全,又避免客户端二次处理。
- 若必须返回带 data-i18n 的片段,请确保插入后立即调用翻译函数(如 i18next.t())处理新节点
- 注意 XSS 风险:后端返回的 HTML 必须经过净化(如 DOMPurify.sanitize),不能直接 innerHTML
- 服务端渲染的 HTML 中,
data-i18n应仅作为 fallback 或调试标记,不可作为主逻辑依赖
实际项目里最容易被忽略的,是语言切换后 DOM 节点复用导致的残留文本——比如 Vue/React 组件没重置 data-i18n 属性值,或旧节点没销毁,结果新语言加载了,界面上却混着两套文案。这问题不报错,但用户一眼就能感知。



















