アラビア語やヘブライ語のように右から左へ書く文字が、日本語や英語のような左から右の文字と混ざると、表示される順番が崩れて読みづらくなることがあります。HTML にはこうした「双方向テキスト(BiDi)」の見え方を制御するために、<bdo> と <bdi> という 2 つの要素が用意されています。この記事では、双方向テキストでなぜ表示順が崩れるのか、<bdo> で表記方向を明示的に上書きする使い方、<bdi> でユーザー名など方向が未知のテキストを隔離してレイアウト崩れを防ぐ使い方、そして dir 属性との関係や両者の違いまで、順に解説します。
目次
双方向テキストで表示順が崩れる仕組み
日本語や英語は左から右へ(LTR: left-to-right)読み書きしますが、アラビア語やヘブライ語は右から左へ(RTL: right-to-left)進みます。1 つのページや文の中にこれらが混在した状態を「双方向テキスト(bidirectional text、略して BiDi)」と呼びます。ブラウザはこうした文字列を表示するとき、ユニコードの双方向アルゴリズムという仕組みに従い、それぞれの文字が持つ本来の書字方向をもとに、画面上での並び順を自動的に決めています。
ふだんはこの自動処理でうまく表示されるのですが、問題になりやすいのが「方向が事前に分からないテキスト」を文中に埋め込むケースです。たとえばユーザーが自由に登録した名前を、数字や記号のすぐ隣に表示する場面を考えてみます。名前がアラビア語(RTL)だと、双方向アルゴリズムがその名前と隣り合う数字や記号までひとかたまりの右書きグループとみなし、意図しない順序で並べ替えてしまうことがあります。結果として、名前の後ろに置いたはずの点数や区切り記号が、名前の前に回り込んで見えるといった崩れが起こります。
このような並び替えを、書き手側から制御するための道具が <bdo> と <bdi> です。前者は「方向を強制的に上書きする」ため、後者は「周囲から切り離して影響を防ぐ」ために使います。まずは <bdo> から見ていきましょう。
bdo で表記方向を上書きする
<bdo> は「bidirectional text override(双方向テキストの上書き)」の略で、中に入れたテキストの表記方向を、双方向アルゴリズムの判定を無視して強制的に指定する要素です。方向は必須の dir 属性で指定し、dir="rtl" なら右から左へ、dir="ltr" なら左から右へと、文字の並びそのものを上書きします。dir を省略すると上書きの効果が得られないため、<bdo> では dir の指定が欠かせません。
<!-- 通常の表示(左から右) --> <p>HTML5</p> <!-- bdo で右から左に上書きすると文字の並びが反転する --> <p><bdo dir="rtl">HTML5</bdo></p>
上の例では、1 つめの HTML5 はそのまま「HTML5」と表示されますが、<bdo dir="rtl"> で囲んだ 2 つめは「5LMTH」のように、文字の並びが右から左へ反転して表示されます。<bdo> はこのように、本来の書字方向を持つ文字に対しても指定した方向を強制するため、効果が非常に強いのが特徴です。
裏を返すと、<bdo> はブラウザ本来の並び替えを打ち消してしまうため、多用すると意味的におかしな表示になりかねません。実際に使うのは、あるコードや識別子を意図的に逆順で見せたいなど、表示方向を書き手が明確に決め打ちしたい特殊な場面に限られます。ふだんの多言語表示では、次に説明する <bdi> の出番のほうがずっと多くなります。
bdi で方向が未知のテキストを隔離する
<bdi> は「bidirectional isolation(双方向の隔離)」の略で、中のテキストを双方向アルゴリズムの計算から切り離し、周囲のテキストへ書字方向の影響が及ばないようにする要素です。<bdo> のように方向を強制するのではなく、「この部分は独立したかたまりとして扱ってください」とブラウザに伝えるのが役割です。中身の方向自体は後述の dir="auto" と同じように自動判定されます。
もっとも活躍するのが、ユーザー名やニックネームなど、どの言語で入力されるか分からないテキストを一覧に並べる場面です。ランキング表示を例に考えてみましょう。名前のあとに点数を続けて表示するとき、名前を <bdi> で囲んでおけば、その名前が右書き文字であっても、後ろの点数や記号が引きずられて並び替わることを防げます。
<!-- ユーザー名を bdi で隔離してレイアウト崩れを防ぐ --> <ul> <li>1位 <bdi>Alice</bdi>: 3200 点</li> <li>2位 <bdi>إيان</bdi>: 3100 点</li> <li>3位 <bdi>קורא</bdi>: 3000 点</li> </ul>
2 位や 3 位の名前は右書きの文字ですが、<bdi> で囲んでいるため、名前の内部だけで方向が完結し、直後の「: 3100 点」はきちんと名前の右側に並びます。もし <bdi> を外して素のまま置くと、右書きの名前とコロンや数字がひとつのまとまりと解釈され、コロンや点数が名前の左側に回り込むなど、行ごとに並びがちぐはぐになることがあります。方向が分からない外部由来のテキストは、とりあえず <bdi> で包んでおくと安全だと覚えておくとよいでしょう。
dir 属性(ltr / rtl / auto)との関係
<bdo> と <bdi> を理解するうえで欠かせないのが dir 属性です。dir はほとんどの HTML 要素に指定できる属性で、そのテキストの基準となる書字方向を決めます。取りうる値は次の 3 つです。
| 値 | 意味 |
|---|---|
ltr | 左から右へ。日本語・英語などの基準方向。 |
rtl | 右から左へ。アラビア語・ヘブライ語などの基準方向。 |
auto | 内容の最初の強い方向を持つ文字から、ブラウザが方向を自動判定する。 |
この dir と 2 つの要素の関わり方が、それぞれの性格をよく表しています。<bdo> は dir の指定を必須とし、その値のとおりに表記方向を強制的に上書きします。一方 <bdi> は、dir を書かなくても既定で dir="auto" が適用され、中身の内容から方向を自動判定します。つまり <bdi> は、方向の分からないテキストを受け取って自動で正しい向きに扱ってくれる、実用向きの初期設定を最初から備えているわけです。
なお、dir="auto" は <bdi> 以外の要素にも指定できますが、通常の要素に付けても隔離の効果はありません。周囲への影響まで断ち切りたいときは、方向の自動判定と隔離をあわせ持つ <bdi> を使うのが目的に合っています。
bdo は「上書き」、bdi は「隔離」
名前が似ていて混同しやすい 2 つですが、役割は正反対といってよいほど異なります。<bdo> は書字方向を書き手の指定どおりに上書き(override)する要素で、ブラウザの自動判定を打ち消して並びを固定します。対して <bdi> は、中身を周囲から隔離(isolation)する要素で、方向そのものは自動判定に任せつつ、その影響が外へ漏れないようにします。「方向を決め打ちしたいなら bdo、周りとの干渉を防ぎたいなら bdi」と押さえておくと選び間違えません。
| 要素 | 役割 | dir の既定 | 主な用途 |
|---|---|---|---|
<bdo> | 表記方向を強制的に上書きする | なし(明示が必須) | 表示方向を意図的に固定したい特殊な場面 |
<bdi> | 中身を双方向的に隔離する | auto(自動判定) | 方向が未知のユーザー名などを安全に並べる |
実際の Web サイトで登場頻度が高いのは圧倒的に <bdi> のほうです。ユーザー投稿や多言語のデータを扱うサービスでは、名前やコメントの断片を <bdi> で囲むだけでレイアウトの崩れを未然に防げます。<bdo> は効果が強い分だけ使いどころが限られるので、「方向を無理やり反転させたい」という明確な理由があるときにだけ選ぶ、と考えておくとよいでしょう。
まとめ
左から右の文字と右から左の文字が混ざる双方向テキストでは、ブラウザの自動的な並び替えによって表示順が崩れることがあります。<bdo> は dir 属性で指定した方向にテキストの並びを強制的に上書きする要素で、表示方向を決め打ちしたい特殊な場面で使います。<bdi> は中身を周囲から隔離し、方向が未知のテキストを安全に扱う要素で、既定で dir="auto" が効くためユーザー名やコメントなどをそのまま囲むだけで崩れを防げます。dir の値(ltr / rtl / auto)との関係も踏まえ、「上書きの bdo」「隔離の bdi」と役割で覚えておけば、混同することなく使い分けられます。