20日目: 国際化とローカライゼーション

昨日は、AJAX のよいところを追加することで検索エンジンを終わらせました。

今日は、Jobeet の 国際化(Internationalization - i18n)と ローカライゼーション(Localization - l10n)を話します。

Wikipediaより:

国際化とはエンジニアリングの変更なしにさまざまな言語と地域に適応できるようにするソフトウェアアプリケーションの設計プロセスです。

ローカライゼーションとはロケール固有のコンポーネントを追加しテキストを翻訳することで特定の地域もしくは言語用にソフトウェアを適応させるプロセスです。

いつものように、Ark フレームワークは車輪を再発明しないので国際化(i18n)とローカライゼーションは GNU gettext に基づいてサポートされます。

I18N プラグイン

Ark アプリケーションを国際化するには I18N プラグインをロードするようにします。

# Jobeet.pm
use_plugins qw{
    Session
    Session::State::Cookie
    Session::Store::Model

    I18N
};

I18N プラグインをロードすると $c->localize メソッドが使えるようになり、

$c->localize('Hello');

などとすることで各ユーザーの言語での挨拶を得ることができます。

新たなモジュール

I18Nプラグインを使うにあたって、Locale::Maketext::Lexicon が新たに必要になります。cpanmで入れておきましょう。Makefile.PLへの追記も忘れずに。

言語リソースファイル

I18N プラグインは lib/Jobeet/I18N ディレクトリにある gettext の言語リソースファイル(.po, .mo) を読み込みます。ファイル名は

などのようにロケール名をファイル名に指定しておきます。

言語リソースファイルの雛型自動生成

言語リソースファイルの生成はどのようにやるのでしょうか。すべての文字列を検索し、手動でリスト化するのは大変です。そしておそらく抜けや重複が出るでしょう。

gettext フォーマットは広く使われているのでこのような場合にリソースファイルの雛型を自動で作成してくれるツールがたくさんあります。ここでは Locale::Maketext::Lexiconに付属している xgettext.pl を使用してみましょう。

zshをお使いの方は:

$ xgettext.pl -o lib/Jobeet/I18N/ja.po (root|lib)/**/*.(mt|pm)

古き良き時代のシェルをお使いの方は:

$ find lib root -name '*.pm' -or -name '*.mt' | xargs xgettext.pl -o lib/Jobeet/I18N/ja.po

とすることで lib/Jobeet/I18N/ja.po という名前の言語リソースファイルの雛型を自動的に生成することができます。

このスクリプトは与えられたファイルリスト(ここではPerlソースとテンプレートファイル)から特定の文字列を検索し、リソースファイルに追加してくれます。さきほどの

$c->localize(...)

というメソッドは自動的に認識され、リストに追加されます。またテンプレートなどでも

<?= $c->localize(...) ?>

と書いておけばそれは認識されます。

xマクロの利用

いちいち $c->localize と書くのはめんどくさいでしょう。マクロを利用し

x('Hello')

と書くだけでいいようにしましょう。この x 関数は xgettext.pl で自動認識される形式に含まれているのでさきほどのコマンドラインでこの表記も対象に含まれます。

# Jobeet/View.MT.pm
has '+macro' => default => sub {
    return {
        sha1_hex => \&Digest::SHA1::sha1_hex,
        x        => sub { Jobeet->context->localize(@_) },
    },
};

これでテンプレート内で のように記述することができます。

フォームの国際化

テンプレートの国際化だけでは不十分です、フォームの多言語化はどうすればいいでしょう。 Ark::Form はフォーム定義のなかで x マクロをデフォルトでサポートしています。

param category => (
    label   => x('Category'),
    type    => 'ChoiceField',
    choices => [map { $_->slug => $_->name } models('Schema::Category')->all],
    constraints => [
        'NOT_NULL',
    ],
);

とかけば、Category というラベルは国際化されます。

エラーメッセージも同様に x マクロを使用し

sub messages {
    return {
        not_null => x('please input [_1]'),
        int      => x('please input [_1] as integer'),
        ascii    => x('please input [_1] as ascii characters without space'),
    };
}

と書くことができます。

翻訳

今までの作業で Perl ソース、テンプレート、フォーム、すべての多言語化したい文字列は $c->localize() または x() を使用するようにしましたので、xgettext.pl で残らず雛型を生成することができます。

そしてあとはそのファイルを翻訳していくだけです。

ここでも標準的な国際化ライブラリを使用している利点で、poファイルを編集するツールとして Emacs の po-mode や、PoEdit というスタンドアローンのアプリケーションなど様々なものが利用できます。

言語リソースの追加

翻訳したあとテンプレートに新たな文言が加わったらどうなるでしょうか? xgettext.pl はすでに翻訳したファイルも上書きしてしまうのでしょうか?

心配ありません。 xgettext.pl はすでにある定義は上書きしません。定義がない文言だけ自動的に判別し、未翻訳状態として .po ファイルに定義を追加してくれます。

したがって

  1. テンプレートファイルの更新
  2. xgettext.pl 3.未翻訳状態のものを翻訳

というルーチンになります。

また明日

今日の宿題は今日の知識を利用し実際に Jobeet アプリケーションを国際化してみてください。

明日は Ark プロジェクトを編成するためにたくさんのファイルを移動させ、異なるアプローチを探求するので、特別なチュートリアルに備えてください。