Ruby-GetText-Package開発手順
ruby-gettext-howto
本ライブラリを使用した開発手順を簡単に説明します。 application_rootがあなたのアプリケーションのルートディレクトリとします。
まずはスクリプトを作る
例えば、以下のように本ライブラリを用いたスクリプトを作ります。hello.rbと言う名前で保存します。
require 'gettext'
class Test
include GetText
bindtextdomain("hello")
def say_hello
puts _("Hello World")
end
end
Test.new.say_hello
文字列の抽出を行う(poファイルを作る)
次にrgettextを使って文字列を抽出します。これをapplication_root/po/に置きます。
$rgettext hello.rb -o po/hello.pot
rgettextは複数ファイルを指定できます。
$rgettext *.rb -o po/hello.pot
hello.potファイルの先頭部分はそのプロジェクトにあったコメントを追加しておくと良いでしょう。
# HelloWorld Sample Program ← これ # Copyright (C) 2005 Masao Mutoh ← これ # This file is distributed under the same license as the Ruby-GetText package. ← これ # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. ← これは翻訳者が書くからそのまま # msgid "" msgstr "" "Project-Id-Version: helloworld 1.0\n" ← これ
なお、rgettextはコマンドだけではなく関数としてRubyスクリプトから呼び出すこともできます。さらに、pot/poファイルをメンテナンスする関数として、GetText.update_pofilesという関数もあります(内部でrgettextとGNU gettext付属ツールであるmsgmergeを呼び出します)。rakeと組み合わせるとpot/poファイルのメンテナンスがとても簡単になりますのでぜひご利用ください。 詳しくはpo/moファイルのメンテナンス方法(英語)を参照してください。
ローカライズする
hello.potから他の言語ファイルを作ります(この例は日本語ファイルを作る場合です)。
$cp hello.pot hello.po
GNU GetTextがインストールされている環境では単にcpコマンドを使うかわりに、msginitを使うとヘッダ部分をある程度ローカライズしてくれるので便利です。
$ mkdir -p po/ja $ LANG=ja_JP msginit -i hello.pot -o po/ja/hello.po
このファイルはapplication_root/po/ja/hello.poに配置します。もちろん他の言語であればapplication_root/po/#{lang}/hello.poと、langの方を適宜置き換えておいてください。
次に、hello.poを編集します。以下のような感じ。
"Project-Id-Version: helloworld 1.0\n" "POT-Creation-Date: 2004-03-25 23:27+0900\n" "PO-Revision-Date: 2005-08-05 14:39+0900\n" "Last-Translator: Masao Mutoh <mutoh@highway.ne.jp>\n" "Language-Team: Japanese\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" #: ../hello.rb:7 msgid "Hello World\n" msgstr "こんにちわ、世界\n"
それから、ヘッダ部分には翻訳した人・言語・時間などの情報を書きます。Content-Type行, Plural-Forms行は必須です。Content-Type行にはhello.poファイル自体の文字コードを記述します。EUC-JP, Shift_JIS, UTF-8等、hello.poファイル自身と同じ文字コードを指定します。 Plural-Formsは複数形の指定になります。n_, ngettextで使用します。msginitの結果をそのまま使えばよいでしょう。
なお、poファイルのメンテナンスにはmsginitの他にも、GNU gettext付属のツール(msgmerge等)を使うことができます。 #時間があったらこの辺も書きましょう。つーか誰か書いてください(^^;)。
一応、samplesディレクトリにいくつかサンプルを入れておきますんでそちらも参考にしてください。
パラメータの使い方
Ruby-GetText-Packageで文字列に変数を使う場合はフォーマット文字列を使います。
_("%s was not found") % "foo.rb"
_("%s and %s is not same file") % ["foo.rb", "bar.rb"]
_("%s is %d byte") % ["foo.rb", 100]
フォーマット文字列の記法についてはsprintfフォーマットを参照してください。
また、Ruby-GetText-PackageではString#%を拡張し、以下のように名前付き変数名を指定できます。
_("%{filename} was not found") % {:filename => "foo.rb"}
_("%{filename1} and %{filename2} is not same file") % {:filename1 => "foo.rb",
:filename2 => "bar.rb"}
_("%{filename} is %{filesize} byte") % {:filename => "foo.rb",
:filesize => 100}
翻訳者がpotファイルを見て翻訳をする場合、前者の記法を用いると、それぞれの%s, %dが何を示すのか、poファイルだけではなく、ソースコードを読まないとわからないということが多くあります。
後者の記述は多少記述が面倒くさいですが、それが何を意味しているのかが前者の記法よりもわかりやすいので、後者を使うようにしましょう。 また、後者の例ではパラメータの順序を翻訳者が容易に変更できる点でも便利です。
式展開について
Ruby-GetText-Packageでは、"#{...}"が評価される順番の関係で、_("#{filename} was not found")という記法を用いることができません。
そのため、式展開を使った場合は、rgettextなどの文字列抽出の対象にもなりませんので注意してください。
GetText.n_(msgid, msgid_plural, n)
日本語は比較的単数形と複数形を意識しないのですが、文法上、これらを明確に分けたい言語があり、GetText.n_()はそのようなメッセージを扱いたい場合に使います。 以下に例を示します。
require 'gettext'
class Test
include GetText
bindtextdomain("plural")
def show
(0..10).each do |n|
puts n_("%{num} file was removed", "%{num} files were removed", n) % {:num => n}
end
end
Test.new.show
若干冗長に感じるかもしれませんが、GetText.n_()の第3引数の数値と、printfに渡すnは別に記述します。 #これは%s等が一緒に文字列に入る場合を想定しているのでこのような形になっています。
上記の言語別実行結果がどう表示されるかというのはpoファイルのPlural-Forms行に依存します。 以下にpoファイルの抜粋を示します。
"Plural-Forms: nplurals=2; plural=(n != 1);\n" #: plural.rb:11 msgid "%d file was removed" msgid_plural "%d files were removed" msgstr[0] "ファイルが削除されました" msgstr[1] "ファイルが%d個削除されました"
npluralsがmsgstr[]の数(この場合は2)、plural=の右辺が条件式です。true/falseかあるいは該当するmsgstrの番号を返すような条件式を書きます。この場合はnが1の時はmsgstr[0]、それ以外の場合はmsgstr[1]が使われます。 ...日本語で説明するとあまりありがたみを感じられませんね。実は、日本語の場合はあまり複数形を使うような場面は無いので、以下のようにしてしまう場合が多いようです。
"Plural-Forms: nplurals=1; plural=0;\n" #: plural.rb:11 msgid "%d file was removed" msgid_plural "%d files were removed" msgstr[0] "ファイルが%d個削除されました"
この場合は常に同じメッセージが表示されます。
英語などでは最初の例のように
Plural-Forms: nplurals=2; plural=n != 1;
という指定にして、1のときのみ単数形、0と2以上は複数形として扱います。 moファイルが存在しない場合にはこのルールが適用されます。
さらに言語によっていろいろな条件があるのですが、これが4つにも分かれる言語もあります。Slovenianを例にあげます。
"Plural-Forms: nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3;" #: plural.rb:11 msgid "%d file was removed" msgid_plural "%d files were removed" msgstr[0] "...." msgstr[1] "...." msgstr[2] "...." msgstr[3] "...."
メッセージ部分は何を入れて良いのかさっぱりなのでごまかしました。 にしても、このように翻訳していくのは大変ですね。日本人に生まれてよかったかも(^^;)。
なお、GNU gettextのドキュメントにさらに詳細な説明があります。英語と日本語以外にも興味がある方はそちらを参照してください。
GetText.N_(msgid)
GetText.N_(msgid)はmsgidをローカライズせずにそのまま返します。 これは、rgettextがソースからローカライズ対象の文字列を抜き出すために必要とされます。 以下に例を示します。
require 'gettext'
module Test
include GetText
bindtextdomain("hello_noop")
MSGS = [N_("Hello World"), N_("Hello World2")]
module_function
def show
MSGS.each do |msg|
puts _(msg)
end
end
end
Test.show
GetText.s_(msgid, div = "|")
デフォルトで表示する文字列(つまりmsgidそのもの)は同じだけど、翻訳した場合に意味が変わることが容易に想定できる場合に使います。GUIのメニュー等で使うことがあるかもしれません。
print s_("File|Open\n") #(1)
print s_("File|Printer|Open\n") #(2)
上記例で、翻訳文字列が無い場合、あるいはCロケールの場合、(1), (2)のどちらも "Open"というメッセージが出力されます。 つまり、一番最後のdiv(この場合は"|")より右側が表示されます。このdivは第2引数で任意に指定できます。
GetText.ns_(msgid, msgid_plural, n, div = "|")
Since 1.10.0 s_()とn_()の合成バージョンです。
puts ns_("File|There is an item", "There are %{num} items", n) % {:num => n} #(1)
puts ns_("Printer|There is an item\n", "There are %{num} items", n) % {:num => n} #(2)
どちらも英語の場合は、"There is an item", "There are %{num} items"に展開されますが、msgidとしては別のものとしてpoファイルに出力されませす。
なお、この"Foo|"、"Printer|"は単数の方にだけ記述するようにします。
GetText.p_(msgctxt, msgid), GetText.pn_(msgctxt, msgid, msgid_plural)
Since 1.93.0 GetText.s_()に似ていますが、msgctxt(メッセージコンテクスト)を使えば、より明確にmsgidをカテゴリ分けすることができます。
puts s_("File|Open")
puts s_("Printer|Open")
puts ns_("File|There is an item", "There are %{num} items", n) % {:num => n}
puts ns_("Printer|There is an item", "There are %{num} items", n) % {:num => n}
上記は以下のように記述できます。
puts p_("File", "Open")
puts p_("Printer", "Open")
puts np_("File", "There is an item", "There are %{num} items", n) % {:num => n}
puts np_("Printer", "There is an item", "There are %{num} items", n) % {:num => n}
この場合、”File"と"Printer"がそれぞれmsgctxtとなります。msgctxt自体は翻訳対象にはなりません。
#: foo.rb:1
msgctxt "File"
msgid "Open"
msgstr ""
#: foo.rb:2
msgctxt "Printer"
msgid "Open"
msgstr ""
#: foo.rb:4
msgctxt "File"
msgid "There is an item"
msgid_plural "There are %{num} items"
msgstr[0] ""
msgstr[1] ""
#: foo.rb:5
msgctxt "Printer"
msgid "There is an item\n"
msgid_plural "There are %{num} items"
msgstr[0] ""
msgstr[1] ""
fuzzy表示について
poファイルにfuzzyと表示されている場合、それを取り除かないとローカライズの対象になりません。
#: hello.rb:7 #, fuzzy msgid "Hello World" msgstr "こんにちわ、世界"
この場合は内容を確認した上で、fuzzy行を削除してください。
#: hello.rb:7 msgid "Hello World" msgstr "こんにちわ、世界"
翻訳者向けのコメント
Since 2.1.0 msgidだけだと一見して意味がわかりづらいようなメッセージなどに翻訳者向けのコメントを残すことができます。
-- foo.rb
def foo
# TRANSLATORS: "file" is xxxxxx.
# "lang" is .....
_("%{file} %{lang} ...") % {:file => file, :lang => lang}
end
上記は以下のようにpoファイルに抽出されます。
-- foo.pot #. "file" is xxxxxx. #. "lang" is ..... #: foo.rb:4 msgid "foo" msgstr ""
moファイルを作る
最後にmoファイルを作り、これを適切なディレクトリへ置きます。 上記例ではbindtextdomain()でmoファイルを置くディレクトリを指定していないため、/usr/share/locale/#{lang}/LC_MESSAGES/か/usr/local/share/locale/#{lang}/LC_MESSAGES/へ置く必要があります。 ここでは、/usr/local/share/locale/ja/LC_MESSAGES/へ置いてみます。
$rmsgfmt ja.po -o /usr/local/share/locale/ja/LC_MESSAGES/ ※ rmsgfmtの代わりにGNU gettext付属のmsgfmtコマンドを使うこともできます。
上記のままコマンドを実行する場合はroot権限が必要ですし、いきなり開発中のものをそこにインストールすることはないでしょう。
特に、setup.rbやrubygemsをインストーラとして使う場合には、application_root/data/localeに置くと便利です。毎回、rmsgfmtを呼び出したりするのも面倒くさいので以下の2つの方法を紹介します。
なお、po/moファイルのメンテナンス方法(英語)で、この辺の情報をまとめて書きましたので、そちらもご一読ください。情報もそちらの方が最新です。
rakeを使う
Since 2.1.0 rakeのRakefileに以下のような記述を追加します。
desc "Create mo-files for L10n" task :makemo do require 'gettext/tools' GetText.create_mofiles # 個別にディレクトリ指定する場合はオプションで指定します(APIリファレンス参照) end
そして、以下のように実行します。
$ rake makemo
これで、application_root/po/#{lang}/*poファイルを全てapplication_root/data/locale/配下に適切に配置してくれます。
setup.rb
Since 2.1.0 setup.rbを使う場合は、pre-setup.rbに以下のように記述します。
require 'gettext/tools' GetText.create_mofiles
そして、以下のように実行します。
$ ruby setup.rb config #一度目のみ $ ruby setup.rb setup
GETTEXT_PATH
開発中は、環境変数GETTEXT_PATHを指定してください。強制的に参照先を変えることができます。
$export GETTEXT_PATH="./data/locale"
実行する
$ruby hello.rb
日本語が表示されましたか? おめでとうございます、成功です。
もし表示されないようでしたら、まずは、-d付きで実行してみましょう。
$ruby -d hello.rb Search path:["./data/locale/%s/LC_MESSAGES/%s.mo", "./data/locale/%s/%s.mo"] locale:"ja_JP.eucJP"
MO file is not found in
./data/locale/ja_JP.UTF-8/LC_MESSAGES/hello.mo
./data/locale/ja_JP/LC_MESSAGES/hello.mo
./data/locale/ja/LC_MESSAGES/hello.mo
./data/locale/ja_JP.UTF-8/hello.mo
./data/locale/ja_JP/hello.mo
./data/locale/ja/hello.mo
もう一度、上記pathのいずれかにきちんとhello.moがあることを確認してください。
#これでわかんなかった場合は....どうしましょう(^^;)
アプリケーションのインストール
アプリケーションのインストールにはsetup.rbやrubygemsを使うことをオススメします。
setup.rbの場合
moファイルはdata/locale配下が/usr/share/locale/, /usr/local/share/locale/辺りにコピーされます。 ここにコピーされると後は何も設定せずに適切な言語に変換されて表示されます。
rubygemsの場合
rubygemsの場合は、moファイル化後にファイルを配布する必要があるので注意してください。 #moファイルは環境非依存ですので事前にmo化しても問題ありません。 配布対象として、localeディレクトリを含めることを忘れないようにしてください。
$ gem install helloworld
とするとアプリケーションは、/usr/lib/ruby/gems/1.8/gems/helloworld-1.0.0/配下にコピーされます。 moファイルは/usr/lib/ruby/gems/1.8/gems/helloworld-1.0.0/data/locale/配下にコピーされます。
実は、rubygemsを使った環境ではRuby-GetText-Packageはmoファイルの検索対象として、/usr/lib/ruby/gems/1.8/gems/#{app}/data/locale/も検索するようになるので、後は何も設定せずに適切な言語に変換されて表示されます。
キーワード:
参照:[Ruby-GetText-Package開発者向けドキュメント] [Ruby-GetText-Package]