Catalyst + Xslate + I18N::Handle で国際化対応してみる

CatalystだとCatalyst::Plugin::I18Nを使うのが楽みたいですが、
Xslateだと使えないようなので
Text::Xslateのドキュメントを参考に自前で設定してみました。
(ちなみに今回の目的は国際化ではなく、ラベル等をファイル管理したかっただけという...)


必要なモジュール(CatalystとText::Xslateは除外)

I18N::Handle
App::I18N
Catalyst::View::Xslate

とりあえずの手順

catalyst.pl でアプリケーション生成

catalyst.pl PoDemo とか。

po プロジェクト?の初期設定

App::I18N をインストールすると「po」というコマンドが使えるようになります。
poプロジェクトを作ってみましょう。

po parse

このコマンドで作成されるファイル*1が雛形になるようなので
CHARSETなどを設定しておきます。


次に使用したい言語のファイルも作っておきます。

po lang ja
po lang en

とりあえずここまで。
あとは普通にコーディングします。



コード抜粋

PoDemo::View::Xslate

I18N::Handleをuseすると「_」というlocファンクションが使えるようになるので、
それをXslateのファンクションとして定義します。

# po
use I18N::Handle;
has '+function' => (
    default => sub {
        +{
            _ => \&_,     #i18n::method
        }
    }
);


何もかんがえずにjaのpoを使いたいときはこんな感じ。
poファイルのパスはコンフィグファイルに書いておきます

my $hl = I18N::Handle->new(
    Gettext => {
        en => PoDemo->config->{en_po_path},
        ja => PoDemo->config->{ja_po_path},
    }
)->accept( qw(en ja) );
$hl->speak( 'ja' );


ヘッダの言語情報から自動判定させたいときはこんな感じ?
Catalyst::Plugin::I18N のパクリですが!

use I18N::LangTags ();
use I18N::LangTags::Detect;
use I18N::LangTags::List;
override 'process' => sub {
    my ($self, $c) = @_;
        my $languages ||= [
            I18N::LangTags::implicate_supers(
                I18N::LangTags::Detect->http_accept_langs(
                    $c->request->header('Accept-Language')
                )
            ),
            'i-default'
        ];
    my $lang = $languages->[0];
    $lang =~ s/^(.+)-+(.+)/$1/;
    $hl->speak( $lang );
    super();
};

テンプレート

root/po.tt

Hello world」というキーを設定しています。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"; xml:lang="en" lang="en">
<head>
<title>My Catalyst App!</title>
</head>

<body>
<div id="content">
    <p>[% _('Hello world') %]</p>
</div><!-- end bodyblock -->

</body>
</html>

サンプルを起動してみる

この時点で実行結果を見てみましょう。
何も設定していないので当然キーがそのまま表示されています。

poファイルの更新

po コマンドでテンプレートディレクトリを指定すると、
自動でpoファイルを更新してくれます。
以下のコマンドでrootディレクトリ以下をparseしてみます

po parse root

なんかエラーが出ますが気にしません。
po/ja.po と po/en.po を見てみると、中身が更新されているはずです*2


en.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=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"

#: root/po.tt:10
msgid "Hello world"
msgstr ""


表示部分を編集します。
en.po はこんな感じ。

#: root/po.tt:10
msgid "Hello world"
msgstr "Hello world English!"

ja.po はこんな感じ。

#: root/po.tt:10
msgid "Hello world"
msgstr "Hello world Japanese!"


言語環境を変えて表示してみます。
英語圏の場合。

日本の場合。

きちんと表示されていますね!

更新していくときの手順

  • テンプレートにガシガシ追加していく
  • po parse root でpoファイルを更新
    • 差分のキーみ追加されていくのでそれを編集


メッセージの編集も簡単ですね!

*1:このサンプルの場合は po/podemo.pot

*2:msgid "Hello world" の部分が追記部分