- サイトデザイン工事中です。ご意見をお寄せください。
- 赤色のリンクは、まだ日本語Codexに存在しないページ・画像です。英語版と併せてご覧ください。(詳細)
I18n for WordPress Developers
目次 |
国際化とは
I18n は、internationalization、すなわちアプリケーションを翻訳可能にするプロセスのことをいいます。WordPress では、決まった方法で翻訳されるべき文字列をマークすることを意味します。I と n の間に 18 文字あるため、これを i18n と呼びます。
Gettext 入門
WordPress では、gettext ライブラリおよびツールを使用して国際化します。
翻訳可能文字列
アプリケーションで文字列を翻訳可能にするには、元の文字列を __ 関数コールで囲います。
$hello = __("Hello, dear user!");
文字列をブラウザーに表示する場合は、_e 関数を使います。
_e("Your Ad here")
POT ファイル
ソースファイルで文字列をマークしたら、xgettext という gettext ユーティリティを使用して文字列を抽出し、テンプレート翻訳 POT ファイルを構築します。以下は、POT ファイルのエントリの例です。
#: wp-admin/admin-header.php:49 msgid "Sign Out" msgstr ""
PO ファイル
翻訳者は WordPress POT ファイルを受け取り、msgstr セクションを自分の言語に翻訳します。出力は PO ファイルです。POT ファイルとフォーマットは同じですが、翻訳されていて、特定のヘッダが異なります。
MO ファイル
翻訳済み PO ファイルから MO ファイルを構築します。このファイルは、すべての元の文字列と翻訳を含み、高速に翻訳を抽出するのに適したバイナリファイルです。msgfmt ツールを使用して変換します。
典型的な msgfmt コマンドは、msgfmt -o filename.mo filename.po です。
fたくさんの PO ファイルを一度に変換するために、バッチ処理を実行することができます。例えば、以下のように bash コマンドを使用します。
# PO ファイルを探し、それぞれに msgfmt を実行し、出力を MO にリネームする for file in `find . -name "*.po"` ; do msgfmt -o `echo $file | sed s/\.po/\.mo/` $file ; done
テキストドメイン
1 つのアプリケーションに、複数の巨大な論理翻訳モジュールを使用し、それらに異なる MO ファイルを使用する必要があるかもしれません。ドメインは、各モジュールをハンドルし、それぞれ異なる MO ファイルを持ちます。テーマの翻訳とプラグインの翻訳は、異なるドメインになります。
gettext に関して詳しく知りたい方は、gettext online manual を参照することをおすすめします。
翻訳する文字列をマークする
翻訳する文字列は、特別な関数のいずれかを呼び出すように囲います。最もよく用いられるのは、__() です。この関数は、引数の訳を返します。
echo "<h2>".__('Blog Options')."</h2>";
別の例は、_e() です。この関数は引数の訳を出力します。echo __('Using this option you will make a fortune!'); と書く代わりに、短縮した _e('Using this option you will make a fortune!'); を使用できます。
プレースホルダ
echo "We deleted $count spams."
この行はどうやって国際化しますか?試してみましょう。
_e("We deleted $count spams.");
これは動作しません。翻訳する文字列はソースから抽出されるので、We deleted $count spams. というフレーズの翻訳は上手くいくでしょう。しかし、この例では、_e は We deleted 49494 spams. のような引数を伴って呼び出され、gettext は適切な翻訳を見つけることができず、引数 We deleted 49494 spams. をそのまま返します。残念ながら、適切に翻訳されません。
解決法は、printf やその類似の関数を使うことです。printf や sprintf が特に役立ちます。スパムを数えるプログラムの適切な解決は、以下のようになります。
printf(__("We deleted %d spams."), $count);
ここでは翻訳する文字列は、テンプレート We deleted %d spams. (ソースでも実行時でも同じ)であることに注意してください。
複数のプレースホルダがある場合は、argument swapping を推奨します。
printf(__("Your city is %1$s, and your zip code is %2$s."), $city, $zipcode);
ここでは都市名の後に郵便番号が表示されています。しかし、最初に郵便番号、次に都市の順のほうが適切な言語もあるでしょう。上の例のように %s 接頭辞を使用することで、対処可能になります。翻訳は、以下のようにすることができます。
printf(__("Your zip code is %2$s, and your city is %1$s."), $city, $zipcode);
(訳注: 実際には printf の中身ではなく .po ファイルで順序を変える --Mizuno 2010年4月12日 (月) 05:30 (UTC))
複数
スパムの例 printf(__("We deleted %d spams."), $count); に戻りましょう。スパムを 1 つだけ削除した場合はどうなるでしょう。出力は We deleted 1 spams. のようになり、正しい英語ではありません。また他の言語でも正しくないでしょう。
WordPress では _n 関数を使うことができます。
printf(_n("We deleted %d spam.", "We deleted %d spams.", $count), $count);
_n は、3 つの引数を取ります。
- singular — 単数形
- plural — 複数形
- count — 単数形と複数形のどちらを用いるかを決定する数。(2 より多い形を持つ言語があります)
この関数の戻り値は、与えられた数に対応する翻訳されたフォームになります。
文脈による曖昧性回避
ある用語が、複数の文脈で用いられ、英語では同じ単語だが、他の言語では別の単語に翻訳すべきことがあります。例えば、単語 Post は、動詞 (ここをクリックしてコメントを投稿する) としても名詞 (この投稿を編集する) としても使用できます。このような場合、_x() 関数を使用すべきです。この関数は __() に似ていますが、文脈を第二引数にします。
if ( false === $commenttxt ) $commenttxt = _x( 'Comment', 'noun' );
if ( false === $trackbacktxt ) $trackbacktxt = __( 'Trackback' );
if ( false === $pingbacktxt ) $pingbacktxt = __( 'Pingback' );
...
// some other place in the code
echo _x('Comment', 'column name');
こうすることで、元バージョンの文字列 Comment を、翻訳者は、異なる文脈の 2 つの Comment 文字列とみなすことができます。
説明
__('g:i:s a') のような文字列を翻訳者がどう訳していいか分かると思いますか? この場合、ソースコードにコメントを追加できます。コメントは、translators: という語で始まり、gettext を呼び出す直前の PHP コメントである必要があります。以下に例を示します。
/* translators: draft saved date format, see http://php.net/date */
$draft_saved_date_format = __('g:i:s a');
translators: コメントを追加することで、翻訳者宛にメッセージを書くことができ、翻訳者が文字列がどんなものかを知ることができます。
改行文字
Gettext は、翻訳する文字列に \r (ASCII code: 13) を好みません。 \r の代わりに \n を使用してください。
JavaScript ファイルを処理する
ベストプラクティス
WordPress 特有の例を集める前に、gettext manual の短いけれど優れた記事を読みましょう。要約すると、以下のようになります。
- きちんとした英語 — スラングや省略は避けます。
- 文全体 — 多くの言語で、語順が英語と異なります。
- 段落で分割 — 関連する文をまとめます。しかし、ページ全体を一つにしてはいけません。
- 文字連結ではなく、文字列フォーマットを使用する — sprintf(__('Replace %s with %s'), $a, $b); が __('Replace ').$a.__(' with ').$b; よりも常に好ましいです。
- 不自然なマークアップや制御記号を避ける — テキストを囲むタグや、URL は翻訳に含めません(他の言語用のバージョンがある場合を除く)。
テーマ開発者、プラグイン開発者のための国際化
ドメインの選択と読み込み
テキストドメインは、WordPress が読み込む全翻訳から区別できるようにするための、一意識別子です。プラグインのベースネームを使用するのがいいでしょう。
例: プラグインが、shareadraft.php という単一のファイル、あるいは shareadraft というフォルダ内にある場合、最適なドメイン名は shareadraft です。テーマの場合は、ディレクトリ名を用います。
ドメイン名は、プラグインの翻訳の MO ファイルの名前としても使用します。以下のようにして呼び出します。
load_plugin_textdomain( $domain, $path_from_abspath, $path_from_plugins_folder )
init アクションの早い段階で呼び出されます。
例:
$plugin_dir = basename(dirname(__FILE__));
load_plugin_textdomain( 'myplugin', 'wp-content/plugins/' . $plugin_dir, $plugin_dir );
プラグインのベースディレクトリから myplugin-locale.mo を呼び出そうとします。locale は、言語コードまたは国コード、あるいはその両方をアンダースコアでつないだもので、WPLANG で定義したものです。たとえばドイツ語のロケールは de ですし、デンマーク語のロケールは da_DK です。(言語コードと国コードについての詳細は、Installing WordPress in Your Language を参照してください。)
- 2.6 より前のバージョンでは、第二パラメータは、.mo ファイルを含むディレクトリの ABSPATH からの相対パスとし、第三パラメータは空白にする必要があります。
- WordPress 2.6 以降では、第三パラメータは、.mo ファイルを含むディレクトリのプラグインディレクトリからの相対パスです。もし古いバージョンの WordPress の互換性が不要ならば、第二パラメータは空白でかまいません。)
テーマの場合の処理も驚くほど似ています。
load_theme_textdomain(domain-name);
上記のコールを functions.php に記述すると、テーマディレクトリ内でlocale.mo を探し、読み込みます (ここで locale は現在の言語、すなわち pt_BR.mo です)。
WordPress 2.8 以降のウィジェットの国際化
WordPress 2.8 から新しいウィジェット API を使用しています。ウィジェット開発者は標準ウィジェットクラスとその関数を拡張すれば良いです。この新しい API では init 関数はありません。widget(), form(), update() 関数を使用してウィジェットをコーディングしたら、ウィジェットを登録する必要があります。テキストドメインは、ウィジェット登録後に読み込みされます。
例:
// register FooWidget widget
add_action('widgets_init', create_function(, 'return register_widget("FooWidget");'));
$plugin_dir = basename(dirname(__FILE__));
load_plugin_textdomain( 'FooWidget', 'wp-content/plugins/' . $plugin_dir, $plugin_dir );
この例は、FooWidget という名前のウィジェットを登録し、プラグインディレクトリ変数を設定し、FooWidget-locale.po ファイル読み込みを試みます。
テーマやプラグインで文字列をマークする
上記で説明した ルールがすべて当てはまります。さらにもう一つ、ルールが加わります。そのルールは、ドメインをすべての __, _e, _c および __ngettext コールの引数に追加しなければならない、さもなければ、翻訳が動作しない。
例:
- __('String') should become __('String', 'domain')
- _e('String') should become _e('String', 'domain')
- __ngettext('String', 'Strings', $c) should become __ngettext('String', 'Strings', $c, 'domain')
ドメインを手作業で追加するのは大変ですね。自動化することができます。
- プラグインが 公式リポジトリ に登録されている場合、Admin ページへ行き、Add Domain to Gettext Calls に進みます。
そうでない場合:
- add-textdomain.php スクリプトを入手し、以下を実行します。
php add-textdomain.php -i domain phpfile phpfile ...
こうすると、ファイルにあるすべての gettext コールにドメインが追加されます。
POT ファイルの生成
POT file を翻訳者に渡して翻訳してもらう必要があることを覚えていましたか?
POT ファイルを生成する方法はいくつかあります。
- 公式リポジトリ に登録されている場合、Admin ページへ行き、Generate POT file に進みます。
- リポジトリに登録されていない場合、SVN (SVN については Using Subversion/en:Using Subversion を参照) から wordpress-i18n tools ディレクトリをチェックアウトし、makepot.php スクリプトを以下のように実行します。
php makepot.php wp-plugin your-plugin-directory
このコマンドを実行するには、 gettext (GNU 国際化ユーティリティ) パッケージがサーバーにインストールされていることが必要です。 完了したら、現在のディレクトリに POT ファイルがあるはずです。
プラグインと一緒に POT ファイルを提供するのは良い考えです。翻訳者がわざわざあなたに尋ねる必要がなくなります。
プラグインヘッダに以下のように追加すると、管理画面でプラグインを表示するときに WordPress がプラグインメタデータを国際化します。
Text Domain: your-text-domain Domain Path: /languages/
PO/MO ファイルに、ここに記入した英語に対応する翻訳を含めるのを忘れないようにしてください。
関連資料
日本語
- WordPress プラグイン日本語化 Tips
- WordPress Plugin 作者に覚えておいて欲しいこと(dogmap.jp)- プラグインヘッダ内のテキストの国際化について。
英語
最新英語版: WordPress Codex » I18n_for_WordPress_Developers (最新版との差分)