Ruby言語やLinuxのネタが多いです。
September 13, 2006 [おもひで]
■ [Ruby] rails edgeでRuby-GetTextが動かない件(3)
ActiveRecordでsave!をするとSystemStackErrorするというレポートが。うーん、確かに再現する・・・。どうもvalidates_系のメソッドが変な風にループしている模様。
ということで、やっぱりまだ動かないみたいです・・・。しくしく。
■ [Ruby] Ruby-GetText-Package-1.8.0の変更点
気を取り直して、Ruby-GetText-1.8.0の変更点を紹介します。長文失礼。
#後で見直して保管庫に移す予定。
(一般)(Rails)に分けて記述します。(Rails)がRails向け、(一般)は一般的なものです。
■ (1) GetText.bindtextdomain_to(klass, domain) (一般)
例えば、RailsでActiveRecordを使わないんだけど便利なモデルクラス(FooModel)、というのがプラグインで提供されていたとします。プラグインの方には手を加えずにFooModelを継承する全てのクラスで"myapp"ドメインを適用したい場合は以下のようにします。
app/controllers/application.rb class ApplicationController < ActionController::Base bindtextdomain_to(FooModel, "myapp") init_gettext "myapp" end
app/models/user.rb
class User < FooModel
def foo
_("user name")
end
end
これの利点は、各サブクラス内でGetText向けの設定(include GetTextとか、bindtextdomain)を一切せずにGetTextのメソッドを使えることです。
後はupdatepoタスクでこのモデルを指定すると(通常、ワイルドカードでModel配下を指定していると思いますので意識する必要は無いかも)"user name"がmsgidとなってmyapp.poに反映されるのでそれを翻訳します。
■ (2) init_gettextで指定したドメイン名をActiveRecord::Validationsにバインドするようになった (Rails)
これは(1)に似ているのですが、例えば、ActiveFormはActiveRecord::Validationsをincludeしているため、そのままmyappドメインがバインドされることになります。したがって、特に何の指定もせずに以下のようにGetTextのメソッドが使えるようになります。
class Search < ActiveForm
attr_accessor :foo
N_("Search|Foo")
validates_length_of :foo, :minimum => 10
end
ActiveForm以外でもActiveRecord::Validationsをincludeしているプラグイン等は同様に扱うことができます。なお、この機能を単体で利用することを想定して、ActiveRecord周りのコードをgettext/rails.rbからgettext/active_record.rbに移しました。
■ (3) init_gettextでのロケール指定パスの変更 (Rails)
今まで、init_gettextはデフォルトで{RAILS_ROOT}/locale/をロケールパスとしていたのですが、init_gettextをfoo/app/controllers/bar_controller.rbの中で呼び出している場合、foo/localeがロケールパスになるようになりました。
これは主にRails Enginesで使用することを想定しています。例えばUserEnginesでは以下のようなファイル構成になります。
{RAILS_ROOT}/vendor/plugins/login_engine
+app/controllers/user_controller.rb
+po/
+locale/
+Rakefile
+その他
上記のuser_controller.rbは以下のように書きます。
class UserController < ApplicationController
init_gettext "login_engine"
:
:
def home
:
:
@fullname = _("Not logged in...")
Rakefileはちょっとだけ工夫が必要です。
desc "Create mo-files for L10n"
task :makemo do
GetText.create_mofiles(true, "po", "locale")
end
$: << "../../../"
GetText::ActiveRecordParser.init(:db_yml => "../../../config/database.yml")
desc "Update pot/po files to match new version."
task :updatepo do
GetText.update_pofiles("login_engine", Dir.glob("{app,lib}/**/*.{rb,rhtml}"),
"login_engine x.x.x")
end
UserEnginesではActiveRecordを使うのですが、database.ymlはアプリケーションと同じモノを使うため、それを指定する必要があります。ここはもうちょっと改善の余地があるかな。
いずれにせよ、上記のようにEnginesでは、通常のRailsアプリケーションとほとんど同様にGetText化することができます。
なお、上記を元にしたUserEnginesはもうちょっとモディファイしてEnginesのメーリングリストにポストする予定です。実はUserEnginesはローカライズ要望が多く、私のところにどう実装すれば良いの?という質問が結構きています。いちいち回答するのが面倒くさくなってきているので、今後はこれを参考にして、と言えて楽になるかなぁとちょっと思ってます。
■ (4) beforge/after_init_gettextの追加 (Rails)
before/after_filterでも大丈夫な場合が多いのですが、こちらを使うとユーザのリクエスト毎に発生するGetTextの初期化処理(ロケールの取得と使用するTextdomainの選択)を行う前後に処理を追加することができます。
例えば、QUERY_STRINGの"foo"パラメータにロケールが指定されている場合はそれを使い、指定されていない場合は日本語を使う、というような場合は以下のようにします。
class ApplicationController < ActionController::Base
before_init_gettext :foo
def foo
GetText.locale = cgi.params["foo"] ? "ja" : cgi.params["foo"]
end
init_gettext "myapp"
DBアクセスやセッションにあるユーザ情報から、使用するロケールを取得する、というような場合にこの機能を使うことができると思います。
after_init_gettextはロケールが決まった後に呼び出されます。他のローカライズライブラリを併用するときにそのライブラリにロケール情報を渡す時に使うことを想定しています。
class ApplicationController < ActionController::Base
after_init_gettext :ya_l10n
def ya_l10n
YetAnotherL10n.set_locale(GetText.locale)
end
init_gettext "myapp"
■ (5) オプションパーサを指定することができるようになった (一般)
poファイルでメッセージを抽出するためのパーサを独自に追加できるようになりました。パーサモジュールの書き方ですが、まず、target?, parseという2つのメソッドを実装し、パーサをGetText::RGetTextに登録する、という感じです。こんな感じ。これをRakefileで使う場合は、require 'testparser'としてからupdatepoタスクを呼び出せば良いでしょう。
■ (6) rgettextに-d, -rオプションを追加した
-dはデバッグオプション、-rで先読みしたいライブラリを指定します。
前述のオプションパーサのメールでも触れていますが、-rは主にオプションパーサを指定することを想定しています。
$ rgettext -r testparser foo.rb -o foo.pot
