2012-07-11

SKK for ChromeOSの開発:辞書データの話


前回書いたように、ChromeOSで動作するSKKを開発しました。当初の予定を変更して、今回は辞書の話を書くことにします。

実は私は昔、AJAX-SKKというものを書いてみたことがあります。とはいえ、JavaScriptの練習用コードだったということもあっていろいろ不備があるわけですが、何といっても変換には、適当なcgiをでっち上げて変換サーバにしていました。今回は、そういうことはやめようと決意し、全部JavaScriptで動くことを目指しました。

SKKの辞書には、バリエーションがいくらかありますが、最大のL辞書は相当大きく、これをそのまま扱うのは簡単ではないように思えました。そこで当初の構想としては、処理にwebworkerを使うだとか、保存にはIndexedDBを使うだとか、そういう現代的なテクノロジーを活用することを目論んでいました。
目論んでいたんですが、結果的にはこういうのを使うのはやめてしまいました。実際に、そういうコードを書き初めていたのですが、完成前にふと思いついてものすごく単純な手法を試してみたところ、あっさり動作してしまったしパフォーマンス上もたいして問題ではないように見えたので、それで行くことにしました。
その手法というのは、SKKの辞書データを単純にJavaScriptオブジェクトに変換してしまい、全部オンメモリに持つ、というものです。また、JavaScriptオブジェクトはJSON.stringifyでJSONフォーマットにシリアライズし、FileSystem APIを使ってローカルなファイルに保存します。次回以降は、ローカルなファイルを読んでJSON.parseするだけで辞書データが復元できます。
SKK-JISYO.Lをダウンロードしてパーズと保存を全部組み合わせた場合、手元の最新のChromebook (Series-5)で試すと、数秒で処理が完了してしまいます(しかもほとんどの遅延は辞書のダウンロード時間な気がします)。JSON.parseでローカルから復旧する場合は1秒強、といったところでした。辞書データは、バックグラウンドページがロードされたとき(つまりログイン時)に一度だけロードすればよいので、この程度の性能なら特に問題ないように思います。ログイン時のもろもろの処理に紛れてしまうんじゃないでしょうか。
むかし、SCIM-SKKを開発したときは、SKK-JISYO.L全体をはじめにパーズすると無視できない遅延が考えられるため、いろいろ効率化を工夫した記憶があるのですが、ああいう細かい工夫はいったいなんだったのかなぁという思いの去来する出来事でした。最近のコンピュータも、最近のJSエンジンも、速いよね……。


さて、それなのにソースコードには「server」の文字が含まれます。これは何をしているのかというと、辞書ファイルのミラーリングと、簡単な前処理です。
SKKは歴史があるので、辞書の配信もいささか時代がかった方式になっています。ファイル名自体が.gzの拡張子を持っていて、Content-Type: x-gzipのレスポンスヘッダがあります。コンテント自体を圧縮して配信するなら、Content-Encodingを使うのがHTTP的には正しかろう、とは思いますがブラウザのバグだとか歴史的な事情を考えると、この方式はむしろ自然だと言えるでしょう。ただ、自然だといっても、このままではJavaScriptで.gzを伸長するはめになります。これは今ひとつ現実的ではない気がします。
それに、SKK辞書はEUC-JPでエンコードされているという問題があります。.gzを伸長しても得られるのはEUC文字列ですから、Unicodeに変換しなければならない。これをJSでやるのはさすがにアホくさい。
そういった事情があり、AppEngine側でSKKの辞書のミラーリングをすると同時に、もうちょっと楽に扱えるように前処理を施します。
AppEngineではscheduled taskで1日に1回、openlabの辞書の配信元に問い合わせます(現在はopenlabが停止しているのでスケジュールも止めています)。で、更新のあった辞書ファイルをダウンロードしてきたらzlibで伸長し、そのデータをblobstoreに保存します。
拡張機能のほうからデータを持ってくるときには、単にサーバに辞書ファイルを問い合わせます。すると、blobstoreに保存してあるデータをContent-Type: text/plain; charset=euc-jpで返します。文字コードの変換はChromeがやってくれるので、JSレベルでは気づかぬうちに扱いやすい普通のテキスト形式でデータが手に入る、というわけです。

ちなみに、辞書ファイルは誰でも取ってこれます。たとえば http://skk-dict-mirror.appspot.com/SKK-JISYO.S.gz にアクセスしてみてください。openlabが停止している今では、偶然ですがキャッシュとして使えるかもしれません。


ところで、この話を会社でしたところ、そんなものはXHRでなんとかなるのではないかという指摘を受けました。あんまり詳しくなかったのですが、XHRでは返ってきたレスポンスのContent-Typeを上書きすることはできます。なのでContent-Typeだけならそれでもいいのですが、今回の場合はContent-Encoding: gzipも足してあげないとChromeはレスポンスボディを解釈できません。XHRではほかのレスポンスヘッダはいじることができないようなので、これだけでは無理なのではないかと思いました。ただ、Chrome拡張機能のwebRequest APIを使えばレスポンスヘッダをいじれるため、組み合わせればAppEngineがなくても良かったかもしれません。気づいてからずっとopenlabが落ちているのでまだ試していないのですが、復旧したら試してみるのもいいかもしれませんね。

image: http://www.flickr.com/photos/62396887@N00/1405476175/