まともな国際化を。minilang は Unicodeベースです。
ASCII文字だけを特別扱いするのではなく, 漢字・ひらがなを含め, 多様な文字を使えるようにします。
Rubyに合わせて, 大文字で始まるのは定数, 小文字はローカル変数です。メソッド名は小文字始まりのみ [[incompat]]。とりあえず, 次のようにしてみました。\p{ }
は, Unicodeプロパティ名 (Unicode property name) です。
CapitalLetter = \p{Lu} | \p{Lt} NonCapitalLetter = \p{Ll} | \p{Lm} | \p{Lo} | "_" | [\x3400-\x4db5] | [\x4e00-\x9fcc] IdentifierPart = ( CapitalLetter | NonCapitalLetter | \p{Mn} | \p{Mc} | \p{Nd} | \p{Nl} )* ConstIdentifier = CapitalLetter IdentifierPart Identifier = NonCapitalLetter IdentifierPart ( "!" | "?" )?
次の文字は空白として扱います。当然, 全角空白もホワイトスペースです。
WhiteSpace = [\t\x{FEFF}\x{000b}\x{000c}\p{Zs}]
まずどのぐらいのレベルを目指すか, です。
よくある状況として, 一枚のWebページに、いろいろな国の人が書き込んだ内容を表示することを考えてみてください。1枚の、という時点で、一つの文字列にいろいろな国の文字を含められるようにしなければならない, と分かります。
現実的にこれを実現する方法は, 一つの文字列に複数の文字集合 character sets を含めるか, すべての文字を含む文字集合を使うか, のいずれかです。
Emacs は, 古くから, 一つのテキストの中で実用的に多言語を扱えました。
この機能は, 当初は Emacs に対する拡張として提供されていて, 1995年には Emacs 19.28ベースの Mule 2.3 (末摘花) がリリースされていました。末摘花, ということは, その前に5つバージョンがあります。(整理されたページがありました; History of Emacs and Mule)
Mule は, それぞれの文字を, {文字集合を表すコード + その文字集合内のコード} という組で表現したようです。これにより, 一つのテキスト (文字列) 内で, 複数の文字集合の文字を同居させ, 多言語を実現していました。
その後, 1999年頃 (?) には UTF-8 などを扱う Mule-UCS が出ていたようです。
そこからだいぶ経って, Emacs23で, 上の (文字集合 + その中でのコード) というやり方を捨て, 内部コードが Unicode になりました。これが2009年7月.
ruby 1.9.1は2009年1月にリリースされました。くしくも Emacsがバージョン23でUnicodeベースになった年です。
ruby 1.9では, 文字列がその文字コード (エンコーディング) を保持するようになりました。Rubyist Magazine - Ruby M17N の設計と実装 文字集合ではなく誤って文字コードを使ってしまっています。
とても残念ですが, 文字列が文字コードを持つ, ただそれだけ。多用字系ですらない。こんなものを多言語 (multilingual) と呼ぶのは, どうかしているとしか思えません。
Encoding::Converter
クラスが文字コードの変換を担当します。
複数の文字集合による多言語化は, ボランティアベースで実用的な水準に至ることは、ほぼ不可能です。CSI は理想に見えますが, それはただの青い鳥です。
ざっと考えただけでも,
などなどを, *すべての*文字集合に対して考えなければなりません。不可能ではないでしょうが, それは商用プロダクトでしかできない / 商用プロダクトでも難しい領域です。
加えて、絵文字など Unicode にしか収録されていない文字が大量に増えてきました。
現在でも更新が続いているライブラリには, 例えば The m17n project があります。
大きな文字集合にするか, 小さな文字集合を切り替えるか, ですが, 効率性の面から, 大きな文字集合のほうがいいでしょう。
2012〜3年現在、上に書いた要求を実用的にこなせるのは、Unicodeだけです。Character Set Independence (CSI) でもいいのですが、でも、いつもUnicodeを選択している状態になります。
ということで、minilang は文字列をUnicodeベースにしてみました。minilang における「文字」は、UTF-16 code unitで、文字列はその並びです。UTF-16はサロゲートペアがあるので, 1または2 code unitで Unicode code point になります。
サロゲートペアは、開発者 (minilangの利用者) が適切に対応してください。
本物の文字とUnicodeコードポイントは, 一致することもあれば, 一致しないこともあります。複数のコードポイントが合成されることが多々あります。Forms of Unicode
ruby 1.9 で String#[]
が文字列を返すように変更されましたが, これは妥当な変更です。一つの code unit はサロゲートペアでは意味がなく、さらに一つの code point も文字の構成部品にしかならない場合も多いです。
IOへのインパクトのほうが大きいです。
Ruby 1.9の String
はただのバイト列なので、テキストファイルであれバイナリファイルであれ、IO
の各メソッドが同じ型を返せます。
minilangでは、String
をUnicode文字列と決めたので、バイナリファイルの入出力では使えません。
どのようにクラスを実装すべきでしょうか?
ファイル (IO) の用途を考えると、次に分けれるでしょう。
1番目のケースは、単純に、読み書きのときに変換すればよろし。読み込み、書き込みの型は String
。
2番目、3番目のケースでは、String
は使えません。
minilang では, 型 (インタフェイス) が違うので、バイナリファイルを扱う IO
と、テキストファイルを扱う TextReaderWriter
の二つのクラスに分割しました。
互換性をある程度確保するために, File.open()
がその引数に応じて IO
か TextReaderWriter
のいずれかのオブジェクトを返します。
IO
の実装が軽くなる, という利点もあります。
昔ならロケールにもとづく文字コードと見なすのがよかったでしょうが、現在では、文字コードが決め打ちのファイルも考えられるので、適切ではありません。
外部エンコードが指定されていないときは、バイナリファイルと見なすようにします。