Webブラウザの日本語改行問題 -改行を実現するHTML/CSS-（1）

日本語のWebサイト（ランディングページ等）をつくっていると、読みにくい所で改行されるパターンがありますよね？実際この記事もブラウザの横幅によって単語の途中なのに改行されています。普通に文章を読んでいる時には良いのですが、これがランディングページのヘッドラインで起きるとものすごいカッコ悪いです。

たとえばPhotoshopのページで「写真のレタッチ、合成、カラー変更」というヘッドラインが以下のようになってたらいかがでしょうか？この場合はできれば、「合成、」の後で改行されている方が自然で読みやすくなっています。（ちなみにこれは、Chrome Developer Toolsで編集して、わざと見にくくしたので、実際にはキレイに改行処理しています）。

![スクリーンショット 2022-02-17 1.49.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42035/531e0746-5e66-a855-60f3-05f451409045.png)

よーしそれじゃあ`<br>`入れれば良いんだなと真っ先に思いついた方がいるでしょう。しかし今日はさまざまなデバイスが皆さんのWebサイトに訪れています。デスクトップではキレイに見えた`<br>`の改行も、モバイルのようなブラウザの横幅が短いものでは`<br>`の改行は余計な結果を生む事があります。

そもそも何故このような問題が起きるのか？ブラウザの仕様として欧文がスペースを改行ポイント（Word break）とするのに対して、日本語は文字単位（行頭行末禁則文字を省く）で改行を許す（Charactor break）ので、このような事が起きます。

この記事ではWebブラウザでの日本語の改行問題の解決策についてCSSのプロパティやHTMLタグの話を交えて話していきます。（※この記事はモダンブラウザを使う前提で話を進めています。Internet Explorer 11等の話については言及しませんのであらかじめご了承ください）

また、予めこの記事で利用するソースコードは、codepenに置いておきます。必要に応じて参照してください。

**サンプルソースコード（デモ）**

https://codepen.io/tamanyan/pen/KKyyxMj


## 解決策1. white-space: nowrap + \<wbr\>

はじめに言います。この手法はお勧めしません。先に使える解決策に行きたい方はこのセクションを飛ばしてください。時間がある方はどうかお付き合いください。

`white-space: nowrap;`[^1]は連続する半角スペース、タブ、改行を一つの半角スペースを詰めて表示し、行の折り返しは行いません。これによって自動での行の折り返しを防ぎ`<wbr>`[^2]によってブラウザ内で改行してよい位置を教えて上げる事で、改行してほしい場所で改行ができます。

[^1]: https://developer.mozilla.org/ja/docs/Web/CSS/white-space
[^2]: https://developer.mozilla.org/ja/docs/Web/HTML/Element/wbr

<img width="400" alt="解決策1. white-space: nowrap + \<wbr\>" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42035/f24513de-9372-ae6f-74c4-fd0532a4d921.png">

```html
<div>
  <h3>1. white-space: nowrap; + &lt;wbr&gt;</h3>
  <p class="nowrap box">
    グレートブリテン<wbr>および<wbr>北アイルランド連合王国という<wbr>言葉は<wbr>本当に<wbr>長い言葉<wbr>ですね
  </p>
</div>
```

```css
.box {
  padding: 10px;
  border: 1px solid;
  font-size: 40px;
}

.nowrap {
  white-space: nowrap;
}
```

### white-space: nowrap + \<wbr\>の問題点

おお、動いているじゃないかと、何が駄目なんでしょうか？理由は、

**Firefoxでは一切改行されない**

残念ながらFirefoxでは`white-space: nowrap;`が指定された中で`<wbr>`を使っても無視されてしまいます。

<img width="400" alt="Firefoxでは一切改行されない　" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42035/beb3a3f0-3799-0134-45bc-8d227050e410.png">


こちらで過去に議論されていたみたいですが、今の所解決する手立てがありません。

https://bugzilla.mozilla.org/show_bug.cgi?id=656960


**チャンクが親の要素の横幅より大きい場合、文字が溢れる**

チャンク（改行できる単位）の横幅が親の要素をからはみ出ている場合は、`<wbr>`を入れないと文字が溢れてしまいます。この場合は`北アイルランド連合王国という`がチャンクとなっています。しかし、`white-space: nowrap;`により改行できなくなっているため、文字が溢れた状態で表示される事になります。

<img width="400" alt="チャンクが万が一親の要素からはみ出でていた場合、文字があふれるい　" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42035/92843d37-2329-f4f2-348c-aff04fe0bbda.png">


## 解決策2. word-break: keep-all; + overflow-wrap: break-word; + \<wbr\>

良い解決策です。**最もキレイに日本語改行処理を行いたい場合はこれを使用しましょう。**

`word-break: keep-all`[^3]はCJK（中国語、日本語、韓国語）の改行を禁止します。なお、CJK以外のテキストについては規定の改行規則に従います。これで日本語の改行ができなくなるので、`<wbr>`によってブラウザ内で改行してよい位置を教えて上げる事で、改行してほしい箇所で改行ができます。

[^3]: https://developer.mozilla.org/ja/docs/Web/CSS/word-break

<img width="400" alt="解決策2. word-break: keep-all; + overflow-wrap: break-word; + \<wbr\>　" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42035/1fc1bd08-22c6-7aa0-788e-da3ceac3409b.png">


これで解決じゃないか`overflow-wrap: break-word;`[^4]は何のために指定するんだ？と疑問に思った人もいるでしょう。`overflow-wrap: break-word;`は表示範囲を超える長いテキストがある場合、**テキストは溢れる事より、改行することを優先します**。

[^4]: https://developer.mozilla.org/ja/docs/Web/CSS/overflow-wrap

**word-break: keep-all; + \<wbr\>の場合**

`overflow-wrap: break-word;`を指定していない状況では、チャンクの横幅が親の要素をからはみ出ている場合は、`<wbr>`を入れないと文字が溢れてしまいます。この場合は`北アイルランド連合王国という`がひと塊になっています。

<img width="400" alt="word-break: keep-all; + \<wbr\>" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42035/b51d7819-e7c7-ab1c-5828-7c045b9f78a1.png">

**word-break: keep-all; + overflow-wrap: break-word; + \<wbr\>の場合**

対して、`overflow-wrap: break-word;`を指定している状況では。チャンクの横幅が親の要素をからはみ出ている場合でも次の行に改行されます。すばらしいですね。


<img width="400" alt="word-break: keep-all; + overflow-wrap: break-word; + \<wbr\>" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42035/2aacb77c-ed69-1d01-38a9-47d56695a5c3.png">

```html
<div>
  <h3>2. word-break: keep-all; + overflow-wrap: break-word; + &lt;wbr&gt;</h3>
  <p class="keep-all-break-word box">
    グレートブリテン<wbr>および<wbr>北アイルランド連合王国という<wbr>言葉は<wbr>本当に<wbr>長い言葉<wbr>ですね
  </p>
</div>
```

```css
.box {
  padding: 10px;
  border: 1px solid;
  font-size: 40px;
}

.keep-all-break-word {
  word-break: keep-all;
  overflow-wrap: break-word;
  /* overflow-wrap: anywhere; が2024年1月8日現在で一番良い */
}
```

### word-break: keep-all; + overflow-wrap: break-word; + \<wbr\>の問題点

**display: flex;が親の要素に指定されているとoverflow-wrap: break-word;が聞かず、レイアウトが崩れる**

overflow-wrap: break-word; の説明を注意深く見てみると、以下のようにコンテンツの最小固有寸法（min-content intrinsic size）[^5]を計算する際には考慮されないと書かれています。

[^5]: https://ishadeed.com/article/intrinsic-sizing-in-css/

https://developer.mozilla.org/ja/docs/Web/CSS/overflow-wrap

> 行内にその他の分割可能な位置がない場合、通常は分割可能でない単語が任意の場所で分割されますが、コンテンツの最小固有寸法を計算する時に、単語分割によって導入された折り返し可能位置が考慮されません。

ところで`flexbox`の`item`は何も`width`を指定されていない場合は、最小固有寸法によって決定される、つまり`北アイルランド連合王国という`のチャンクの横幅によって`width`が決定されるため、ブラウザの横幅に収まらない形で以下のように表示されてしまいます。


<img width="300" alt="word-break: keep-all; + overflow-wrap: break-word; + \<wbr\>" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42035/1630c51b-4726-3f58-2e12-d7957965df78.png">

```html
<div class="flex">
  <div>
    <h3>5. word-break: keep-all; + overflow-wrap: break-word; + &lt;wbr&gt;</h3>
    <p class="keep-all-break-word box">
      グレートブリテン<wbr>および<wbr>北アイルランド連合王国という<wbr>言葉は<wbr>本当に<wbr>長い言葉<wbr>ですね
    </p>
  </div>
</div>
```

```css
.box {
  padding: 10px;
  border: 1px solid;
  font-size: 40px;
}

.flex {
  display: flex;
}

.keep-all-break-word {
  word-break: keep-all;
  overflow-wrap: break-word;
  /* overflow-wrap: anywhere; が2024年1月8日現在で一番良い */
}
```

この問題を回避するには、`flex-item`に横幅を指定する方法 or `overflow-wrap: anywhere`を指定する方法がありますが、後者は残念ながら主にSafariでサポート外なので、使用はできません。かなC。また、各チャンクをできるだけ細かく分割し、親の要素の横幅を超えないように調整する事で未然に防ぐ方法もあります。参考までに、`display: grid`でも同様の問題が起きるので使用する際には注意してください。

**※追記: `overflow-wrap: anywhere` がSafari (7(iOS)/15.4(Desktop))でサポートされました。よって`overflow-wrap: anywhere`を使いましょう。**

## 解決策3. \<span\> + display: inline-block;

こちらもよく使われている手法で、`display: inline-block`のスタイルが適用した`<span>`を使って改行したくない文字列をくくることで、改行を制御します。非常にシンプルな手法ですが、\<span\>で囲まれる事によって、HTMLコードが若干複雑になる点は気になります。

<img width="400" alt="\<span\> + display: inline-block;" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42035/b6b26b2d-be7b-8e47-bff2-19508dff29db.png">

```html
<div>
   <h3>3. display: inline-block;</h3>
  <p class="box">
    <span class="nobr">グレートブリテン</span><span class="nobr">および</span><span class="nobr">北アイルランド連合王国という</span><span class="nobr">言葉は</span><span class="nobr">本当に</span><span class="nobr">長い言葉</span><span class="nobr">ですね</span>
  </p>
</div>
```

```css
.box {
  padding: 10px;
  border: 1px solid;
  font-size: 40px;
}

.nobr {
  display: inline-block;
}
```

### \<span\> + display: inline-block;の問題点

**チャンクが親の要素の横幅より大きい場合、次の文字が回り込まない**

チャンクの横幅が親の要素をからはみ出ている場合は、文字があふれることはありませんが、[解決策2](#解決策2-word-break-keep-all--overflow-wrap-break-word--wbr)の時のように、次の文字が回り込む事なく、不自然な改行になってしまいます。下の例では、`北アイルランド連合王国という`が`<span>`で囲まれている事で、次の`言葉は`が回り込む事ができません。レイアウトが崩れるわけではありませんが、変な部分で改行されて読みづらくなってしまうという問題があります。これを回避するには各チャンクをできるだけ細かく分割し、親の要素の横幅を超えないように調整しなければなりません。

<img width="400" alt="\<span\> + display: inline-block;" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42035/6d61bafd-bf72-ee2a-17f3-c6eab5a083cf.png">

## 結局何を使えば良いの？

- レイアウトが崩れる時に適切な対処を毎回行える場合は、[解決策2](#解決策2-word-break-keep-all--overflow-wrap-break-word--wbr)を使ってください。これが**最もきれいに改行できます**
- レイアウトが崩れる事を極力避けたい場合、[解決策3](#解決策3-span--display-inline-block)を使ってください

**(※追記　202４年1月8日現在では解決策2（+ `overflow-wrap: anywhere`）を使う事をオススメします)**

## 自動で日本語改行処理を行うには？

ここまで読んだ方は毎回`<wbr>`や`<span>`を手動で入れていくのかーと感じているでしょう。文章がHTMLにハードコードされているならともかく、CMSで文章が管理されている場合、リッチテキストエディタとの相性も考えると、これらのタグを手動で入れていくのはかなり骨の折れる作業になります。

[BudouX](https://github.com/google/budoux)はまさにこの問題を解決するツールになります。詳細な内容は次回の記事に回します。実際の使い方だけでなく、アドビで利用する際に直面した問題や、その解決策等についても説明していきます。

https://github.com/google/budoux

（追記、次回の記事投稿しました）

https://qiita.com/tamanyan/items/aab0d41caef4a6f21ad1