まつもと ゆきひろです

I18Nについてこんなこと考えてみました。

---
= 前提条件

  * 現行のAPIを変更しない

    すべてのRubyプログラムが99%変更無しに動作する。ただし、
    「構造体の内容を直接変更しない」などの制約が付くのはあり
    える。(現状でも暗黙のうちに仮定しているので)

  * 移植性がある

    現状Rubyが動作しているすべてのプラットフォームで動作する
    必要がある。外部コマンドやライブラリによる対応は仮定でき
    ない(よってlocaleもiconvも使えない)。

  * 実行効率が悪くない

    I18N対応による実行速度の低下はわずかしか許容できない。具
    体的には、GCやVM化など他の改善なども勘案した後、最悪でも
    数%程度に収まる必要がある。特にバイトを文字の単位として
    扱うモード(無変換モード)での低下は最小にすること。

= 問題

(a) 現状のRubyの文字列は実質的にバイト列であるが、世の中の多
    くの国では1文字が複数バイトで表現されるマルチバイトエン
    コーディングが用いられている。この場合、文字列を「文字単
    位」で扱うことができない。

(b) Rubyは国産であるため、正規表現については、日本で広く用い
    られているEUC, SJIS, UTF-8の各エンコーディングについては
    対応しているが、それらの対応はハードコードされており、新
    しいエンコーディングへの対応は容易でないうえに、ユーザ定
    義は不可能である。

上記(a)の問題についてはjcodeライブラリで、ある程度対応できる
が依然

  * 処理単位が文字かバイトかの切替えがグローバルにしか行えない

  * EUC, SJIS, UTF-8にしか対応できない

という欠点が残る。

他のスクリプト言語(Perl, Python)では、この問題に関して、以下
のような対応を行っている。

  * 文字列をUnicode(UTF-8)で保持し、文字単位で処理する。

  * バイト列と文字列を切替えられる(Perl: スコープ単位で宣言、
    Python: 別クラス)。

Unicodeは多くの文字集合を同時に表現できるため、内部エンコー
ディングとしては都合の良い性質を持っている。しかし、この対応
には以下の問題がある。

(c) Unicodeの性質と文字集合間の変換テーブルの乱立などにより、
    ラウンドトリップ(2度変換をして同じ文字列に戻ること)が成
    立しない場合がある(yen sign problem)。

(d) Unicode以外で表現されたテキスト処理には必ず変換が必要と
    されるため、実行効率のペナルティが予想される。

(e) Unicodeとそれ以外のエンコーディングとの間の変換はテーブ
    ルを用いて行うより他なく、Unicode以外で表現されたテキス
    ト処理には必ず相当なサイズのテーブルをロードする必要があ
    り、インタプリタサイズの増大を招く。

(f) 文字列の内部表現がUTF-8である場合でも、C APIが要求するエ
    ンコーディングは既存のものであることが予想され、拡張ライ
    ブラリで明示的な変換を要求することになる(前提との矛盾)。

このうち、(c)はほとんどの場合グリフの相違程度と考えることが
できるので致命的ではないことが多い。しかし、重大なことはこの
問題に対する回避策が提供されないのはことである。(d), (e)は時
間、空間の効率の問題であるため、ある程度妥協点を見出すことが
できる。しかし、すでに既存のエンコーディングのデータを大量の
保持している場合には特に(d)を無視することは難しい。(f)は互換
性をどこまで保証するかという問題である。しかし、既存のエンコー
ディングをベースにした蓄積がある場合には大きな問題となりえる。

結論としては、PerlやPyhonのような内部コードをUTF-8に揃えると
いうアプローチは「今までASCIIデータ(+α)しか扱ってこなかっ
たが、今後はUnicodeも使いたい」というニーズには十分であるが、
「今までマルチバイトテキストデータを大量に処理してきて、それ
は早急にはUnicodeに置き換わらない」という日本(や他のアジア諸
国)のニーズには適合しないと考えられる。

= 要求

  * 文字単位での処理が行いたい

    より直感的な処理

  * 多くのエンコーディング(および文字集合)を扱いたい

    既存のエンコーディングは数多くあり、Unicodeで話は終らな
    い。Unicodeは将来広く使われるだろうが、あくまでも対応す
    る文字集合のひとつという位置づけであるべき。

  * 文字集合はユーザ定義可能にしたい

    文字集合は多く、すべてにあらかじめ対応することは不可能で
    ある。また、Unicodeが完全でない以上、今後も文字集合が登
    場する可能性はある。

  * 文字は変化してはいけない

    仮に内部的な文字集合の変換を行うことがあっても、その変換
    による文字の変化は許容できない。よって、実質的には文字集
    合の変換を行えない。

= 選択肢

以下の二つの選択肢が考えられる。

(1) 文字集合はそのまま、エンコーディングは揃える

    文字の変化(c)、変換テーブルによる空間コストの増大(e)は結
    局はエンコーディングの変換ではなく、文字集合の変換によっ
    て発生する。逆に言えば、文字集合を操作しなければこれらの
    問題は回避できると言うことである。

    そこでUTF-8が用いているエンコーディングが31bit幅の任意の
    整数をエンコードできることに着目し、文字集合はオリジナル
    のまま、文字列のエンコーディングだけUTF-8エンコーディン
    グに揃えることを考える。

    この場合、バイト列から個別の文字を順に取り出す手続きを定
    義するだけで、文字単位の処理が可能になる。ほとんどの文字
    処理に必要なのはエンコーディングに関する知識のみであるこ
    とは実証されているので、このことにより、実装をUTF-8(エン
    コーディング)対応だけに限定することができ、実装が簡潔に
    なることと、UTF-8の持つ良い性質(リーディングバイトとそれ
    以外のバイトを区別できる、逆方向へのスキャンが可能)を利
    用できるようになる。

    しかし、この対応では(f)の問題には対処できないので、文字
    列のポインタ部分の直接取り出しの禁止、およびポインタ部取
    り出し関数におけるエンコーディング変換が必要になると思わ
    れる。

(2) 文字列は一切変換しない

    その上で、文字操作を抽象化した構造体(各操作毎に関数ポイ
    ンタを持つ)を経由して操作する。元の文字列に対する変換を
    一切行わないため、(c)、(d)、(e)、(f)の各問題は発生しない。

    この選択肢における課題は、文字列に対する直接操作を行うこ
    とができず、すべて構造体経由の操作とする必要があることで
    ある。そのための実行効率のペナルティと、実装が複雑になる
    ことが予想されるので、これらがどのくらい妥協できるかを見
    積もる必要がある。

なんとなく後者の方がすぐれているような....

= 課題

入力ストリーム、プログラムのエンコーディングを指定する方法

  * デフォルト(-Kで指定)
  * 明示的な指定(ストリーム: line discipline、プログラム: ??)
  * guessを許すか