さて、今度はLocale::MaketextとLocale::Maketext::Lexicon。

Locale::Maketextは基本操作は以下のようなイメージ。get_handle() というメソッドでローカリゼーション用のオブジェクトを引っ張り出し、それに対してmaketext() を呼び出して変換を行う。
use MyApp::I18N; my $handle = MyApp::I18N->get_handle('ja'); print $handle->maketext("Hello, World!"), "\n";

get_handle() になにげに言語IDが渡されているのもポイント。引数無しでget_handle() を呼び出すと、前出のI18N::LangTag::Detectのコードとかが自動的に呼ばれる。もしこの辺りを自分でコントロールする必要がないと感じるのであれば、引数無しでもよいかもしれない。

話は戻って、MyApp::I18Nを設定するのに、Locale::Maketextを継承した国際化用のモジュールを作る必要がある。
package MyApp::I18N; use strict; use base qw(Locale::Maketext); 1;

国際化ルール自体は、上記MyApp::I18N名前空間以下に置かれる。Locale::Maketextは基本的に.poファイルや.moファイルには対応しないので、直接ハッシュに指定する形になる。
package MyApp::I18N::ja; use strict; use base qw(MyApp::I18N); our %Lexicon = ( "Hello, World!" => "みなさん、こんにちは!" ); 1;

この名前空間が前もって固定されている、というところに「後付で(プラグイン等から)国際化ルールを追加できない」という制限が出てくる。もちろん、%Lexiconハッシュに直接ぶっこめばいいんだけれども・・・それ用のコード書くのは微妙じゃないか?というわけでなんとなく自分はこのアプローチはよくない気がする。

Locale::Maketext::LexiconはLocale::Maketextから継承とかなんもしないくせに、Locale::Maketextを拡張している。意図はいいんだけど、APIデザインとかに全くセンスを感じられないモジュール。こちらは上記Locale::Maketextを使うときと同じくMyApp::I18Nの設定はするのだけれども、この際に.poファイルや.moファイルの扱いができるようになる。要はLocale::Maketext::Lexiconはそれらのファイルの中味を解析して、%MyApp::I18N::xxx::Lexiconにぶちこんでくれるわけです。
package MyApp::I18N; use strict; use base qw(Locale::Maketext); use Locale::Maketext::Lexicon { '*' => [ 'Gettext' => '/path/to/localization/*.po' ], # ファイル名じゃなくてパス中にen/jaとかがはいるなら、以下のようにする: # '*' => [ 'Gettext' => '/path/to/localization/*/data.po' ], _decode => 1 }; 1;

上記の場合全ての言語(「*」という表記)の国際化データは、gettext形式(.poファイル)で/path/to/localization/ディレクトリ以下の、それぞれの言語IDの名前の付いたファイルに格納されている、と表記してある。Locale::Maketext::Lexiconの場合はあとは.poファイルを作るだけ。

ちなみにAPIは最悪なのですが、Locale::Maketext::Lexiconは後からPerl形式以外の国際化データを使えるというのと、それらを後から追加できるというのが最大のメリット。
eval "package MyApp::I18N; Locale::Maketext::Lexicon->import( { ja => [ Gettext => '/path/to/another/localization/ja.po' ] _decode => 1 }); "

とかすると、新しい日本語データを追加することができる。何回も言うけどAPIは最悪。パッケージ名を指定しなくちゃいけないとか、本当にアホだけど、しないとできない。

あとカバーしてないのは_AUTOとかかな。以下次号。