必须在<html>标签设置lang属性,如lang="zh-CN",仅改body或子元素无效;需严格遵循BCP 47规范(小写+连字符),并为局部多语言内容显式声明lang。

不设 lang 或设错,等于主动放弃中文搜索流量和屏幕阅读器支持——这不是优化问题,是基础可用性缺失。
为什么只改 document.body.lang 无效
浏览器、爬虫、VoiceOver、Chrome 翻译按钮,全部只读取 <html lang="zh-CN"> 这个根声明。改 <body> 或任意子元素的 lang,对主语言识别零作用。常见后果包括:Google Search Console 报“未声明页面语言”,Chrome 不显示翻译按钮,NVDA 把“上海”读成 “Shang Hai”(英语重音+无声调)。
实操建议:
- SSR 页面:服务端根据
Accept-Language头直接输出<html lang="zh-CN">,别等 JS 加载 - SPA 切换语言时:必须执行
document.documentElement.lang = "ja-JP",不是body,也不是只更新 React state - 静态多语言站:每个语言版本单独一份 HTML 文件,
<html>上硬编码对应值,避免 JS 注入延迟导致闪动
lang 值必须严格符合 BCP 47 规范
写成 zh_cn、Chinese、ZH-CN、zh-CN (末尾空格)全被忽略——等于没写。浏览器和爬虫只接受小写字母 + 连字符 + 地区码(可选)的格式,例如 zh-CN、en-GB、ja-JP。
立即学习“前端免费学习笔记(深入)”;
容易踩的坑:
- 后端返回
zh_ch,前端没做标准化转换就直接赋给document.documentElement.lang - 误以为
lang="ja"足够:iOS VoiceOver 某些版本会降级为英语发音,lang="ja-JP"更稳妥 - 用
lang="zh"模糊声明:zh-Hans和zh-Hant触发的语音引擎、拼音标注逻辑不同,尤其影响繁体用户
局部多语言内容必须显式声明 lang
浏览器不会自动推断「这段英文是引用」「这行日文是代码注释」。不加 lang,就全按根语言处理——拼写检查失效、语音朗读错、字体 fallback 异常(比如日文假名被中文字体渲染成方块)。
使用场景举例:
- 中文文档里嵌法语引文:
<blockquote lang="fr">Je suis français.</blockquote> - 技术文档中夹英文术语:
<code lang="en">useState</code>,可触发 IDE 插件英文高亮 - 命令行示例:
<pre lang="bash">curl -X POST</pre>——虽然bash不是标准语言码,但被语法高亮工具和部分 TTS 引擎识别
hreflang 和 lang 完全无关,必须各自配齐
hreflang 是给搜索引擎看的链接关系声明,lang 是给浏览器和辅助技术看的内容语言。两者职责分离,不能互相替代。
典型翻车场景:
- 英文页
<html lang="en">,但嵌一段中文说明没加<p lang="zh-CN">→ VoiceOver 用英语念中文词 - 中文页写了
<link rel="alternate" hreflang="en" href="...">指向英文版,但英文版<html>仍为lang="zh-CN"→ 搜索引擎收了hreflang,浏览器却当它是中文页渲染 - JS 动态注入
<link rel="alternate">标签:爬虫基本不执行 JS,等于没写;必须后端静态输出或构建时生成
最易被忽略的一点:同一页面内中英混排时,lang 是比 CSS font-family 更底层的语言感知开关。某些字体(如 Noto Sans CJK)内部嵌了语言分支规则,只有匹配 lang 才启用对应字形变体。



















