異常現象
對於相同的文字「將來」
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 碼的文字,字形被設計成了中文的字形)
日文的文字組成包含以下三個部分:
- 漢字
- 平、片假名
- 羅馬字
其中漢字的來源起源於中文的漢字,但對於其中的一些字,日本人決定改造一下,設計成了不同的形狀,含義和發音可能一致也可能不一致。