Catalyst::Plugin::I18Nで国際化 with Template-Toolkit(Catalyst::View::TT)

前回のCatalyst+Template-Toolkit(Catalyst::View::TT)で国際化 - ヒルズで働く@robarioの技ログCatalyst::Plugin::I18Nでできるよとのコメントをいただきましたので、それでやることにしました。

Catalyst::Plugin::I18Nで国際化

lib/MyApp/L10Nではなく lib/MyApp/I18N を用意します。

lib/MyApp/I18N/en.pm
package MyApp::I18N::en;
use base qw(MyApp::I18N);
our %Lexicon = (
    '_AUTO' => 1,
);
lib/MyApp/I18N/ja.pm
package MyApp::I18::ja;
use base qw(MyApp::I18N);
our %Lexicon = (
    '_AUTO' => 1,
    'Hello' => 'こんにちわ',
);

MyApp::setup('I18N')かuse Catalyst 'I18N'で、Catalyst::Plugin::I18Nを読み込んでおきます。

これでアクション内で

$c->localize('Hello');

とすれば「こんにちわ」が得られます。非常に簡単ですね。

Template-Toolkitで使う

これをテンプレートで利用するには

[% c.localize('Hello') %]

のように書くらしいです。そのまんま過ぎます。さすがに長ったらしいので、MACROを定義する方法がPODに書いてあります。

[% MACRO l(text, args) BLOCK;
   c.localize(text, args);
END; %]

[% l('Hello') %]

ただ[% l('Hello') %]でも十分長ったらしいので、やはりここは_('Hello')のように書きたいですよね。

xgettext

ところで話は変わって、Catalyst::Plugin::I18NGNU gettextのpoやmoを直接扱うことができます。
Locale-Maketext-Lexicon-1.00 - Use other catalog formats in Maketext - metacpan.orgというディストリビューションにxgettext.plというツールが付いてきます。これは翻訳対象文字列を抽出してpoファイルを生成してくれるツールです。
抽出対象文字列はLocale::Maketext::Extract - Extract translatable strings from source - metacpan.orgに書かれている、以下の全てです。

Perl関数呼び出し形式
  • translate(...)
  • maketext(...)
  • gettext(...)
  • loc(...)
  • x(...)
  • _(...)
  • __(...)
HTML::Mason風
  • <&|/l&>...
  • <&|/loc&>...

(ここドキュメントが間違ってますのでご注意)

Template Toolkit
  • [% |l %]...[% END %]
  • [% |loc %]...[% END %]
Text::Template風
  • STARTxxx ... ENDxxx
一般Template

{{...}}

これらは全てが抽出の対象となるため、例えばTemplate-Toolkit用のテンプレート内で<&|/l&>...と書いても抽出されます。で、_(...)か{{...}}が一番簡単っぽいのでそれらを対象とすることします。


テンプレートroot/defaultを

<html><body>
<p>{{Hello}}<p>
<p>_('World')<p>
</html></body>

としておきます。

% xgettext.pl -o ja.po root/default

とすると、ja.poの中身は

# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"

#: root/default:2
msgid "Hello"
msgstr ""

#: root/default:3
msgid "World"
msgstr ""

となります。msgstrのところに「こんにちわ」「世界」を書き込み、CHARSETをutf-8に変更し、「#, fuzzy」行を削除します。
poの編集に関しては他のサイトを参照してください。

Template-Toolkitで簡単に使う

出来上がったpoファイルを lib/MyApp/I18N/ja.po として置いておきます。(msgfmtでja.moに変換しても構いません)
テンプレートへの記述を[% c.localize(...) %]という関数呼び出しから{{...}},_(...)という記述に変えたので、このままでは翻訳されません。ですので、ビューにlocalizeを呼び出すコードを追加します。

package MyApp::View::TT;
use base qw(Catalyst::View::TT);

sub process {
    my ( $self, $c ) = @_;

    $self->SUPER::process($c);

    my $output = $c->res->body;
    $output =~ s/{{(.+?)}}/$c->localize($1)/egmsx;
    $output =~ s/_\((["'])(.+?)\1\)/$c->localize($2)/egmsx;
    $c->res->body($output);
    return;
}

1;

これでroot/defaultを表示させると

<html><body>
<p>こんにちわ<p>
<p>世界<p>
</html></body>

という出力が得られます。



ところで、今回はlanguageの指定をしていません。これはCatalyst::Plugin::I18Nが$c->request->header('Accept-Language')を見て勝手に設定してくれているからです。実際の判定はI18N::LangTags::Detectが行なっています。