:lang()仅匹配带正确BCP 47格式lang属性的元素,真正生效需配合精准font-family声明、HTML显式标注、足够特异性及合理字体栈排序,缺一不可。

:lang() 本身不“应用字体”,它只负责匹配带 lang 属性的元素;真正生效靠的是你写的 font-family 声明,且必须配合正确的 HTML 标注、特异性提升和字体栈排序,否则大概率被全局样式覆盖或 fallback 失控。
HTML 的 lang 属性必须写对,且不能只靠 <html lang="zh">
浏览器不猜语言,只认属性值。常见失效原因就是只在 <html> 上写了 lang,但正文段落、术语、代码块没继承或没显式标注。
-
lang值必须符合 BCP 47 规范:用短横线(zh-CN),不用下划线(zh_CN)或大写(ZH-CN) - 多语言混排时,英文术语必须包裹在带
lang="en"的元素里,比如<code lang="en">API</code>或<span lang="en">HTTP</span> - React/Vue 中动态渲染内容,
lang要随数据一起注入,SSR 首屏必须输出真实值,否则 CSS 不匹配 - Safari 对动态插入的
lang属性支持更弱,建议在 DOM 加载前就写好,别等 JS 补
:lang(zh) 和 [lang="zh-CN"] 的匹配逻辑完全不同
误用选择器是字体不生效的第二大原因。两者行为差异极大:
-
:lang(zh)是语义化匹配:能命中lang="zh"、lang="zh-CN"、lang="zh-Hans",还支持祖先继承 -
[lang="zh-CN"]是字符串精确匹配:只认字面完全一致的lang="zh-CN",不继承、不降级、不模糊 - 写
:lang(zh-CN)没意义——它只匹配lang="zh-CN",不如直接用[lang="zh-CN"],且失去继承能力 - 别写
:lang("zh")或:lang(zh-cn):括号内不加引号,连字符小写是允许的,但推荐统一小写+短横线风格
font-family 声明顺序错一位,中日韩混排就出视觉 bug
中日韩共用 CJK 字体(如 Noto Sans CJK)看似省事,实则埋雷:同一字体对简体中文、日文假名、韩文字母的支持质量不一,且系统渲染优先级混乱。
立即学习“前端免费学习笔记(深入)”;
- 每个
:lang()规则必须有**独立、精简的字体栈**::lang(zh) { font-family: "SF Pro SC", "HarmonyOS Sans SC", sans-serif; } - 避免把日文字体(如
"Hiragino Kaku Gothic Pro")放在中文规则里——它可能抢走中文汉字渲染权,导致标点宽度异常 - 西文字体必须放在中日韩字体之后:
:lang(zh) { font-family: "PingFang SC", "Helvetica Neue", sans-serif; },否则英文会被中文字体强行渲染,字母间距过紧 - 别在
font-family开头放sans-serif:它会跳过你指定的所有专有字体,直接用系统默认
为什么 :lang(zh) { font-family: ... } 经常被忽略?三个硬性条件缺一不可
不是语法错,而是环境没配齐。以下三点任意一条不满足,规则就等于没写:
-
特异性太低:如果
body { font-family: Arial; }这类全局声明存在,它的权重就压过了:lang(zh)。解决办法是提高选择器权重,比如写成body :lang(zh)或article :lang(zh) -
字体 fallback 机制干扰:即使规则命中,若第一字体缺失某个字符(比如英文标点),浏览器会按顺序回退,可能落到你不期望的字体上。用
unicode-range切分 @font-face 是更稳方案 -
HTML 根节点
lang值与子元素冲突:比如<html lang="en"><p lang="zh">你好</p>,这时p:lang(zh)有效,但body:lang(zh)就不会触发——因为body自身没lang,继承的是html的en
最易被忽略的一点:移动端 WebView(尤其安卓旧版)对 :lang() 的继承支持不稳定,局部标注比依赖根节点更可靠。


















