2008-05-13

Springの lang:jrubyで日本語を通す

Spring Framerowkと JRubyを組み合わせると、Springの Beanを Rubyで実装することが出来る。
が、Bean定義ファイルにインラインで記述するにしろソースファイルに記述するにしろ、スクリプト中の文字列リテラルに日本語が含まれていると化けてしまう。仕方ないので Springと JRubyのソースを追ってみた。

まず結論として、Springの JRuby連携部分に対するその場しのぎのパッチを示す。
org/springframework/scripting/jruby/JRubyScriptFactory.java をこんな風に書き換えると、とりあえず日本語が通る。

96a97,98
>String iso8859_1edString =
> new String(scriptSource.getScriptAsString().getBytes("UTF-8"), "ISO-8859-1");
98c100
< scriptSource.getScriptAsString(), actualInterfaces, this.beanClassLoader);
---
> iso8859_1edString, actualInterfaces, this.beanClassLoader);

デバッガを使って文字の化ける箇所を特定→charを惜しげもなく byte へキャストしている箇所発見(日本語を含む文字列リテラルが化ける直接の原因)→なぜそうなっているか大体想像がついたので JRubyの mainメソッドからスクリプトのロードが行われる箇所までを追跡→やはりコマンドラインからの通常起動では ISO-8859-1固定でスクリプトファイルを読み込んでいる

つまりJRuby1.0 の Lexarは、スクリプトがどんなエンコーディングで記述されていようともそれを一旦 ISO-8859-1文字列としてパースすることを前提に実装されている。
「コマンドラインや$KCODEで文字コードの指定が出来るじゃないか!」と思うかもしれないが、指定された文字コードによって文字列の扱い方が影響を受けるのは、構文解析が終わった後である。通常のI18Nアプローチでは考えにくいことだが、そうしないともともとI18Nに対応していない(文字列とバイトストリームの区別がない) MRI(CRuby) 1.8との互換性を取ることが困難なのだろう。

上記の理由により、スクリプトを Correctly I18N'edな文字列として JRubyパーサに渡すと逆に文字化けが起こってしまう。だが、Springはそのようなことをお構いなしに「正しいエンコーディングで読み込まれた」スクリプト文字列を JRubyのパーサに渡してしまう。
上のパッチは、一度正しく読み込まれたI18N'edな文字列を UTF-8表現のバイトストリームにバラし、それをわざわざISO8859-1文字列であると偽って Rubyパーサに渡すためのクイックハックである。

・・・っていうか、Springで JRubyが使えるようになってからしばらく経つと思うんだけど、この問題についての情報を検索で見つけることができなかった。日本人は誰もこの機能使ってないの?

ラベル: ,

2 件のコメント:

Blogger Yasushi さんは書きました...

まずCRubyもJRubyも-Kでエンコーディング指定すると、スクリプトソースもそのエンコーディングで読むよ。

で、springを確認したら、JRubyに-Kオプションどころか何も渡してない、つまり-KN状態なので8859_1でパースされてる模様。

org.springframework.scripting.jruby.JRubyScriptUtils.initializeRuntime()あたり。
-Ku固定で渡すようなパッチをspringに送ってもらえると、助かるなw

2008/05/21 17:20  
Blogger shimarin さんは書きました...

いんや。JRubyのソースをMainからおっかけてごらん。
-Kに関係なくISO-8859-1で読んで構文解析にかけてるよ。
将来のRubyからはこうじゃなくなる・・・みたいなコメントも書いてある。
って、JRuby 1.0.3しか読んでないんで1.1はどうかしらないけど。

2008/05/22 8:41  

コメントを投稿

<< ホーム