lang属性必须显式声明有效BCP 47值,禁止为空或非法值;空值导致读屏中断、SEO失效、CSS选择器不匹配;应使用zxx占位,服务端直出真实语言,构建工具需禁用removeRedundantAttributes压缩。

lang属性不能留空,必须显式声明有效BCP 47值
HTML规范不接受lang=""或lang="undefined"这类空值——浏览器会静默降级为lang="und"(未知语言),导致屏幕阅读器跳过TTS引擎、搜索引擎忽略语言信号、:lang()选择器完全失效。
常见错误现象:<html lang="">在Chrome DevTools里看似“存在”,但VoiceOver读不出中文,Google Search Console报“未指定语言”,CSS中html:lang(zh-CN) { font-family: ... }也不匹配。
- 绝对不要写
lang=""、lang="null"、lang="auto"——这些都不是BCP 47标准值,全被忽略 - 若页面语言尚未确定(如SSR首屏前、i18n初始化中),应先用占位值
lang="zxx"(表示“无语言内容”),等语言确定后再JS更新:document.documentElement.lang = "zh-CN" - 服务端渲染场景下,务必在初始HTML中就写出真实语言值;JS后期赋值只是补救,对读屏软件和SEO无效
lang=""被误删后怎么快速定位和修复
构建工具(如Vite+html-minifier-terser)默认开启removeRedundantAttributes,会把lang="zh-CN"当作冗余属性删掉,结果变成<html>——表面看没报错,实则丢失全部语言语义。
验证方法:打开DevTools Elements面板,直接搜索<html,确认是否存在lang属性;再在Console执行document.documentElement.lang,返回""就说明已被删。
立即学习“前端免费学习笔记(深入)”;
- Vite项目:在
vite.config.ts中显式禁用该压缩项:minify: { removeRedundantAttributes: false } - Webpack+
html-webpack-plugin:配置minify: { removeRedundantAttributes: false } - 手动检查
index.html源码,确保<html lang="zh-CN">在构建前后都存在,而非仅靠JS插入
动态切换语言时lang=""不是过渡态,而是故障信号
单页应用(SPA)中,用户点击切换语言,常见错误是先清空再赋值:document.documentElement.lang = "" → document.documentElement.lang = "en-US"。这个中间的""状态会让已激活的语音引擎中断、翻译菜单消失、CSS重排失效。
正确做法是跳过空值,直接原子更新:
- 用
dataset暂存目标语言:document.documentElement.dataset.nextLang = "en-US",再一次性赋值 - 或使用
requestIdleCallback批量更新所有带lang的元素,避免DOM反复重绘 - 切勿在
lang上做条件渲染,比如React中写<html lang={locale || ""}>——这等于主动引入bug
表格单元格和代码块里lang=""比不写更危险
<td lang="">API</td>比<td>API</td>问题更大:前者明确告诉浏览器“这段内容无语言”,读屏软件可能跳过或乱读;后者至少会继承<html lang="zh-CN">的上下文。
表格、<pre>、引用块等局部多语言场景,必须按实际内容设值:
-
<td lang="en">API</td>✅ 显式声明英文术语 -
<pre lang="en"># Initialize counter</pre>✅ 注释用英文,不是lang="bash" -
<blockquote lang="fr"></blockquote>✅ 引文语义清晰,比<div lang="fr">更可靠 - 数值、日期、单位(如
2026-09-21、USD)不用lang,它们本就不参与语言处理
最常被忽略的一点:lang属性一旦写错格式(如下划线zh_CN)、拼错值(如chinese)、或出现在非<html>标签上,效果等同于没写——而浏览器不会给你任何提示。



















