lang属性是浏览器解析语言的唯一依据,必须严格设在<html>标签且符合BCP 47标准(如zh-CN),其他位置无效;动态内容需显式标注,否则导致翻译、朗读、索引等功能失效。

lang属性不是可选装饰,而是浏览器解析语言的唯一依据
不设 lang 属性,页面就等于没声明自己说什么语言——Chrome 翻译按钮不出现、NVDA 用英语读“北京”、Google 把中文页当英文索引。这不是体验问题,是基础语义缺失。
浏览器、搜索引擎、屏幕阅读器只认 document.documentElement.lang(即 <html lang="...">),其他位置写的 lang 全部无效。写在 <body> 或某个 <div> 上,等于没写。
常见错误现象:
-
<body lang="zh-CN">→ 页面被当作“无语言声明”处理 - 后端返回
lang="zh_cn"或lang="ZH-CN"→ 浏览器忽略,降级为默认语言 - SPA 切换语言只改
state或容器 class → 语音朗读、标点间距、字体 fallback 全部错乱
BCP 47 语言码必须严格合规,小写+连字符缺一不可
写 lang="chinese"、lang="zh"、lang="zh-Hans" 都可能被忽略或降级。浏览器不“猜”,只按标准匹配。
立即学习“前端免费学习笔记(深入)”;
实操建议:
- 简体中文用
zh-CN(不用zh,Safari 和部分语音引擎对zh支持不稳定) - 繁体中文优先用
zh-Hant-TW或zh-Hant-HK(zh-TW在 Safari 的:lang()CSS 选择器中匹配不准) - 英文区分
en-US(color)和en-GB(colour),影响拼写检查词典加载 - 所有动态生成的
lang值,必须小写 + 连字符 + 无空格,前端需做标准化清洗
局部多语言内容必须显式标注,继承根本不存在
浏览器在 DOM 解析初期就固化每个节点的语言上下文,之后改根节点对已渲染内容完全无效。不会自动推断“这段是引用”“这行是代码注释”。
使用场景与写法:
- 外文段落:
<blockquote lang="fr">Je suis français.</blockquote> - 技术术语:
<p>我们使用<code lang="en">React Router</code>实现路由</p> - 代码块注释:
<pre lang="en"># Initialize counter</pre>(别写lang="bash",它不是 BCP 47 语言码) - 品牌名/标识符:
<span lang="en" translate="no">API</span>,避免误翻且保留英文发音
容易踩的坑:
- 给每个单词都加
lang→ DOM 体积膨胀,可访问性树构建变慢 - 第三方组件插入的 DOM 自带
lang→ 可能覆盖父级,需主动检查并重置 - Shadow DOM 中
lang不自动继承 → 必须手动生成或注入
动态语言切换必须同步更新所有 lang 属性,不只是根节点
只执行 document.documentElement.lang = "ja-JP",对已加载的子元素毫无作用。屏幕阅读器、拼写检查器、字体引擎全按**每个元素自身的 lang 值**工作。
关键条件:
- SSR 页面:服务端必须根据
Accept-Language头注入<html lang="...">,否则首屏声明与内容不符 - SPA 页面:语言切换钩子中,不仅要改根节点,还要遍历所有含文本的动态节点,重设其
lang - 动态插入内容(如
innerHTML、框架渲染):必须显式设置新节点的lang,否则无语言上下文 - Vue/React 组件内嵌文本:若未通过 prop 透传语言信息,内部文本节点将沿用根语言,而非实际语种
最难的从来不是写对一个 lang="zh-CN",而是让每个跨框架、嵌套引入、服务端流式渲染的文本节点,都带着准确的语言上下文被解析。这点在 SSR + CSR 混合架构里尤其容易漏。



















