あいまい幅の問題は大きくなる方へ向かってる

Posted on:

あいまい幅とは

Unicodeには、幅が「あいまい(Ambiguous)」とされる文字が存在します。 ターミナル等の固定幅の世界では、アルファベット等の幅が1(半角)、漢字やひらがな等の幅が2(全角)になります。 あいまい幅とされている文字の多くは元々日本を含む東アジアの方で全角の文字で使われていた文字が多いです。 これらの一部の文字(記号)がUnicodeでは全角幅とはならずにAmbiguousとして定義されています。

Unicode文字の使用が増えている

Unicodeが使用されるようになって、日本では当然ながらJISの文字コードが使用されていた文字がUnicodeになってからもそのまま使用されていました。 英語圏ではASCIIコードの範囲内の文字の使用が基本で、それ以外の文字はそれほど多くされていませんでした。

ただ最近では絵文字が世界中で使用されるようになっていて、ASCIIコードではなくUnicode(UTF-8)で解釈する必要があるテキストが増えてきました。 そういったテキストが増えてくるとターミナル上でもそれらの文字が使用されるようになり、絵文字以外の文字も使用されるようになってきています。

あいまい幅に該当する文字の使用も増えている

CLIやTUIでも罫線(┌───┬───┐)を利用して境界を示すようなテーブル等の表示が増えてきました(以前は(+—+—+)のようなASCIIコードの範囲内の文字を使用していました)。

ところが罫線(┌─└─┬─┤)のような文字はあいまい幅の定義になっています。日本語環境でもこれらの文字を使用して枠が書かれていたので、それらの互換性のために全角で使用できる必要があったのですが、英語圏でも使用したいとなったら、幅が倍の全角よりも半角幅で枠が書けたほうが便利なため理にかなっています。

TUI,CLIでのあいまい幅の問題

しかし、ここに問題があります。

英語圏で想定している罫線は半角幅で表示されることを想定していますが、日本語環境では全角で使用されていたため全角を想定しています。 半角で想定していた罫線を文字数をそのままで全角の環境で表示すると横幅だけが倍になり、中の値の表示の幅はそのままなので枠だけがずれてしまいます。逆に全角幅を想定した文字数をそのまま半角で表示すると中の文字数(幅)に対して枠線は半分の幅を使用するため、中身だけずれてしまいます。 つまり枠線を書く場合も幅が1または2の両方ありえることを想定して、文字数ではなくその幅が確保できるように書かなければなりません。 そして、実際にその文字の幅が1になるのか2になるのかを教えてくれるような統一的なAPIがターミナルに存在していません。 アプリケーションによっては実際に書いてみて、読み取ることで判別するようなことをやっていることもあるようです。 単純に幅がわかったとしても枠を書くだけで最初の想定よりも大幅に大変になります。

例えば、TUIアプリケーションで罫線(┌ ─ ┘など)を使う場合、環境によっては1文字幅、別の環境では2文字幅で表示され、レイアウトが崩れることがあります。

GoのTUIで表示が崩れる場合も参考にしてください。

日本語環境であいまい幅=2を見直す必要がある

TUIの罫線であいまい幅の文字が使われる場合は、両方の幅に対応するのが理想です。 しかし、罫線が使われるような場面では、そのハードルは非常に高いと感じています。 英語圏の作者では特に、この幅が変わることを想定するのが難しいです。 そのため、ターミナルエミュレーターの作者やTUIライブラリの作者は(東アジアの)言語環境での切り替えを止めて、あいまい幅の幅を1をデフォルトにして2に切り替える別の手段を用意したり、切り替えられないようになっていたりします。

Go言語の有名なライブラリでも筆者がそのように進言しました。 あいまい幅の幅が1を想定しているTUIでは、あいまい幅が2の環境ではどうやっても問題が出てしまうためです。

どうすればよいのか?

通常使う環境のデフォルトは、あいまい幅=1で統一することを推奨します。 Goのアプリケーションの場合はRUNEWIDTH_EASTASIAN=0(無効)の環境変数をセットします。 新しいtcell(v2.11.0以降)では、設定する必要もなくなっています。

新しいtcellでは文字幅の計算がrunewidthからunisegに変更されて、デフォルトはあいまい幅=1になり、互換性のためRUNEWIDTH_EASTASIAN=1(有効)の場合のみ動作するようになっているためです。

あいまい幅=2を使用したい場合

あいまい幅=2で使用したい場合は、そのためのターミナルアプリケーションの設定とフォントを用意して限定的に実行できる環境を用意すると良いでしょう。

もし、TUIアプリケーションの作者であいまい幅=2の日本語環境向けに作りたい場合はtcellを使っていても、最初にグローバル変数をセットすればあいまい幅を2にすることができます。

uniseg.EastAsianAmbiguousWidth = 2

CLIは問題ない?

CLIでもテーブル形式や縦を揃えて出力する場合はTUIと同じ問題がおきます。

幅は関係なく文字列をそのまま出力する場合は大きな問題は起きません。 ただ、東アジア圏の方向け以外では、あいまい幅は1が想定されているため、幅が2になると出力が不格好に見えることが増えるでしょう。 例えばleft← →rightの矢印だけ幅が広いと出力に違和感を感じる人も出てくるでしょう。