:lang()伪类仅匹配显式声明且符合bcp 47标准的lang属性,不继承、不推断、不fallback;必须在或目标元素上写合法值(如zh-cn),并列声明多变体或使用[lang^="zh"]兜底,字体链须将语言专用字体置首。

lang属性没写对,:lang()根本不会触发
浏览器只认显式声明的 lang 属性值,不推断、不继承、不 fallback。哪怕页面全是中文,<p>你好</p> 没加 lang="zh",:lang(zh) 就完全无效。
必须确保至少根节点有合法值:。更稳妥的做法是:在 DOMContentLoaded 后用 JS 设置,避免硬编码死值:
document.documentElement.lang = navigator.language || 'en';
注意大小写敏感——lang="ZH" 不会被 :lang(zh) 匹配;也别用 zh-ch 这类非法码,得严格按 BCP 47(如 zh-Hans、zh-Hant)。
:lang(zh) 和 :lang(zh-CN) 匹配行为不一致
:lang(zh) 理论上匹配所有以 zh 开头的值(zh-CN、zh-TW、zh-Hans),但 Safari 某些版本会跳过它,只认精确值。Chrome 和 Firefox 表现更宽松。
- 最稳写法是并列声明:
html:lang(zh), html:lang(zh-CN), html:lang(zh-Hans), html:lang(zh-Hant) - 或改用属性选择器兜底:
html[lang^="zh"],但要注意它可能误中lang="zhx"这类非法值 - 混合内容(如中英日混排)必须给每段文字单独加
lang:<p lang="ja">こんにちは</p>,不能指望父级lang继承生效
字体链顺序错一个位置,整条规则就失效
:lang() 规则里,font-family 的顺序决定渲染优先级。常见错误是把通用 fallback 放最前,比如:
:lang(zh) { font-family: sans-serif, "PingFang SC", "Noto Sans SC"; }
这会导致浏览器直接用 sans-serif(如 Helvetica),后面所有中文字体全被跳过。
正确写法必须把该语言强支持的字体放最前:
-
:lang(zh)→"PingFang SC", "Hiragino Sans GB", "Noto Sans SC", system-ui, sans-serif -
:lang(ja)→"Hiragino Kaku Gothic Pro", "Yu Gothic", "Noto Sans JP", system-ui, sans-serif -
system-ui要放在自定义字体之后、sans-serif之前,否则 macOS/Windows 渲染行为不一致
切换语言后字体闪动,不是 swap 的锅,是没预加载
用户切换语言时,JS 修改 document.documentElement.lang,浏览器重新匹配 :lang() 规则,触发新字体加载。若用了 font-display: swap,就会出现回退字体 → 新字体闪入的过程。
解决方式不是禁用 swap,而是提前加载关键语种字体资源:
<link rel="preload" href="/fonts/noto-sc.woff2" as="font" type="font/woff2" crossorigin><link rel="preload" href="/fonts/noto-jp.woff2" as="font" type="font/woff2" crossorigin>
同时确保不同语种对应的 @font-face 声明中 font-family 名互不冲突(例如分别叫 "NotoSC" 和 "NotoJP"),否则浏览器可能复用缓存但套错字形。
真正容易被忽略的是:字体链里每个字体的字符集覆盖范围是否完整。比如只配了 "Noto Sans SC",但用户系统里缺它,又没 fallback 到能撑起日文假名的字体,结果就是部分字符显示为方块——这不是 CSS 写错了,是字体选型本身没兜住多语种边界。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











