単数形・複数形に対応したpoファイルの作り方

単数形の場合

PHPや各種フレームワーク等でシステムを国際化するために用意する辞書ファイル(poファイル)では、単数形・複数形の記述が可能です。

以下、複数形の必要ない言語のpoファイル例です。(一部省略してます)

~

"Plural-Forms: nplurals=1; plural=0;\n"

~

msgid "apple"
msgstr "リンゴ"

msgid "apples"
msgstr "リンゴ"

~

こんな感じで、msgidにキーとなる文章を、msgstrに翻訳された文章をどんどん書いていきます。
※但し、poファイルは自動翻訳の辞書ではなく単なる文字列置き換え用のリストなので、単語単位ではなく文章単位で入力すべきです。(上記例はわかりやすく単語になってるだけです)

ちなみに、poファイルをGUIで編集できるソフトウェア Poedit で読み込むとこんな感じ。

Poedit:単数形の例

実際に翻訳されるとこんな感じになります。

ソースコード例 日本語翻訳
gettext('apple') リンゴ
gettext('apples') リンゴ
gettext('an apple') an apple(変換されず)

上記はPHPの国際化関数 gettext() を使用して変換している例です。
ちなみに、WordPressだと_e()、CakePHPだと__()等のラッパー関数で置き換わっています。

複数形の場合

日本語では特に必要としないですが、英語圏などは単数形・複数形の表現があります。 この場合、poファイルのカタログ情報(ヘッダ情報)を変更し、さらに翻訳の書き方もちょっと変わってきます。 (通常の単数形の形ももちろん書けます。)

以下、ちょっと無理矢理ですが、日本語で複数形の表現に対応してみました。

~

"Plural-Forms: nplurals=2; plural=(n!=1);\n"

~

#: sample.txt:3
msgid "apple"
msgid_plural "apples"
msgstr[0] "リンゴ"
msgstr[1] "リンゴ達"

#: sample.txt:10
msgid "orange"
msgid_plural "%d oranges"
msgstr[0] "ひとつのオレンジ"
msgstr[1] "%d個のオレンジ達"

Poeditで読み込むとこんな感じで複数の入力が可能になってます。

Poedit:複数形の例

実際の変換はこうなります。

ソースコード例 日本語翻訳
ngettext('apple', 'apples', 0) リンゴ達
ngettext('apple', 'apples', 1) リンゴ
ngettext('apple', 'apples', 2) リンゴ達
ngettext('apple', 'apples', 3) リンゴ達
printf(ngettext('orange', '%d oranges', 0), 0) 0個のオレンジ達
printf(ngettext('orange', '%d oranges', 1), 1) ひとつのオレンジ
printf(ngettext('orange', '%d oranges', 2), 2) 2個のオレンジ達
printf(ngettext('orange', '%d oranges', 3), 3) 3個のオレンジ達

PHPの国際化関数 ngettext() を使用して、判断用に実際の数を引数に渡して変換している例です。 WordPressだと_n()、CakePHPだと__n()等のラッパー関数で置き換わっています。

1個の時のみ単数形になっており、0個と2個以上の場合に複数形になっていますね。 0個が複数形ってちょっと不思議な気もしますが、"0=いくつあるかわからない"という意味合いなんだと思います。

Plural-Formsヘッダの詳細

上記に記載したpoファイルのサンプルで、Plural-Forms:というヘッダがありますが、単数形と複数形で宣言方法が違います。


"Plural-Forms: nplurals=1; plural=0;\n"

"Plural-Forms: nplurals=2; plural=(n!=1);\n"

nplurals

npluralsには変化するパターンの数をセットします。 国によっては変化形が複数ある場合があるようです。

  • 日本語なら、単数形1パターンのみなので、1とします。
  • 英語圏なら、単数形1パターンと複数形1パターンなので、合わせて2とします。

plural

入力された個数値から引数値を計算する式を書きます。 nが実際に入ってくる個数で、上記例plural=(n!=1)ですと、「nが1じゃなければ正(1),それ以外は偽(0)」となります。 plural=(n==1?0:1);のような三項演算子も使えるようです。

そして、この計算された01が、

msgstr[0] "リンゴ"
msgstr[1] "リンゴ達"

このmsgstr[]の引数に対応している、という仕組みです。 なので、うまく計算式を利用すれば3個以上の場合分けなども書けそうですね。まぁ使わないと思いますが。

以上、ご参考になりましたでしょうか。

参考にさせて頂いたサイト