异常现象
对于相同的文字「将来」
Unicode 对应:\u5c06\u6765
中文用户反馈在网页上看到了日文汉字,关注「将」
日文用户反馈在网页上看到了中文汉字,关注「将」
用户预期:
中文用户看到中文的汉字
日文用户看到日文的汉字
为什么会有这种中日用户看到错误的文字问题呢?
其实跟字符的 Unicode 有关系
首先看这两张图:
这些文字(一半日文、一半中文)很相似,都又有点不一样。但他们都有一个共同的特点:对应一个相同的 Unicode 码,尽管有不同的字形(形状)。
这种特殊情况涉及到的字符不多。从没人反馈说看到「おはようございます」有类似问题
那 Unicode 是什么?
简单讲就是:一个包含了世界上大部分字符的标准。每一个字符都分配了一个唯一的代码。
浏览器渲染文字的机制
我们再回到「中文文字异常」的问题,搞清楚这个问题需要先理解浏览器渲染文字的机制。
浏览器在渲染网页上的字符时,其实先看到的是字符的 Unicode 码。
我们用上面这个文字解释。
浏览器在遇到 U+6d45 这个字符时,应该渲染成什么形状呢?
有多个可能性。最终由浏览器决定使用的字体决定
字体(字体文件)是什么?
简单讲就是一个包含了 Unicode 码和对应字形的映射表
系统会内置很多字体,有一些网站也会选择使用 Web 字体。
中文有很多字体:
- 苹方
- 楷体
- 宋体
- 微软雅黑
日文也有很多字体:
- MS 明朝 MS ゴシック
- メイリオ EPSON 教科書体M
- HIragino Kaku Gothic
如果给「浅(U+6d45)」设置成日文字体,那就渲染出
如果给「浅(U+6d45)」设置成中文字体,那就渲染出
所以到这里,对于解决「中日文字异常」问题,下一步的思路应该是:只要给不同语言的用户设置不同的字体,问题就解决了。
确实可行,但可能没必要。
直接交给浏览器处理好了,浏览器能处理好这个问题。
因为浏览器可以自己检测用户的语言,然后选择对应语言的字体。
这有个前提:开发人员没有给文字设置字体(font-family
没有设置任何内容),或者只是设置了通用字体(衬线、非衬线)
但是浏览器如何判断用户的语言呢?
- 首先会先查找网页的
lang
属性,如果没有的话 ⬇️ 查找系统使用的语言
测试过,不会使用浏览器的首选语言设置
所以 第一种解决方案(稍次) 就是开发人员:
- 不设置任何字体(但设置通用字体(衬线、非衬线)无妨)
- 不设置 HTML 的
lang
属性。相当于跟随系统的语言设置。
能解决这次的问题。效果就是:(对于上面那些中日相同 Unicode 的特殊文字)中文系统用户看到的都是中文字形,日文系统用户看到的都是日文字形。
但实际上,设置lang
属性经常是有必要的。尤其是当产品做了多语言支持,已经支持了语言切换。那当用户切换语言时,除了需要使用不同的文案,还需要设置下 HTML 的lang
属性来确保那些特殊文字能正确渲染成符合用户预期的字形。如果这时候不设置,就会看到:界面文案都改成了日文,但用户自己输入日文时却有时能看到中文字型,这是因为用户的系统字体是中文。
所以还是需要开发人员在网页刷新时根据浏览器的首选语言设置 HTML 的lang
属性。
document.documentElement.lang = navigator.language || navigator.userLanguage;
或者当用户主动切换语言时,也顺便设置下 HTML 的lang
属性。
所以解决中日文字异常的最佳方案是:「根据浏览器语言设置/用户选择界面语言,动态设置 HTML 的lang=xxx
」
字体的 fallback 机制
- 有些日文字体文件只包含日文文字,基本上其他文字(简体文字)都不包含。
- 有些日文字体文件除了包含日文文字,也包含基本上所有的简体文字(但中日相同 Unicode 的这部分文字肯定是被设计成了日文字形)
- 中文字体文件这边同理
当页面使用了日文字体(同时文件里基本没有简体字),但页面上又遇到了简体字的时候,怎么办?浏览器要怎么渲染这个 Unicode?
其实浏览器会按照顺序寻找包含这个简体字(Unicode)的字体,直到找到,然后渲染出来。
所以浏览器的 fallback 机制生效时,经常会遇到什么问题呢?字体混淆,不同字符有不同的字体,很不和谐,虽然中是中、日是日,但很别扭。
(图片只是混合了多种字体的中文,不是中日的,仅仅为了说明混了字体时的内容看着有多别扭。)
所以搞懂了 fallback 的原理后,其实也能明白:字体的 fallback 机制,跟我们这次的「中日文字异常,日本人看到了中华字体」不是一回事。
字体的 fallback 机制,只影响内容是否和谐,和文字正确显示与否无关。
常见问题
前端不指定任何字体,是否能解决这次中日文字异常的问题?
不一定解决问题。重点不是字体的设置,而是正确设置 lang 的问题。
使用 Web 字体 是否能解决问题?
同上,不能解决。
前端自己设置下日文字体,是否能解决问题?
不能解决。这样不管中文、日文用户在遇到上面的那种特殊文字时都会看到日文字体,对中文用户不友好。同理,只设置成中文字体,也不行。对日文用户不友好。
但有时候还是想给中文、日文设置一些漂亮的字体,那该怎么处理?
在正确设置lang
的前提下,考虑追加类似 CSS:
html[lang='ja'],
html[lang='jp'] {
font-family: 日文字体;
}
/* 包含 zh, zh-CN, zh-TW, zh-HK, zh-Hant, zh-Hans 等语言 */
html[lang^='zh'] {
font-family: 中文字体;
}
提醒下:
- 其实上面把简体中文、繁体中文、粤语中文统一当成见简中处理也不是很合适。毕竟这次「中日字体异常」的问题,其实中繁那边也有同样的问题。
- 其实这次的「中日字体异常」问题,本质是:两个语言的一些文字公用了一些 Unicode,但有不同的字形设计。所以除了汉字圈的这些语言会有这种问题,拉丁字母圈同样会有这种问题。
其他相关知识
PingFang SC: 虽然是中文字体,但也包含了日文文字(但对于中日相同 Unicode 码的文字,字形被设计成了中文的字形)
日文的文字组成包含以下三个部分:
- 汉字
- 平、片假名
- 罗马字
其中汉字的来源起源于中文的汉字,但对于其中的一些字,日本人决定改造一下,设计成了不同的形状,含义和发音可能一致也可能不一致。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。