document.documentelement.lang 动态修改无效,因浏览器和辅助技术仅读取首屏 html 的 lang 属性;正确做法是服务端注入、静态生成多语言 html 或整节点替换,并严格遵循 bcp 47 规范(如 zh-cn)。

只改 document.documentElement.lang 不会触发屏幕阅读器重读、浏览器翻译按钮激活或 :lang() 样式重计算——它只是个属性值,DOM 已渲染完成,没人重解析。
为什么 document.documentElement.lang = 'en-US' 没效果
浏览器和辅助技术(如 NVDA、VoiceOver)只读取首屏 HTML 中的 。JS 后续赋值不会让语音引擎切换发音规则,Chrome 也不会因此显示翻译按钮。SEO 抓取、拼写检查、字体 fallback 链也都基于初始值。常见错误现象包括:切换后朗读仍是中文、表单 placeholder 没变、:lang(zh) q::before 样式不生效。
- SSR 页面必须在服务端模板里就注入正确
lang值(如根据Accept-Language头) - 静态站(Hugo/Jekyll)应为每种语言生成独立 HTML,
lang硬编码进模板 - 纯前端 SPA 若坚持不刷新,只能用
document.documentElement.outerHTML = newHtmlString替换整根节点(但 ARIA 缓存不清,IE/旧 Safari 兼容差)
lang 属性值必须严格符合 BCP 47 规范
写错不报错,但等于没写:zh_CN、Chinese、zh-ch、cn、zh-hans-cn 全部无效。IANA 不收录下划线或三段式码,Chrome 会静默降级为 zh,导致 :lang(zh-CN) 完全不匹配。
- ✅ 正确写法:
zh-CN、en-US、pt-BR、zh-Hans(注意是短横线-) - 多数 CMS 和 SEO 工具只认
zh-CN;zh-Hans仅当你需排除繁体且明确强调“简体字”时才用 - 大小写敏感:
en-us≠en-US,后者才是标准写法
局部混排内容必须显式设 lang,不能靠继承
只定义主语言,不影响内部英文术语、代码块或日文引文。这些节点必须单独加 lang 属性,否则屏幕阅读器会用中文引擎硬读 API 成“阿皮”,<pre class="brush:php;toolbar:false;" lang="bash"></pre> 的语法高亮工具也无法识别语言类型。
-
<p lang="en">API</p>→ 英文术语按英语发音 -
<pre class="brush:php;toolbar:false;" lang="bash">curl -X POST</pre>→ 高亮工具和翻译插件可识别 - 语言切换时,要遍历所有已带
lang的元素,决定是否同步更新(如一段日文引用应始终保留lang="ja",不能被主语言覆盖)
data-i18n + lang 同步更新才是可行路径
真正能运行时切换的最小闭环是:data-i18n 标记文案节点 + JSON 语言包 + 同步更新 document.documentElement.lang + 遍历更新子元素 lang 值 + localStorage 持久化。
- 按钮点击后执行:
document.documentElement.lang = nextLang,再querySelectorAll('[data-i18n]').forEach(...)替换文本 - 所有显式写了
lang的子元素(如<p lang="en"></p>)也得手动遍历更新,否则它们仍按旧语言渲染 - 表单控件属性需额外处理:
data-i18n-placeholder、data-i18n-title,不能指望一个data-i18n覆盖全部
最易被忽略的点:局部多语言节点的 lang 值不是“继承来的”,而是逐节点生效的;你改了根节点,却忘了那些 <blockquote lang="ja"></blockquote>,它们就会在中文页面里被当成中文朗读——这种问题在无障碍测试中才会暴露,开发阶段几乎看不到。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











