中日韩混排的字形难题:从日文页面字体适配说起
中日韩混排的字形难题:从日文页面字体适配说起
起因:网站纯日文文章的字体问题
前几天把 VCCL 的声库统计数据搬到网站上来后,因为网页内容几乎全都是日文,我便想着使用日文字体解决字形不匹配的问题。使用日文字体很简单,在 head 中引入 Google Fonts 即可,参考我之前换用 Noto Sans SC 的流程:
...
[
"link",
{
href: "https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@100..900&display=swap",
rel: "stylesheet",
},
],
...随后在博客的 palette.scss 中编辑字体引用即可。由于并不是全站都使用JP字形,需要用选择器进行限定。在限定之前,我们还需要标记哪些网页需要使用JP字形。首先想到的是在 YAML Frontmatter 中编辑网页的 lang 属性(直接改为ja-JP);
lang: ja-JP但是这么操作就会导致网页下方的Twikoo字体出现异常:不难推知Twikoo组件直接通过网页html标签中的lang属性来推测网页语言,这样一来不仅中文汉字只能显示为宋体(SimSun),UI的语言也会变为英语。此外,网页正文外的内容,如 navbar 、sidebar 等,也会被迫变为JP字形。
于是我考虑弃用这一方法。既然修改全局根语言属性会引发全局 UI 与第三方组件的异常,那么最优解就是仅对目标页面的正文内容做语言与字体的限定,而 Theme Hope 恰好提供了对应的原生能力。查阅 Theme Hope 文档可知,官方提供了在 YAML Frontmatter 中自定义容器 Class的选项。默认情况下,每个页面都会渲染在 class 为 theme-container 的 div 中。修改页面容器的类名就允许我们使用SCSS选择器匹配特定语言的标记来实现替换字体。
containerClass: lang-ja.theme-container.lang-ja div#markdown-content {
font-family: "TorusPro", "Noto Sans JP", "Noto Sans SC", sans-serif;
}这个选择器即匹配形如
<element class="theme-container lang-ja">
…
<div id="markdown-content">的元素。由于网页中没有类名也是 theme-container lang-ja 的元素,所以没有必要强耦合div这一标签。
什么造成了中日韩字形如此混乱?
这种同一汉字在不同场景下的字形差异,并非字体厂商的设计失误,其根源在于 Unicode 对 CJK 汉字的编码设计决策。
在互联网上,我们时常遇到一个汉字在不同的字体中显示不同的情况。这是因为不同语言中汉字的字形并非完全相同,甚至有很大差异。这一切的源头要追溯到 Unicode 的设计决策。这种同一汉字在不同语言环境下的字形差异,根源在于 Unicode 的 CJK 统一表意文字设计,也就是业内常说的Han Unification(汉字统一编码)。当年 Unicode 与 ISO/IEC 10646 国际编码标准融合时,为了避免同源同义的汉字重复编码,决定将中、日、韩三地同源、语义对等、仅存在地域字形差异的汉字,合并到同一个 Unicode 码位中。

针对同一码位的地域字形差异,行业有两种主流解决方案:一是像 Noto Sans 系列这样,按地区拆分独立的字体文件,直接通过字体引入决定渲染字形;二是制作 CJK 超集字体,在单文件内为同一码位准备多套字形变体,通过 OpenType 的 locl(Localized Forms)特性,匹配元素的lang属性自动切换对应字形。
- 当
lang="ja"时 → 显示日文字形 - 当
lang="zh-Hans"时 → 显示简中字形 - 当
lang="zh-Hant"时 → 显示繁中字形
若仅给页面全局设置单一lang属性,一篇文章内的中日韩混排内容,必然会出现部分文字字形错配;而 HTML 规范原生支持局部语言标记,可对不同语言的内容单独设置lang属性,这也是解决混排字形问题的W3C原生标准方案。
只能说这是 Unicode 在几十年前欠下的技术债,结果就是无数的开发者、UI设计师被 Unicode 坑惨了。Unicode 当年为了省码位统一了汉字,却把“谁来决定这个字长什么样”的责任甩给了字体和渲染引擎。对内容呈现严谨性有要求的开发者而言,极易影响阅读体验与内容质感。
多邻国的解决方案
多邻国作为全球主流的语言学习平台,天然存在大量多语言混排的内容场景。通过分析其页面源码可以发现,多邻国采用了HTML 原生精细化语言标记的核心方案:页面全局根标签设置主语言,所有日语学习内容的区块、短语,均通过<span lang="ja">标签单独标记日语属性,同时为不同lang属性的元素配置对应的字体栈。这套方案的核心,是通过精准的语言标记,同时触发字体的locl地域字形特性与 CSS 字体 fallback 规则,确保日语汉字始终渲染正确的日文字形,同时不影响其他语言内容的正常展示。
更新日志
a573a-于