lang属性值必须全小写,因BCP 47强制语言子标签(如zh、en)小写,地区子标签(如CN)虽语法允许大写但实际兼容性差;ZH-CN等混写会导致html-validate报错、屏幕阅读器失效、SSR静默丢弃lang值。

lang 属性值必须全小写,大写或混合大小写属于非法值
BCP 47 标准明确规定:语言子标签(如 zh、en、fr)必须全部小写;地区子标签(如 CN、US)虽允许大写,但 HTML5 实践中推荐统一小写以避免工具链误判。写成 lang="ZH-CN"、lang="Zh-cn" 或 lang="zh-CN" 中的 CN 大写,虽被部分浏览器“宽容解析”,但会触发以下问题:
-
html-validate工具直接报lang-valid错误,CI 构建失败 - NVDA 和旧版 JAWS 可能跳过中文语音引擎,改用系统默认语种朗读
- W3C Validator 将其标记为 “Invalid language subtag”,影响 SEO 审计得分
- 某些 SSR 框架(如 Astro、SvelteKit)在服务端预渲染时静默忽略该值,导致首屏无语言声明
为什么 zh-CN 是推荐写法,而 ZH-CN 不行
不是浏览器“不支持大写”,而是 BCP 47 对主语言子标签有强制小写要求:zh 合法,ZH 非法;地区子标签 CN 在语法上允许,但实际兼容性差——iOS VoiceOver 16+ 会将 zh-CN 视为标准简体中文,而 zh-CN 中的 CN 若大写,部分 Android TTS 引擎无法匹配到对应语音库。
更关键的是:HTML 解析器在遇到非法子标签时,并不会报错,而是直接丢弃整个 lang 值,等效于 lang=""。这意味着 Chrome 翻译按钮不出现、:lang(zh) CSS 选择器失效、document.documentElement.lang 返回空字符串。
如何批量修复已存在的大小写混用问题
手动改每个 效率低且易漏,建议用工具链自动标准化:
立即学习“前端免费学习笔记(深入)”;
- 用
html-validate配置lang-valid规则,在 CI 中阻断非法值提交 - VS Code 安装插件
HTMLHint,启用attr-lowercase+ 自定义正则检查lang="[^"]*"是否含大写字母 - 脚本快速替换(Linux/macOS):
grep -rl '<html lang=' . | xargs sed -i 's/lang="\(.[^"]*\)"/lang="\L\1"/g'
—— 注意该命令仅处理lang值全小写,不修正下划线或非法子标签 - 若用 Prettier,需配合
prettier-plugin-html并自定义htmlWhitespaceSensitivity,但 Prettier 默认不校验属性值内容,仍需额外插件
动态设置 lang 时最容易忽略的大小写陷阱
服务端模板(如 Django、Nunjucks)或构建时注入 lang 值,常从环境变量或配置读取,比如 {{ site.lang | upper }} 导致输出 lang="ZH-CN"。这类错误不会在本地开发暴露,只在生产环境生效后才被无障碍测试发现。
安全做法是:所有 lang 值来源必须经过标准化函数过滤,例如:
function normalizeLang(lang) {<br> return lang<br> .toLowerCase()<br> .replace('_', '-')<br> .replace(/-([a-z])/g, (m, p1) => '-' + p1.toUpperCase());<br>}<br>// 但注意:BCP 47 要求地区子标签大写是可选的,实际应统一小写更稳妥<br>// 所以最终只需:return lang.toLowerCase().replace('_', '-');
真正麻烦的不是怎么转,而是没人检查它是否被转了——很多团队把 lang 当作普通字符串拼接,忘了它是有严格格式约束的元数据字段。



















