7 : 11 LRM文字, RLM文字, <bdi>

← 前 7‒10 p | ↑ もくじ i | 次 7‒12 n →

[Unicode] 双方向アルゴリズム境界における句読点

2003年 3月12日
記事ID d30312

RLM文字(HTMLでは &rlm;)は、書式方向「右から左」のゼロ幅文字だ(幅がゼロなので、書いてあっても目には見えない)。 これは書式方向制御文字(ここから「右から左」にしろ、というたぐい)ではない。 どういうときに使うのだろうか。

ヘブライ語の例

例えば、「こんにちは!」という意味のヘブライ語「シャローム!」の最後につける感嘆符(びっくりマーク)について考える。 SHAを表す ש U+05E9, Lを表す ל U+05DC, Oを表す ו U+05D5, Mを表す ם U+05DD をこの論理的順序で並べて、最後に感嘆符 U+0021 をつけるとする。

バイナリー (UTF-16 BE)
05 E9 05 DC 05 D5 05 DD 00 21

大域的な書式方向が左から右になっているなかにこのコードを書くと、期待通りの効果が得られないだろう。

ヘブライ文字の部分はちゃんと右から左につながるが、感嘆符がヘブライ語の行頭(いちばん右)に来てしまう。 パーサが間違っているというより、上のコードがそれだけでは、あいまいだからだ。

どこがあいまいか?

この現象は、双方向アルゴリズムが作用する境界があいまいなために発生する。 パーサは、左から右へ進む文字なかで、U+05E9 に出会う。 これはヘブライ語の文字で右から左に結合すべきことが定められているので、ここから「右から左」ブロックになる。 U+05DC, U+05D5, U+05DD まで4文字を読み込んだあと(ここまではヘブライ文字)、感嘆符を発見する。 感嘆符のあとでは、ふたたび左から右へ進む文字が並んでいる。 ―― 「右から左」ブロックの最初の文字が U+05E9 であること、 U+05DC, U+05D5, U+05DD までの4文字は「右から左」ブロックに属すること、 感嘆符より後ろが「左から右」ブロックに属することは、確定的だが、 感嘆符そのものがどちらのブロックに属するかが、ハッキリしない。 特殊扱いすべきブロックに属する証拠がないので、パーサは、感嘆符をデフォルト扱いするだろう。 つまり「右から左」ブロックに含めないようにするだろう。

感嘆符がどっちのブロックに属するのか、形式的に判断する方法はない。 例えば、「ヘブライ語のあいさつはシャローム! 覚えておけ」と言うとき、 「ヘブライ語のあいさつはシャローム」ということを力説しているのか、 それとも「シャローム!」の感嘆符まで含めて引用しているのかハッキリしない。

感嘆符でなく、ピリオドや疑問符のようなほかの句読点でも、同じことが起きる。 HTMLであれば、dir属性を使って方向を強制することもできるが、プレインテキストを含むUnicode一般では、 dir属性のような上位の制御が利用できるとは限らないから、HTMLのdir属性はユニコードのレイヤにおける本質的な解決法ではない。

言い換えれば、ヘブライ語の部分が感嘆符でまだ終わりでないことがハッキリしているなら、 パーサは迷わず感嘆符を「右から左」ブロックに含める。 実際、「シャローム! シャローム!」と2回、繰り返して書けば、1つめの感嘆符はヘブライ語の一部と認識され、 正しい位置にくる。観察しやすくするため、2つめの感嘆符を疑問符に変えて調べてみよう。 「シャローム! シャローム?」

感嘆符は期待通りの位置 ―― ひとつめ(右側)のシャロームのうしろ(左) ―― に来るが、 疑問符は期待通りの位置 ―― ふたつめ(左側)のシャロームのうしろ(左) ―― に来ないで、 見かけ上、ヘブライ語部分の行頭(いちばん右)に現れるだろう。 これは「シャローム! シャローム」でヘブライ語ブロックが終了して、「?」はヘブライ語ブロックに続く次のブロックに属すると認識されたためだ。

幅なしRLM文字

以上から分かるように、「シャローム」につづく句読点(感嘆符、疑問符、ピリオドなど)もヘブライ語の部分に属することを明示するには、 句読点がブロックの境目にならないよう、句読点のあとでも、まだまだ右から左へ進む文字が続いているようにすれば良い。 実際にヘブライ語文がまだまだ続いていれば、何もしなくても良い。 実際には「シャローム!」で終わりにしたい場合、ユニコード的解決法としては、 感嘆符のうしろに「見えないヘブライ文字」があるようにすれば良い。 これが幅なしRLM文字 U+200F であり、 ヘブライ語に限らず、アラビア語での同様な問題を解決するのに使うことができる。 幅なし(zero-width)なので、このコードポイントは論理的に(バイナリーで)のみ存在し、表示上は見えない。

バイナリー (UTF-16 BE)
05 E9 05 DC 05 D5 05 DD 00 21 20 0F

逆に、感嘆符がヘブライ語に属さないことを明示的に示す一つの方法として、 感嘆符の前に幅なしLRM文字 U+200E を置くことも考えられる。

RLM文字は、HTMLでは、&rlm; として参照することもできる。

<!ENTITY lrm     CDATA "&#8206;" -- left-to-right mark, U+200E NEW RFC 2070 -->
<!ENTITY rlm     CDATA "&#8207;" -- right-to-left mark, U+200F NEW RFC 2070 -->

次の2つの例を比較せよ。

&#x05e9;&#x05dc;&#x05d5;&#x05dd;!
שלום!
&#x05e9;&#x05dc;&#x05d5;&#x05dd;!&rlm;
שלום!‏

ユニコードで、日本語や英語の文字は「左から右に書く」という文字単位の強い属性を持っている。 また、ヘブライ語やアラビア語の文字は「右から左に書く」という文字単位の強い属性を持っている。 句読点、例えばピリオドは、「左から右に書く」言語と「右から左に書く」言語で共用できるように、 それ自身では強い書式方向属性を持たず、 環境に依存する。より具体的には、同一の強い書式方向属性をもつ文字に囲まれると、その属性を継承して、弱い書式方向属性をおびる。 弱い書式方向属性をおびた句読点類は、いっそうニュートラルな空白類のレイアウトに影響を与えることが可能だが、 強い書式方向属性がある文字に逆に影響を与えることはできない。 したがって、ある句読点の手前に「右左」文字、その句読点の直後に「左右」文字がある場合、 その句読点の書式方向属性は直前、直後のブロックからは等しく影響を受けるので、 より大きな全体の属性に依存するが、 もし句読点の手前に「右左」文字があり、その句読点の直後にさらに「右左」文字(幅なしでも良い)があるなら、 その句読点は、より上位レベルを調べるまでもなく、ローカルな前後の環境から「右左」属性を継承する。

Windows 2000 のメモ帳では、右クリックメニューから選ぶだけで、指定の場所にRLMほかを挿入できる(Microsoft Windows 2000 Professional ドキュメント)。 べんりな機能ではあるが、しょせんメモ帳なので、あまり使いやすくはない。

実際のヘブライ語圏のウェブページ

2003年3月現在、実際のヘブライ語圏のウェブページでは、dir属性を明示したりRLMを使うかわりに、 次のように「末尾に来る句読点を、あらかじめひっくり返しに書く(論理的な行頭に書く)」という妥協案で、 プレフォーマットされていることが多い。 (ヘブライ文字そのものをひっくり返しに書くわけでなく、パラグラフの最後の句読点のみ細工する。)

!&#x05e9;&#x05dc;&#x05d5;&#x05dd;
!שלום

この方法は、 パラグラフ末尾の「行末の句読点」をソースでは「行頭」にタイプしなければならず不自然だが、 比較的に古いブラウザでも期待通りの表示が得られるだろう。

リンク

関連記事

この記事のURL


HTML5 の bdi 要素と Unicode 6.3 の新しい双方向アルゴリズム

2012年12月 4日
記事ID e21204

ブログのコメント欄で起きる身近な例を出発点に、「右から左に書く文字 + 数字」のパターンに潜む問題と、解決法を探る。

HTML の dir 属性は「テキスト方向」を表すが、挙動が複雑で、役立たない場合もある。HTML5 で予定されている新しいタグ <bdi> を使うと、問題は簡単に解決する。将来的には、「ユーザー入力欄などの動的に生成される語句は、このタグで隔離」というのが常識になるかもしれない。

目次

入門編

[この記事の目次へ]
なぜこんな表示に

例1: ブログのコメント欄で、ユーザー名 User のコメントが次のように表示されるとする:

Comment by User: 2012年11月23日 20時30分

ユーザー名が אלישע(エリーシャ)なら、こうなってほしい:

Comment by אלישע‎: 2012年11月23日 20時30分

実際には、次のように表示が乱れる:

Comment by אלישע: 2012年11月23日 20時30分

*

例2: エジプトの歴史を紹介するウェブページを作成中の人が、「人名はオリジナルのつづりも表記したい」と思って、こう書こうとした:

ナセル(عبد الناصر‎, 1918–1970)は…

ところが、ソースで…

ナセル(<span lang="ar">عبد الناصر</span>, 1918–1970)は…

…と書くと、ブラウザ上では次のように表示が乱れてしまう:

ナセル(عبد الناصر, 1918–1970)は…

数字の位置がおかしい上、生年と没年もあべこべだ。

そうなってしまう訳

「左から右に書く文字」・「右から左に書く文字」が混在するテキストの処理を双方向アルゴリズムと呼ぶが、次のように動作する:

  1. アラビア文字・ヘブライ文字などの←「右から左」の文字に半角や全角の数字が後続する場合、その数字は←「右から左」の範囲の一部となり、従って文字の←左に表示される。
  2. 数字そのものは、その位置で「左から右」→に並ぶ。
  3. 文字と後続の数字の間に、スペースや句読点が含まれていても結果は変わらない。

例えば、「エリーシャ - 123」は、ヘブライ語の文章の中では右から左へ、こう表示される:

אלישע - 123

一方、「左から右」のインターフェースにおいて「ユーザー名 - 現在のスコア」のようなフォーマットが並んでいる場所なら、同じ文字列を次の順番に表示する必要があるかもしれない:

אלישע - 123

このように、「右から左」の文字と後続する数字の位置関係には、2種類の可能性がある(「右から左」の表示それ自体は、現在のソフトウェアなら自動で正しく行われる)。

双方向アルゴリズムでは、デフォルトで前者のパターンになる。

「数字を含む現代ヘブライ語の文章」(前者のパターン)はいくらでも存在するが、「ヘブライ文字と数字を含むヘブライ語でない文章」は比較的まれなので、このデフォルト設定は妥当だろう。

例1・例2の「乱れた」表示も、実は乱れているわけでなく、仕様通りの正しい結果だ。

Comment by אלישע: 2012年11月23日 20時30分

上の例は、ヘブライ文字に続く数字「2012」が仕様通りに配置されたもの。

ナセル(عبد الناصر, 1918–1970)は…

この例で、丸かっこ内の文字は、次の順序で右から左に並んでいる: アラビア文字の部分、カンマ、スペース、「1918」、ダッシュ、「1970」。

これらは正しいデフォルト表示だが、意図する表示とは違う。タグや制御文字を使って表示方法を指定する必要がある。

3種類の解決法

HTML では、テキストの方向を指定する dir 属性を使って、この種の問題を解決できることがある。ただし、使い方にはこつがあり、やたらと付ければいいものではない。例えば、この書き方はどうだろう:

例2a:

<p lang="ja" dir="ltr">
ナセル (<span lang="ar" dir="rtl">عبد الناصر</span>, 1918–1970) は…
</p>

パラグラフのテキスト方向が ltr(左から右)、埋め込まれるアラビア文字部分が rtl(右から左)と明示され、言語属性 lang もきちんと示されている。一見、筋の通った模範解答のようにも思える。だが、これだと結果は最初と変わらない:

ナセル (عبد الناصر, 1918–1970) は…

例2において、アラビア文字のテキスト方向はすでに正しい。デフォルト表示で都合悪いのはアラビア文字の並び方ではなく、その後ろの数字の位置だ。この場合、数字部分に dir 属性を指定すると問題が解決する。

例2b:

<p>
ナセル (<span lang="ar">عبد الناصر</span>, <span dir="ltr">1918–1970</span>) は…
</p>

ナセル (عبد الناصر, 1918–1970) は…

別解として、「右から左」区間を終わらせたい場所に、Unicode の特殊文字:
U+200E LEFT-TO-RIGHT MARK
を挿入する方法もある。この文字は LRM と略称され、HTML では、​&lrm; という簡単な表記で使える。「右から左」の文字と数字の間にこれを挿入すれば、「右から左」区間が数字の手前で終了するようにできる。

<p>
ナセル (<span lang="ar">عبد الناصر</span>&lrm;, 1918–1970) は…
</p>

この場合、dir 属性を使う方法より簡潔で、意図も明確だ。

HTML5 で予定されている <bdi> タグを使えば、次のシンプルな表記で問題が解決する:

<p>
ナセル (<bdi lang="ar">عبد الناصر</bdi>, 1918–1970) は…
</p>

bdi は Bidirectional Isolate(双方向性における隔離区域)という意味で、「この部分の方向性は独立しています。後ろの数字とつながったりはしません。切り離して考えてください」と指示する役割を持つ。

*

同様に、次の表示を得るには…

Comment by אלישע‎: 2012年11月23日 20時30分

こう書けば良い:

Comment by אלישע: <span dir="ltr">2012年11月23日 20時30分</span>
<!-- または -->
Comment by אלישע&lrm;: 2012年11月23日 20時30分
<!-- HTML5では -->
Comment by <bdi>אלישע</bdi>: 2012年11月23日 20時30分

数字(例えば値段)が乱れて表示されると、情報が正しく伝わらず、場合によってはセキュリティー問題にもつながる。「ユーザーが自由に入力できる欄によって、その欄と関係ない隣の欄の表示が影響される」という状況は、良くない。将来的には、「動的に生成される語句(ユーザー入力欄など)には <bdi> を付ける」というのが常識になるかもしれない。そうしておけば、世界中のどういう文字がそこに来ても周囲の表示は乱れないからだ。

普通の(静的な)ウェブページの作成でも、日本語や西欧語の文章にアラビア文字・ヘブライ文字などの単語を混在させるとき、​<bdi> タグは役立つだろう。

まとめ

「右から左」の文字の直後にある数字は、デフォルトで左側に表示される。文字と数字の間にカンマ、ハイフンなどがある場合も同様。数字を右側に表示させる方法は、3種類ある:

  1. 「右から左」区間を終了させたい場所に特殊文字 LRM を挿入。
  2. HTML では、数字の部分に dir="ltr" を指定する方法もある。
  3. HTML5 には <bdi> という便利なタグがある。

*

(入門編・終わり)

事例研究

[この記事の目次へ]
かっこの表示が乱れるケース

例3: 次のように表示したい:

ガルディヤ(ގަރުދިޔަ‎)[12]は、モルディブの伝統的なツナ・スープ。一般に、お米と一緒に食べる。付け合わせはライム、チリなど。

かっこの中は、モルディブのディベヒ語(Dhivehi, 言語コード: dv)の単語で、ターナ文字(Thaana)で書かれている。「右から左」の文字だ。​[12] は注釈や文献番号だとする。素直に書けば、

ガルディヤ(<span lang="dv">ގަރުދިޔަ</span>)<sup>[12]</sup>は…

…となるが、それだと表示が乱れる。

Unicode 6.2 時点での表示:

ガルディヤ(12](ގަރުދިޔަ]は、モルディブの伝統的な…

あなたのブラウザでは…

ガルディヤ(ގަރުދިޔަ)[12]は、モルディブの伝統的な…

ターナ文字は単に「右から左」の文字の例で、それ自体が特殊なのではない。かっこ内がアラビア文字・ヘブライ文字でも同じ現象が起きる。

この表示を理解するために、
ガルディヤ()は、[12]
という構造に注目しよう。
ガルディヤ(
…までは順調に表示され、そこで「右から左」エリアに入る。入門編で見たように、「右から左」の文字の後ろに数字がある場合、その数字も「右から左」エリアの一部なり、文字の左側に来る。間にかっこがある場合も同様だ。
)[12
…の部分がこれに当たる。「右から左」エリアでは、かっこは鏡像表示(ミラーリング)されるため、見掛け上、ターナ文字の末尾(左側)に
12](
…があるように出力される。これは、かっこを鏡像にしながら、
)[12
…を逆順にしたものだ。12という数字自体は「左から右」に並ぶ。
]は、
…の部分では「左から右」に戻り、表示上、そこで「右から左」エリアの右側に飛んでいる。

Unicode 6.3 では双方向アルゴリズムが改訂され、かっこの扱いも若干改善される。しかし、現時点の仕様予定(rev. 28d4)を見た限りでは、上の例のかっこの表示は変更されない。 2013-08-23追記: かっこの処理は、上記の予定よりさらに改善される模様。

例1・例2と同様、数字に dir 属性を設定するか、またはターナ文字の末尾に &lrm; を挿入することで問題を解決できる:

ガルディヤ(<span lang="dv">ގަރުދިޔަ</span>)<sup>[<span dir="ltr">12</span>]</sup>は…
<!-- これでも良い -->
ガルディヤ(<span lang="dv">ގަރުދިޔަ</span>)<sup dir="ltr">[12]</sup>は…
<!-- または -->
ガルディヤ(<span lang="dv">ގަރުދިޔަ</span>&lrm;)<sup>[12]</sup>は…
<!-- HTML5では -->
ガルディヤ(<bdi lang="dv">ގަރުދިޔަ</bdi>)<sup>[12]</sup>は…

ガルディヤ(ގަރުދިޔަ)[12]は…

dir 属性が使えないケース

例4: 注釈の番号「12」を以下の位置に表示したい:

ワイツマン(ヘブライ語: חיים ויצמן‎12)はイスラエルの初代大統領で…

普通に書いたのでは、こうなってしまう:

ワイツマン(ヘブライ語: חיים ויצמן12)は…

ヘブライ語の単語に注釈を付けるのなら、これはこれで正しい(単語の頭でなく末尾に注釈の印が付いている)。しかし、「ヘブライ語: חיים ויצמן」という「ヘブライ語の引用を含む日本語」に注を付けているのだとしたら、「12」は右側に来るべきだ。

「12」に dir 属性を与えることでは、もはや解決できない:

例4a:

ワイツマン(ヘブライ語: <span lang="he">חיים ויצמן</span><sup dir="ltr">12</sup>)は…

ワイツマン(ヘブライ語: חיים ויצמן12)は…

もしヘブライ語の span と数字の span の間に1文字でも(半角スペース1個でも)あれば、同じ方法でうまくいく。

例4b:

ワイツマン(ヘブライ語: <span lang="he">חיים ויצמן</span> <sup dir="ltr">12</sup>)は…

ワイツマン(ヘブライ語: חיים ויצמן 12)は…

だが、スペースを入れると表示も少し変わってしまう。ここでは、ヘブライ文字にぴったりくっつけて「12」を表示したいとしよう。

普通だったら、こういう場合、ゼロ幅のスペースが利用できる。U+2060 WORD JOINER や U+200B ZERO WIDTH SPACE だ(後者は改行禁則でないため、「ぴったりくっつけて」という条件とは少しずれる)。ところが、この二つの文字は双方向アルゴリズム上 BN(境界中立)というタイプで、「あってもなくても同じ」という扱いになっている。「あってもなくても同じ」なので、挿入しても区間を切り離すことができない。

次善の策として、U+200A HAIR SPACE を挿入してみよう:

ワイツマン(ヘブライ語: <span lang="he">חיים ויצמן</span>&#x200A;<sup dir="ltr">12</sup>)は…

ワイツマン(ヘブライ語: חיים ויצמן 12)は…

一応もっともらしい表示になったが、髪の毛1本の幅(ヘア・スペース)の妥協とはいえ、条件を満たせなかった。dir 属性を使うのでは制御できないテキスト方向というものがあることが分かる。

この場合、挿入すべき最善の文字は、もちろん &lrm; だ。その方法なら dir 属性は必要ない。

ワイツマン(ヘブライ語: <span lang="he">חיים ויצמן</span>&lrm;<sup>12</sup>)は…

ワイツマン(ヘブライ語: חיים ויצמן‎12)は…

一般に、この種の問題の解決法としては、dir 属性より LRM/RLM の方が優れている。​(RLM は LRM と正反対の働きを持つ特殊文字。この記事では使わない。)

特に、プレーンテキストの場合、HTML の dir 属性に相当する制御文字は2文字のペアで、「ここから」・「ここまで」と範囲を指定しなければならない。エディタで編集していると、往々にしてペアの2文字の片方だけがコピーされたりして、訳の分からない状態になる。LRM/RLM なら1文字だけなので、こうした問題が起きにくい。

HTML5 なら、いっそうすっきりする:

ワイツマン(ヘブライ語: <bdi lang="he">חיים ויצמן</bdi><sup>12</sup>)は…
<bdo> を使う方法

参考までに <bdo> を使う方法を挙げておく。

ワイツマン(ヘブライ語: <bdo dir="ltr" lang="he">ןמציו םייח</bdo><sup>12</sup>)は…

ワイツマン(ヘブライ語: ןמציו םייח12)は…

一応同じ表示が得られるが、良い方法ではない。単語や文を逆さまにタイプするのは面倒だし、結果はワードラップ、検索などの処理にもなじまない。

dir 属性における HTML のタグ構造の破綻

単純化した例で、問題を再検討してみよう。

<span dir="rtl">אב</span><span dir="ltr">12</span>

HTML の常識で考えれば、上のソースは、こう表示されると期待される:

‎ב‎ א‎ 1 2
← ← → →

実際には暗黙にネストが起きる:

‎1 2 ב‎ א‎
← ← ← ←
→ →

つまり、次の二つのソースが等価になってしまう:

<span dir="rtl">אב</span><span dir="ltr">12</span>

<span dir="rtl">אב<span dir="ltr">12</span></span>

HTML のタグ構造としては、次の二つが同じだと言っているのと同じことで、受け入れにくい:

<strong>bold</strong><em>italic</em>

<strong>bold<em>italic</em></strong>

さらに、次の二つが全く異なる構造を表しており、これも受け入れにくい:

<span dir="rtl">אב</span><span dir="ltr">12</span>

<span dir="rtl">אב</span> <span dir="ltr">12</span>

HTML と Unicode では、設計の前提も理念も大きく異なる:

HTML は内部的に Unicode の双方向アルゴリズムを利用しており、双方向性の処理では、上記の二つの異なる原理が混在している。ここではそれが表面化し、「仕様的には正しいが感覚的におかしい」という状況が生じている。

双方向アルゴリズムの詳細

[この記事の目次へ]

この章では、こうした挙動の背景にある Unicode のアルゴリズムを紹介する。次の単純化した例を考えよう:

「右から左」の文字に続く数字
論理的位置 1 2 3 4 5 6 7 8
文字 a b א ב 1 2

א(アレフ) と ב(ベート) はヘブライ文字で、右から左に אב と表示される。テキストデータとしては א が前にある。論理的位置は 0, 1, 2, … と 0 から数える方がよりコンピュータ的だが、ここでは 1, 2, 3, … としておく(本質的な違いはない)。

1. デフォルト

デフォルトでは、ヘブライ文字に続く数字がヘブライ文字の左側に表示される。

ab אב <span>12</span>

結果:

ab אב 12

「ヘブライ語は右から左に進むので、文字に続く数字は文字の左に来る」というだけのことだが、アルゴリズム的には次のように動作する。

双方向性における文字タイプ(初期状態)
論理的位置 1 2 3 4 5 6 7 8
文字 a b א ב 1 2
文字タイプ L L N R R N EN EN
埋め込みレベル 0 0 0 0 0 0 0 0
1. 文字タイプ

これ以外の型もあるが、ここでは必要ない。詳しくは仕様書(UAX #9)の Table 4. Bidirectional Character Types、または UAX #44の Table 13. Bidi_Class Values を参照。各種の型は Bidi_Class と呼ばれ、文字ごとに UnicodeData.txt のフィールド4で定義されている。同じ「右から左」でも R と AL の区別があるのは、数字の扱いが異なるため(付録参照)。

2. 弱タイプの解決

EN は、その位置から前へ前へとさかのぼって最初に出会う強タイプ(“支配者”)により、次のように型が変わる: ①支配者が AL なら、AN(「アラビア語数字」型)になる(規則 W2)。 ②支配者が L なら、L になる(規則 W7)。 ③支配者が R なら、EN のままになる。 ④区間始点に達した場合、区間始点タイプに支配される(後述)。ここでは③が適用される。

3. 中立タイプの解決

①同じ方向性の要素に挟まれた N は、その方向性に同化して L または R に変わる(規則 N1)。ただし、AN、およびこの時点で残っている EN は、「右から左」の方向性を持つ。 ②それ以外の(=異なる方向性の要素に挟まれた)N は、埋め込みレベルに応じたデフォルトの方向性を持つ(規則 N2)。

HTML 4 では、dir 属性のデフォルト値は ltr(左から右)であり(仕様書 8.2.2)、パラグラフはデフォルトでその方向になる。ここではデフォルトのままだと仮定している。従って、パラグラフの埋め込みレベルは 0 となる(規則 HL1)。これは「左から右」の基本状態を意味する。結局、位置 3 のスペースは N2 により L になり、位置 6 のスペースは N1 により R になる。

上記は HTML における処理であり、プレーンテキストのパラグラフとは扱いが異なる。また、区間境界が絡む場合も扱いが異なる(後述)。中立タイプの解決は暗黙レベルの解決より先に行われるため、この例では(この時点では)全文字がレベル 0 で、区間境界は絡まない。

4. 暗黙レベルの解決

レベル 0 において、R の文字は暗黙にレベル 1 になり、AN または EN の文字は暗黙にレベル 2 になる(規則 I1)。

弱タイプ・中立タイプ・暗黙レベルを解決した結果
論理的位置 1 2 3 4 5 6 7 8
文字 a b א ב 1 2
文字タイプ L L L R R R EN EN
埋め込みレベル 0 0 0 1 1 1 2 2
5. 表示順序の決定

これで全文字のレベルが定まったので、規則 L2 により、表示位置を決めることができる。

まずレベル 2 の区間を左右反転させる。

レベル 2 の文字(位置 7, 8)を左右反転
論理的位置 1 2 3 4 5 6 8 7
文字 a b א ב 2 1
埋め込みレベル 0 0 0 1 1 1 2 2

次に、「レベル 1 以上が連続する範囲」(レベル 2 の区間を含む)を左右反転させる。この処理をすると、レベル 2 の区間は合計2回反転されたことになり、元の向きに戻る。

レベル 1–2 の文字(位置 4, 5, 6, 8, 7)を左右反転
論理的位置 1 2 3 7 8 6 5 4
文字 a b 1 2 ב א
埋め込みレベル 0 0 0 2 2 1 1 1
2. LRM使用

次のように、特殊文字 LRM を挿入することで、数字を「右から左」区間から切り離し、右側に表示することができる:

ab אב&lrm; 12

結果:

ab אב‎ 12

ヘブライ文字の直後に LRM を挿入した場合
論理的位置 1 2 3 4 5 6 7 8 9
文字 a b א ב LRM 1 2
文字タイプ L L L R R L L L L
埋め込みレベル 0 0 0 1 1 0 0 0 0

LRM 文字は強タイプの L であり、従って数字(位置 8, 9)は W7 により L になる。位置 7 のスペースは N1 により L になる。結局、位置 4, 5 のヘブライ文字だけが「右から左」区間となる。

&lrm; については、HTML 4 の仕様書の 8.2.5 参照。

3. dir 属性使用

次の書き方でも、同じ結果が得られる:

ab אב <span dir="ltr">12</span>

結果:

ab אב 12

HTML でインライン要素に dir="ltr" 属性が指定された場合、Unicode の言葉で言えば、

…があるのと同等だ。つまり、内部的なデータは次のようになる:

明示的に埋め込まれたレベル
論理的位置 1 2 3 4 5 6 7 8 9 10
文字 a b א ב LRE 1 2 PDF
文字タイプ L L L R R N LRE EN EN PDF
埋め込みレベル 0 0 0 0 0 0 * 2 2 *

1. 明示的埋め込みの処理: 規則 X3 により、位置 8, 9 は、1段階上の偶数レベル、つまりレベル 2 になる。このレベルは、デフォルトの最終結果と変わらない。しかし、デフォルトでは暗黙に後からレベルが上がるのに対して、LRE を用いると明示的に先にレベルが上がる。この順序の違いにより、規則 W7 の適用結果が変わる。

規則 X9 により、LRE と PDF は除去される(以下、無いものとして扱われる)。

2. 弱タイプの解決: EN(数字)は、そこから前へ前へとさかのぼって最初に出会った強タイプに支配されるのだった(W2, W7)。ただし、区間始点(sor)に達したら、それ以上さかのぼらずに、区間始点タイプを支配者とする。

直前の強タイプが L 型の区間始点であるときにも W7 が適用されるかどうかは、明文規定されていない。リファレンスコードを見ると、その場合も W7 が適用される。

この例では、数字は区間始点タイプに支配される。区間始点の前方はレベル 0、後方はレベル 2 だから、区間始点タイプはレベル 2 に対応する L であり、EN は L に変わる。

Unicode 6.2 以下において、LRE は定義上、「強」カテゴリーの L だ。しかし、弱タイプの解決前に除去されるため、W7 には関係しない。

3. 中立タイプの解決: N の方向性は前後の要素の方向性によって決まるのであった(N1, N2)。ただし前後を考えるのは現在の区間内に限る。前方へさかのぼって方向性不明のまま区間始点に達したたら、区間始点タイプを「前方の要素」とする。後方へ進んで方向性不明のまま区間終点に達したら、区間終点タイプ(区間始点タイプと同様に定義される)を「後方の要素」とする。位置 6 のスペースは、その前方(位置 5)が R、その後方(区間終点)が L であり、N2 により L になる。

4. 暗黙のレベルの解決: 暗黙にレベル 1 になるのは位置 4, 5 のみ。この区間は表示上、左右が逆転する。

暗黙のレベルを解決した状態
論理的位置 1 2 3 4 5 6 7 8 9 10
文字 a b א ב LRE 1 2 PDF
文字タイプ L L L R R L LRE L L PDF
埋め込みレベル 0 0 0 1 1 0 * 2 2 *

「L になってほしい数字がデフォルトでは EN のままなので、数字に dir 属性を設定する」というのは一見筋が通っているし、結果的にも望む表示が得られる。しかし、内部的には次のような回りくどい動作となる:

  1. dir 属性を明示した結果、位置 8, 9 が独立した区間となった。区間始点タイプが L になるので、数字も L になった。位置 6 のスペースから見れば、区間終点タイプが L になるので、このスペースも L になり、暗黙にレベル 1 となる運命を免れた。
  2. これにより、レベル 1 とレベル 2 の間にレベル 0 の部分ができ、レベル 2 区間はレベル 1 区間と分断された。そのため、左右反転処理において、「レベル 1 以上が連続する範囲」の反転のときレベル 2 区間が巻き込まれなかった。

核心は区間の分断だ。実際、dir 属性を明示して数字を L にしても、レベル 1 区間とレベル 2 区間が密着している場合は望む結果が得られない。この例では、位置 6 にたまたまスペースがあったので、両者は分断された。

4. dir 属性使用(前区間と密着)
ab אב<span dir="ltr"> 12</span>

結果:

ab אב 12

この例は前項とほとんど同じだが、最終的な表示は大きく異なる。一般に、「右から左」区間と「左から右」区間をこの順序で密着させたい場合、dir 属性ではうまくいかない。

レベル 1 区間とレベル 2 区間の密着(位置 6)
論理的位置 1 2 3 4 5 6 7 8 9 10
文字 a b א ב LRE 1 2 PDF
文字タイプ L L L R R LRE L L L PDF
埋め込みレベル 0 0 0 1 1 * 2 2 2 *

もし位置 6, 7(LRE とスペース)が入れ替われば前項と全く同じデータになり、密着は起きない。

密着している場合、数字は、L 型の区間始点に支配されて L になる。位置 7 のスペースは、前後の型がいずれも L なので L になる(N1)。位置 7 の前方の型とは、区間始点タイプだ。最後に、R の区間は暗黙にレベル 1 になる。

表示位置の反転処理は次のように進み、レベル 2 区間が「レベル 1 以上が連続する範囲」の反転に巻き込まれる:

レベル 2 の文字(位置 7, 8, 9)を左右反転
論理的位置 1 2 3 4 5 6 9 8 7 10
文字 a b א ב LRE 2 1 PDF
埋め込みレベル 0 0 0 1 1 * 2 2 2 *
レベル 1–2 の文字(位置 4, 5, 6, 9, 8, 7)を左右反転
論理的位置 1 2 3 7 8 9 6 5 4 10
文字 a b 1 2 LRE ב א PDF
埋め込みレベル 0 0 0 2 2 2 * 1 1 *

便宜上、LRE(位置 6)も反転の範囲に含めているが、この制御文字はアルゴリズムの初めで除去されており、実際には消滅している。別の言い方をすれば、ある範囲に span を設定して dir 属性を明示しても、span 要素の「ここからここまで」という情報は、双方向アルゴリズムの初期において捨てられてしまう。

5. HTML5 の <bdi>(Unicode 6.3)

HTML5 の <bdi>(Bidirectional Isolate)は、それ自体としてテキスト方向を指定するものではない。方向区間を隔離するのが主な役目だ。HTML 4 の <bdo>(Bidirectional Override)とは関係ない。

ab <bdi>אב</bdi> 12

結果(予想):

ab אב‎ 12

bdi 要素の機能に対応する Unicode 側のアルゴリズム改訂は、次のバージョン 6.3 で暫定的に行われる見通しだ。正式導入は、Unicode 7 で予定されている。この節の説明は、仕様草案(2012年10月26日付け draft 3)に基づく将来のアルゴリズムの予想だ。仕様はまだ確定でないので、今後内容が変わるかもしれない。

bdi 要素は、Unicode の言葉で言えば、

…があるのと同等だ。これら4種類の制御文字は、Unicode 6.3 で追加が予定されている。

LRI/RLI/FSI の区別は、bdi 要素の dir 属性値による。​"ltr" なら LRI、​"rtl" なら RLI、​"auto" なら FSI で、デフォルトは "auto" だ。​"auto" の場合、内部的には "ltr" または "rtl" が(要素の内容に応じて)自動選択される。dir 属性が明示的に指定されなかった場合でも、​"auto" が設定されることに注意。つまり、bdi 要素は常に(明示的または暗黙的に)自分自身の dir 属性を持ち、決して上位の dir 属性を継承しない。

dir 属性を持たないインライン要素が上位の dir 属性を継承しないのは、一般的な規則だ(リンク先の最後のパラグラフ参照)。一般に、明示的な dir 属性を持たないインライン要素は dir 属性を継承もリセットもせず、双方向的な埋め込みに一切関与しない。ところが、明示的な dir 属性を持たない bdi 要素は、要素の内容に応じて自動的に決定される暗黙の dir 属性を使って、双方向的な埋め込みを行う。

隔離されたレベル
論理的位置 1 2 3 4 5 6 7 8 9 10
文字 a b FSI א ב PDI 1 2
文字タイプ L L N N R R N N EN EN
埋め込みレベル 0 0 0 0 1 1 0 0 0 0

1. 隔離: FSI と PDI により埋め込まれた区間において、最初の強タイプが R または AL なら、FSI を RLI とみなし、そうでなければ、FSI を LRI とみなす(規則 X5c)。ここでは、前者のケースに当たる。埋め込まれた区間のレベルは、従来の LRE/RLE による埋め込みと同様に設定される。隔離開始文字(LRI, RLI, FSI)と隔離終了文字(PDI)は、それらが囲む隔離区間には属さず、埋め込みが行われるバックグラウンドの側に属する(この例では、レベル 0)。

区間列(定義 BD9): 有効な隔離開始文字で終わる区間と、対応する隔離終了文字から始まる区間は、処理上連続する。それ以外の2区間は、処理上連続しない。処理上連続する区間をつなげたものを区間列と呼ぶ。処理上どの区間とも連続しない区間は、独立した区間列(長さ1の列)と見なされる。

この例では、

  1. 位置 1, 2, 3, 4 から成る区間は、有効な FSI で終わり、
  2. 位置 7, 8, 9, 10 から成る区間は、対応する PDI で始まるので、

…それら2区間は処理上連続し、一つの区間列を成す。位置 5, 6 から成る区間は、処理上どの区間とも連続せず、長さ1の区間列となる。

区間とは、同じ埋め込みレベルが続く範囲。区間列は、1個または2個以上の区間を並べたもの。区間列を構成する区間は、必ずしも位置的に連続しない。

区間列は区間の列、区間は文字の列なので、区間列は文字の列とも解釈できる。

隔離開始文字と隔離終了文字は、正しい入れ子レベルで対応している場合にのみ有効と見なされる。この結果、一つの区間列に属する全部の区間は、同一の埋め込みレベルになる。

2. 弱タイプの解決: 位置 9 以降の数字のタイプを確定するため、強タイプを求めてその位置から前方へさかのぼる。ただし、従来は区間内をさかのぼったが、拡張されたアルゴリズムでは区間列内をさかのぼる(最大で、区間列始点まで)。8→7→4→3→2 までさかのぼると、L に出会う。よって、数字は L になる(W2)。

3. 中立タイプの解決: 従来、中立タイプの解決は、区間内において、その文字の前方と後方の方向性に従って行われた。拡張されたアルゴリズムでは、区間列内で同じ判断を行う。位置 2 の L と位置 9 の L は、一つの区間列内において4個の N を挟んでいる。これらの N は L に変わる(N1)。位置 5, 6 の R はこの区間列に属さない。

4. 暗黙レベルの解決: 位置 5, 6 は、奇数レベルの R なので、レベルに変更はない。

弱タイプ・中立タイプ・暗黙レベルを解決した状態
論理的位置 1 2 3 4 5 6 7 8 9 10
文字 a b FSI א ב PDI 1 2
文字タイプ L L L L R R L L L L
埋め込みレベル 0 0 0 0 1 1 0 0 0 0

このほか、Unicode 6.3 の新アルゴリズムでは、エラー処理の機能が提供される見通しだ。例えば、​<bdi> 要素に「閉じていない双方向性制御文字」が含まれている場合、その影響も隔離される。

付録

[この記事の目次へ]
「右から左」の文字システム一覧

双方向性の話では、「アラビア文字・ヘブライ文字などの右から左に書く文字」という表現がよく使われる。「など」というのは、具体的に何を含んでいるのだろうか。

以下の記述で、言語の分類・話者数は Ethnologue 第16版(2009)・第17版(2013)による。Ethnologue の信頼性には限界があるが、目安にはなる。

アラビア文字系

現在、最も多く使われている「右から左」の文字は、アラビア文字系のシステムだ。

標準アラビア語は2億5千万人によって話される国際語で、国連の公用語。アラビア文字で書かれる。アラビア語には、このほか、エジプト・アラビア語、アルジェリア・アラビア語、スーダン・アラビア語など多くの変種があるが、それらは基本的に話し言葉で、文字で書かれることは少ないようだ(従って「右から左」の話とは直接関係ない)。

ペルシャ文字はアラビア文字に数文字を加え、一部の文字の形を変更したもの。ペルシャ語を記すのに使われる。狭義のペルシャ語(イラン・ペルシャ語)はイランの公用語で、4700万人によって話される。アフガニスタンの公用語の一つダリー語(アフガン・ペルシャ語)は、書き言葉としてはイランのペルシャ語と事実上同じだとされる。およそ1000万人の話者がいる。タジキスタンの公用語タジク語もペルシャ語系だが、今はペルシャ文字では書かれない。一方、イランのアゼルバイジャン語(南アゼルバイジャン語)は、1000万人以上により話され、ペルシャ文字で記される。

ウルドゥー文字は、ペルシャ文字をさらに拡張したもので、ウルドゥー語を記すのに使われる。ウルドゥー語は、インド、パキスタンでそれぞれ公用語の一つであり、6000万人の話者がいる。

書くのにアラビア文字を使う(またはかつて使った)言語は多い。例えば、パシュトー文字はアラビア文字系のシステムで、パシュトー語を記すのに使われる。パシュトー語はアフガニスタンのもう一つの公用語だ。

ヘブライ文字

ヘブライ文字は、世界で2番目に多く使われる「右から左」の文字。旧約聖書の文字として知られ、現代語でも、イディッシュ語(話者1300万)や現代ヘブライ語(話者500万)の表記に使われる。一部のヘブライ文字、特にアレフは、数学記号として国際的に使われる(ただし、Unicode では、数学記号としてのヘブライ文字には別のコードポイントが割り当てられている)。

ターナ文字

ターナ文字は、ディベヒ語を表記するのに使われる文字システム。ディベヒ語は、インドの南の島国モルディブの公用語で、インドのミニコイ島の主要言語でもある。約34万人の話者がいる。

詳細については、別記事「ターナ文字入門: 表記と発音」参照。

シリア文字

シリア文字は、古典シリア語を記すのに使われる文字システム。宗教上では現在も使われる(アッシリア東方教会・カルデア教会・シリア正教会など)。現代シリア語を記すのにも使われる。別の文字とも言えるほど異なる3種類の字体を持つ。ヘブライ文字・アラビア文字とやや似ている。

いわゆるアッシリア人(シリアック)の文字であり、現代のシリア・アラブ共和国とは関係ない。この人々は、中東がイスラム化される前からトルコ・シリア・イラク・イランなどの地域に住み、キリスト教の文化を保ってきた。現在は少数民族となっている。現代シリア語には、合計約50万人の話者がいるが(2013年の推計値)、不穏な情勢のため国外に移住した人が多く、世代が変わるにつれ言語使用者が減る傾向がある。

現代シリア語のうち、イラン北西部ウルミエなどの方言はアッシリア現代アラム語とも呼ばれる。イラク北部アルコーシュなどの方言はカルデア現代アラム語とも呼ばれる。両者は「東方言」と総称されることがある。トルコ南西部ミドヤトなどのアッシリア人(シリア人)が使うトゥロヨ語(スライト語)も近縁の言語であり、「西方言」と呼ばれることがある。

シリア文字は紀元1世紀にさかのぼる長い歴史を持ち、この文字で書かれた多くの著作物が存在する。しかし、現在、日常生活でシリア文字が使われることは比較的少ない。

別記事「シリア文字メモ: Unicode規格書の問題、Firefoxの問題」も参照。

ンコ文字

ンコ文字は、1940年代にギニアで作られた文字システムで、西アフリカのマンディング諸語の表記に使われることがある。マンディング諸語の主なものは:

バンバラ(バマナンカン)語 [bm][bam]
話者407万(主にマリ)
東マニンカ(東マニンカカン)語 [emk]
話者353万(主にギニア)
ジュラ(デュラ)語 [dyu]
話者255万(主にブルキナファソとコートジボワール)
マンディンカ語 [mnk]
話者135万(主にセネガルとガンビア)
マンダ文字

マンダ人は、メソポタミア地域(現在のイラクなど)を故郷とする少数民族(民族的・宗教的コミュニティー)の一つ。この人々が伝統的に使う言語がマンダ語(アラム語系)で、マンダ文字により記される(シリア文字に似ている)。 マンダ人の総数は、およそ6–7万人とされる(2008年資料: N3485)。 ただし、少数民族への迫害のため、大部分は故郷を離れた。

古典マンダ語は、民族の宗教において現在でも使用される。 現代マンダ語は現代東アラム語の一種であり、日常的にも使われることがある。 Ethnologue によれば、イラクに、5000人の話者がいるという(2006年)。

サマリア文字

このほか、Unicode の基本多言語面にはサマリア文字が収録されている。かつてサマリア人がサマリア・ヘブライ語などを記すために用いた文字だが、サマリア人は総数約750人(2012年1月1日現在)の少数民族となってしまった(イスラエル、パレスチナに住む)。この人々はヘブライ語またはアラビア語を第1言語としており、サマリア文字は主に宗教的目的で使われる。

そのほか

補足多言語面には「右から左」の文字用になっているエリアが2カ所あり、Unicode 6.2 の時点で10種類以上の文字システム(古代文字や歴史的システム)が収録されている。

アラビア文字型とヘブライ文字型の違い

Unicode の双方向アルゴリズムでは、同じ「右から左」の強タイプでも R と AL の区別があり、アルゴリズム上、R に支配される数字と AL に支配される数字は、振る舞いが異なる。

次のソース例では、アラビア文字 اب とヘブライ文字 אב の後ろに、同じ文字列をペーストしている:

<p dir="rtl">
<span>اب</span> / 2012-03-14 / 1+2<br />
<span>אב</span> / 2012-03-14 / 1+2
</p>

対応しているブラウザでは、数字部分の表示に差ができるはずだ:

اب / 2012-03-14 / 1+2.
אב / 2012-03-14 / 1+2.

AL 型の文字
R 型の文字
アラビア文字などの部分に dir 属性は必要?

「左から右」区間内で、「右から左」の語句をちょっと引用する程度なら不要。dir 属性が必要になるケースの代表は、引用部分がピリオドなどの句読点で終わっている場合(末尾の句読点が U+061F ARABIC QUESTION MARK [؟] のときは、dir 属性は必須ではない)。

インライン要素

アラビア文字・ヘブライ文字などの語句を引用するときに dir="rtl" を指定することは、間違いではないが、あまり意味がない。これらの文字は強タイプで、どっちにしても dir 属性では文字タイプを設定(上書き)できないからだ。実際、逆方向の dir="ltr" を指定しても結果は変わらない。「右から左」は暗黙レベルで十分で、全体は一つの「左から右」区間に収まる。

「右から左」の枠を明示する必要があるのは特殊な場合のみ: ①引用部分が中立タイプの文字(句読点など)で終わる場合、②アラビア文字・ヘブライ文字などの部分が内部に「左から右」の文字を含んでいる場合、③数字で始まる場合など。

dir 属性は title 属性値(いわゆるツールチップ)にも適用されるため、以下のような場合、余計な指定をするとかえって表示が乱れる:

ナセル(<span title="アラビア文字。アブデンナセルと読む。">عبد الناصر</span>)は…

ナセル(عبد الناصر)は…

これを次のように書いてしまうと、ツールチップがおかしくなる:

ナセル(<span title="アラビア文字。アブデンナセルと読む。" dir="rtl">عبد الناصر</span>)は…

ナセル(عبد الناصر)は…

仕様書の dir 属性の項には、​This attribute specifies the base direction of directionally neutral text […] in an element's content and attribute values. とあり、方向性の枠組みは要素の値と属性値の両方に及ぶ。これを厳密に解釈すると、すぐ上の例では、ツールチップが
「。アラビア文字。アブデンナセルと読む」
…のように表示されてしまう。

厳密には、こう書くことができる:

ナセル(<span title="アラビア文字。アブデンナセルと読む。"><span lang="ar">عبد الناصر</span></span>)は…

この書き方なら、アラビア文字に dir 属性を指定しても表示が乱れない。

ブロック要素

パラグラフ全体を右から始めたい場合、ブロックレベルで dir="rtl" を指定する必要がある。dir 属性はブロックレベルでは継承されるので、最上位の「右から左」要素でまとめて指定できる。

参考文献

[この記事の目次へ]

HTML5 の bdi 要素と… > 更新履歴

  1. 2012年12月4日: 初版。
  2. 2012年12月5日: 入門編セクション3を一部改訂。bdi 要素の意義を明確化。
  3. 2012年12月6日: 「右から左」の文字一覧で、シリア文字・ンコ文字・サマリア文字の項を改訂。
  4. 2012年12月7日: dir 属性が title 属性に影響する例を分かりやすいものに変えた。
  5. 2012年12月14日: 細部の調整。
  6. 2012年12月15日: ויצמן の片仮名表記を「ワイズマン」から「ワイツマン」に変更。
  7. 2013年1月16日: ターナ文字についての記述を更新。「ターナ文字入門: 表記と発音」へのリンク追加。
  8. 2013年1月27日: 「右から左」の文字一覧に、マンダ文字の項を追加。
  9. 2013年2月1日: 「右から左」の文字一覧で、シリア文字の項を少し改訂。
  10. 2013年2月11日: 「右から左」の文字一覧: 言語名 Tûrôyo (Ṣurayt, Surayt, Suryoyo) の日本語表記を、「トゥロヨ語」から「スライト語」に変更。
  11. 2013年2月26日: 「右から左」の文字一覧: 「スライト語」を「トゥロヨ語(スライト語)」に変更。「シリア文字メモ」へのリンク追加。
  12. 2013年8月23日: (1) Unicode 6.3での「かっこの処理の改善」が最初の予定よりも充実する見通しであることを追記。 (2) 「西ファルシ語」という表記を「イラン・ペルシャ語」に変更。話者数も「2000万人以上」から「4700万人」に更新。これらの変更は Ethnologue 16→17版での更新に対応している。ペルシャ語では「ペルシャ語」を fārsi と呼ぶが、この場合、英語では現地名を使うべきではないという議論がある(Persian NOT Farsi: Iranian Identity Under Fire)。同様に「東ファルシ語」を「アフガン・ペルシャ語」に変更。
  13. 2013年11月29日: 「シリア文字」の項を改訂。現代シリア語の話者数を52万から50万に変更。これは、Ethnologue の第16版(2009)→第17版(2013)での更新に対応する。
  14. 2014年1月12日: 「ンコ文字」の項を整理。Ethnologue 第17版により話者数データなどを更新。
  15. 2014年2月23日: IE5.5対応を改善。
  16. 2014年4月27日: 「別の文字とも言えるほど異なる3種類の異なる字体」→「別の文字とも言えるほど異なる3種類の字体」。

この記事のURL



<メールアドレス>