data-i18n标记必须覆盖所有可翻译节点,包括textContent及placeholder、title、aria-label、alt等属性;lang属性须逐层设置而非仅改documentElement;深层嵌套(超6层)易漏翻译;非首屏内容应使用<template>而非display:none。

data-i18n标记必须覆盖所有可翻译节点,不能只扫textContent
很多团队只给 <button data-i18n="btn_submit">提交</button> 加基础标记,却漏掉 placeholder、title、aria-label、alt 这些属性——结果语言切换后输入框提示仍是英文,屏幕阅读器读错内容,Lighthouse 直接报“labelled-by-missing”或“aria-allowed-attr”错误。
实操要点:
-
data-i18n是基础,必须加在所有含文本的语义元素上:<h1>、<p>、<label>、<button>、<option> - 含属性值的,额外加对应后缀:
data-i18n-placeholder、data-i18n-title、data-i18n-alt、data-i18n-aria-label -
value属性一般不翻译(属于用户输入),但label的for必须与input的id严格匹配,否则辅助技术失效 - 含 HTML 结构的文案(如“请阅读使用条款”)必须用
innerHTML替换,且语言包里对应值得是可信纯 HTML 片段;否则有 XSS 风险 - 动态插入的 DOM(弹窗、AJAX 表格行)插入后必须立即调用翻译函数,不会自动监听
lang属性必须逐层设置,不能只改documentElement.lang
只执行 document.documentElement.lang = 'zh-CN' 是无效的。浏览器和屏幕阅读器按每个元素自身的 lang 属性决定行为:标点间距(中文顿号 vs 英文逗号)、字体回退链、语音合成引擎的语言选择,全依赖具体节点上的 lang 值,不是继承来的。
常见错误现象:
立即学习“前端免费学习笔记(深入)”;
- 切换语言后,
<input placeholder="Search">提示仍读英文 - 中文页面里嵌套的
<pre lang="bash">被强制套中文字体,代码显示错乱 - 页面闪动、语音播报滞后,本质是旧
lang值还在生效
实操建议:
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
- 切换前先收集所有已设
lang的元素:document.querySelectorAll('[lang]') - 对每个匹配元素执行
el.lang = newLang,包括<p lang="en">、<blockquote lang="ja">、<pre lang="bash"> - 明确需保留原语言的场景(如代码块、引文)除外,其余一律同步更新
- 特别注意表单控件:
placeholder、title、aria-label的语言行为,都绑定在宿主元素自身
DOM深度超6层会导致data-i18n节点漏遍历
当 data-i18n 出现在 <div><div><div><div><div><p data-i18n="msg">…</p></div></div></div></div></div> 这类纯样式堆叠结构里时,JS 遍历逻辑或 SSR 初始化脚本常因性能阈值跳过第7层及更深节点——结果是语言切换后部分文字没变,控制台也无报错,极难定位。
验证方式:
- 在 DevTools Elements 面板右键目标元素 → “Reveal in Elements panel”,手动数从
<body>到该data-i18n元素的层级(含<body>) - 超过 6 层即高风险;尤其 SSR 渲染的首屏 HTML 中,若
data-i18n在第7层出现,服务端 i18n 初始化大概率漏掉
解决路径:
- 把冗余
<div>替换为语义标签:<main>、<section>、<article>—— 它们天然中断嵌套深度,同时支持 i18n 工具识别作用域 - 深层嵌套容器若承载业务逻辑(如权限控制 wrapper),优先用
<template>拆离渲染,而非堆<div> - 运行
document.querySelectorAll("*").forEach(el => el.children.length > 50 && console.log(el.tagName, el.children.length))扫出子节点爆炸的容器,优先重构
非首屏HTML必须用<template>,不能靠display: none
display: none 或 visibility: hidden 不是懒加载,只是隐藏。DOM 已建、CSSOM 已算、内存已占,首屏解析拖慢 300ms+,FCP 直接超标。轮播图下方的推荐区、评论列表、侧边栏,初始 HTML 里必须彻底删掉,而不是藏起来。
正确做法:
- 用
<template id="comments-template">包裹非首屏片段 - 服务端渲染(SSR)时,
<template>内容不输出 HTML,避免冗余传输 - 首屏加载完成后,用
document.getElementById('comments-template').content.cloneNode(true)动态挂载 - 配合
IntersectionObserver触发挂载,比滚动事件更轻量、更可靠 - 挂载后立即调用翻译函数,否则
data-i18n不生效
复杂点在于:语义标签替换不是搜索替换,而是契约重写;<main> 页面只能有一个,<nav> 不能塞面包屑,<section> 必须有可访问标题——这些约束一旦违反,Lighthouse 会硬性报错,且无法通过简单 JS 补救。


















