PHPでGettext拡張を使ってみた

PHPのGettext拡張は、PHPに標準的についてきます。
自前でビルドしたりする場合、デフォルトで機能が有効になってなかったりするかもしれないので注意です。

なので、PHPで複数言語対応するには、gettextを使うのが一番簡単です。
# そう信じていました。


参考:

ウノウラボ Unoh Labs: 5分でわかる PHP で書かれた Web サービスの国際化(その2)
http://labs.unoh.net/2006/06/5_php_web_2.html

※ ほとんど、まんまですが。


さて、Gettextの使い方です。
前提として、phpが動作するようにWebサーバ(apache)が設定済みで、かつ、phpのgettextが有効になっていることが必要です。
また、gettext もインストールされている必要があります。


まず、スクリプトから。
ファイルのエンコードUTF-8にします。
当然ですが、EUC-JPやSJISだと、他の言語の時に文字化けすることになります。


↓単純なサンプルです

<?php
$lang = "ja_JP.UTF-8";
$domain = "messages";
setlocale(LC_ALL, $lang);
bindtextdomain($domain, "./locale/");
textdomain($domain);
echo _("hello world");
?>

このファイルを、test.php という名前で保存します。


次に、翻訳するべき文字列を xgettext で抽出します。
抽出対象となるプログラミング言語は、拡張子で自動判別してくれます。
拡張子に特殊なものを使っている場合はオプションで指定してください。(manを参照)

 $ xgettext test.php

このコマンドを実行すると、 messages.po というファイルができると思います。
この時抽出される文字列は、 _("hello world") というGettextの関数内の文字列です。
# _() 関数は gettext() 関数の別名です


ファイルの中身はこんなかんじ。

# 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"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2008-03-28 12:40+0900\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"

#: test.php:7
msgid "hello world"
msgstr ""


このファイルをローケール(言語)毎に翻訳してあげればいいわけです。


まず、次の行の CHARSET を、utf-8euc-jp など、日本語が扱える文字コードにします。
今回は utf-8 にしました。
もちろん、ファイルの文字コードutf-8 です。

"Content-Type: text/plain; charset=CHARSET\n"


で、本題の翻訳についてです。
つぎのように、msgid と、msgstr が対になっています。

#: test.php:7
msgid "hello world"
msgstr ""

msgid が翻訳前の文字列、msgstrが翻訳した文字列です。
複数在る場合はこの対がメッセージの数だけ並びます。
要するに、msgidに対応するように、msgstrに翻訳後の文字を書いていくだけです。



翻訳したファイルは次のディレクトリに配置します。

/<ロケール名>/LC_MESSAGES/<指定したドメイン名>.po

日本語のロケールは ja_JP です。 英語は en、 en_US などがあります。
うちの環境では、jaではダメでした。環境によってロケールの指定は異なるみたいです。
# ja_US じゃダメなのかというツッコミもしたくなりますが。日本語には違いないんですがね。

最後の方に、ロケールでハマってたことをグチグチ書いてますが、正解をここで。
文字コードUTF-8にしているので、 日本語のロケールは ja_JP.UTF-8 とします。
これも環境依存かもしれないので100%ではないんですが。


翻訳したファイルをコンパイルして、gettext で使える形式に変換します。
実行すると、messages.mo というファイルができます。
このファイルも、翻訳したpoファイルと同じディレクトリ内に配置します。

 $ msgfmt messages.po


これで翻訳の作業は完了です。
他の言語を扱う場合も同様に処理します。


あとは、ブラウザから、スクリプトを見にいってあげれば、ロケールにあった言語で表示されるはずです。
ちなみに、用意していない言語は、スクリプトに書いている文字列がそのまま出力されるようです。

問題

これで無事翻訳機構完了・・・かと思いきや、
うちの開発環境(Fedora)ではそれだけは終わりませんでした。


_("hello world") で得られる文字列は "こんにちわ世界" です。
しかし、文字コードが、EUC-JP で返ってきます。
ブラウザのエンコードEUC-JPに変えるとちゃんと見れます。
なので、

mb_convert_encoding(_("hello world", "UTF-8", "EUC-JP");

とすると、ちゃんとUTF-8で表示されます。
でも、これだと他の言語で文字化けするので使えません。


コンパイルしたmoファイルをlvで無理やり開くと、文字コードeuc-jp に勝手に変換されていました。
別の環境の、FreeBSD上でコンパイルすると無事、utf-8になりました。
# なんかライブラリがたりてなかったみたいです。gettext-lib を yumでいれたら無事 utf-8で作られるようになりました。


が、まだ文字化けします。
今度はブラウザのエンコード設定を変えても文字化けたままです。
文字コードが壊れてるようです。
PHPのgettextが utf-8 なのに euc-jp なつもりで読み込んでくれてる気がします。


これ書きながら、ふとおもいついたので、ロケールUnixのLANG みたいにしてみました。
ja_JP.UTF-8 にするとあっさりうごきました。

なんだそれorz