中日文字化け問題の解析と3つの解決方法

### 問題の現象

同じ文字「将来」について
> Unicode 対応：`\u5c06\u6765`

中国語ユーザー：Web ページで日本語の漢字が表示される（「将」に注目）
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3749976/88a84974-5611-4665-b6b4-cc5c57de84d2.png)
日本語ユーザー：Web ページで中国語の漢字が表示される（「将」に注目）
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3749976/ab8fa02e-c431-4df2-8615-1877ad3bfea2.png)


**期待する動作：**

中国語ユーザー → 中国語の漢字を表示
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3749976/ab8fa02e-c431-4df2-8615-1877ad3bfea2.png)
日本語ユーザー → 日本語の漢字を表示
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3749976/88a84974-5611-4665-b6b4-cc5c57de84d2.png)

**なぜこんな文字化けが起きるのか？**

### 原因は Unicode にある
---

まず以下の 2 つの画像を見てほしい：
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3749976/6a1201c2-e875-49c5-8ec5-ac15d793f60e.png)
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3749976/103caf6f-fb2b-495b-ba27-9894e8407b9c.png)

これらの文字（日本語と中国語が混在）は似ているが、微妙に違う。ただし共通点がある：**同一の Unicode コードなのに、字形（見た目）が異なる**ということだ。

> この特殊ケースに該当する文字はそれほど多くない。「おはようございます」で同様の問題が報告されたことはない

**Unicode とは？**
簡単に言うと、世界中のほぼ全ての文字を含む標準規格。各文字には固有のコードが割り当てられている。

### ブラウザの文字レンダリングの仕組み

---

「中国語文字化け」問題を理解するには、まずブラウザの文字レンダリングの仕組みを知る必要がある。

ブラウザが Web ページの文字をレンダリングする際、最初に見るのは文字の Unicode コードだ。
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3749976/437994d0-fbfe-455f-beb3-076de7f1d866.png)


上記の文字で説明しよう。

ブラウザが U+6d45 という文字に遭遇した時、どんな形でレンダリングするか？

複数の選択肢がある。**最終的にはブラウザが使用するフォントで決まる**

> フォント（フォントファイル）って何？
> 簡単に言うと、Unicode コードと対応する字形のマッピングテーブルのこと

システムには多数のフォントが内蔵されており、Web サイトによっては Web フォントを使用する場合もある。

中国語フォントの例：

1. 苹方
2. 楷体
3. 宋体
4. 微软雅黑

日本語フォントの例：

1. MS 明朝・MS ゴシック
2. メイリオ・EPSON 教科書体Ｍ
3. Hiragino Kaku Gothic

「浅（U+6d45）」を日本語フォントで表示 →
![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3749976/085da80a-3ce5-4bd8-bb4c-cddad933fc5c.png)


「浅（U+6d45）」を中国語フォントで表示 →
![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3749976/7a8ce6fd-cf8c-4724-9165-991d1744f419.png)

つまり「中日文字化け」問題の解決策は：言語別にフォントを設定すればよい、ということになる。

確かに可能だが、実は不要かもしれない。

ブラウザに任せればよい。ブラウザがこの問題をうまく処理してくれる。

ブラウザはユーザーの言語を自動検出し、対応する言語のフォントを選択できるからだ。

> 前提条件：開発者が文字にフォントを設定していない（`font-family`が未設定）、または[汎用フォント](https://developer.mozilla.org/zh-CN/docs/Web/CSS/font-family)（serif、sans-serif）のみ設定している場合

ブラウザはどうやってユーザーの言語を判定するのか？

1. まず Web ページの`lang`属性をチェック、なければ ⬇️
2. システムの使用言語をチェック
   > 検証済み：ブラウザの優先言語設定は使用されない

**解決策その 1（次善策）**：

1. フォントを指定しない（[汎用フォント](https://developer.mozilla.org/zh-CN/docs/Web/CSS/font-family)の指定は OK）
2. HTML の`lang`属性を設定しない → システムの言語設定に従う

これで問題は解決する。効果：（上記の中日共通 Unicode 文字について）中国語システムユーザーは中国語字形、日本語システムユーザーは日本語字形を見ることになる。

ただし実際には`lang`属性の設定が必要なケースが多い。特に多言語対応済みで言語切り替え機能がある場合。ユーザーが言語を切り替える際、テキストの変更だけでなく、HTML の`lang`属性も設定して特殊文字が期待通りの字形でレンダリングされるようにする必要がある。設定しないと：UI テキストは日本語になったのに、ユーザーが日本語入力する際に中国語字形が表示される（ユーザーのシステムフォントが中国語のため）。

そのため、ページ読み込み時にブラウザの優先言語に基づいて HTML の`lang`属性を設定する必要がある。

```javascript
document.documentElement.lang = navigator.language || navigator.userLanguage;
```

または、ユーザーが言語を切り替えた際にも HTML の`lang`属性を設定する。

**中日文字化け問題の最適解：「ブラウザ言語設定/ユーザー選択 UI 言語に基づいて、動的に HTML の`lang=xxx`を設定」**

### フォントの fallback 機構

---

1. 一部の日本語フォントファイルは日本語文字のみ含み、簡体字はほぼ含まない
2. 一部の日本語フォントファイルは日本語文字に加え、ほぼ全ての簡体字も含む（ただし中日共通 Unicode の文字は日本語字形で設計）
3. 中国語フォントも同様

ページが日本語フォント（簡体字をほぼ含まない）を使用しているのに、簡体字が出現した場合は？ブラウザはどうやってこの Unicode をレンダリングするのか？

ブラウザは順番にその簡体字（Unicode）を含むフォントを探し、見つかるまで続行してレンダリングする。

ブラウザの fallback 機構が動作する際の問題は？フォントの混在。異なる文字が異なるフォントになり、統一感がない。中は中、日は日だが、非常に違和感がある。

（画像は複数フォントが混在した中国語のみ。中日混在ではないが、フォント混在時の違和感を示すため）
![8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3749976/36b49718-8c83-41cb-b63f-08597bcbcfd8.png)


fallback の仕組みを理解すると分かること：フォントの fallback 機構は、今回の「中日文字化け」問題とは別物。

**フォントの fallback 機構は見た目の統一感にのみ影響し、文字の正しい表示とは無関係。**

### よくある質問

---

**フロントエンドでフォントを指定しなければ中日文字化け問題は解決する？**

必ずしもそうではない。重要なのはフォント設定ではなく、lang の正しい設定。

**Web フォント使用で問題解決する？**

同上、解決しない。

**フロントエンドで日本語フォントを設定すれば解決する？**

解決しない。中国語・日本語ユーザーが特殊文字に遭遇した際、どちらも日本語フォントを見ることになり、中国語ユーザーに不親切。同様に中国語フォントのみ設定も日本語ユーザーに不親切。

中国語・日本語に美しいフォントを設定したい場合は？
`lang`を正しく設定した上で、以下のような CSS を追加：

```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: 中国語フォント;
}
```

> 補足：
>
> 1. 上記で簡体字・繁体字・広東語を一律簡体字扱いするのも完全ではない。「中日フォント化け」問題は繁体字でも同様に発生する
> 2. この問題の本質：2 つの言語の一部文字が同一 Unicode を共有するが字形デザインが異なること。漢字圏だけでなく、ラテン文字圏でも同様の問題が発生する

### その他の関連知識

---

**PingFang SC：** 中国語フォントだが日本語文字も含む（中日共通 Unicode の文字は中国語字形で設計）

**日本語の文字構成：**

1. 漢字
2. ひらがな・カタカナ
3. ローマ字

![9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3749976/5b31f9e4-e27d-4958-bbd4-943fa06a17a9.png)


漢字は中国語の漢字が起源だが、一部の字について日本人が改良し、異なる形状にデザインした。意味や読み方は一致する場合もあれば異なる場合もある。