【WordPress】テーマの翻訳対応(gettextと.po/.moファイル)

WordPress

WordPressのテーマを国際化(i18n)しておくと、日本語以外の環境でも、翻訳ファイルを差し替えるだけで表示を切り替えられます。基本は、コード中の文字列をgettext関数で囲み、テキストドメインを読み込み、.po/.moファイルを用意する流れです。ここでは、その手順と、つまずきやすい点をまとめます。

スポンサーリンク

gettext関数で文字列を翻訳対応にする

テーマに直接書いた文字列は、そのままでは翻訳できません。gettext関数で囲んで「翻訳可能な文字列」にします。このとき原文は英語で書くのが慣例です。日本語の原文に対してja.poを作っても翻訳の意味がなく、他の言語にも展開しにくくなるためです。日本語表示は、英語の原文をja.poで日本語に訳して実現します。

PHP:gettext関数の使い方
<?php
// 翻訳して出力する(HTMLエスケープ付き)
esc_html_e('Read more', 'mytheme');

// 翻訳した文字列を返す(属性値ならesc_attr__)
$label = esc_html__('Show comments', 'mytheme');

// 文脈を加える(同じ英単語でも意味が違う場合)
echo esc_html_x('Post', 'noun', 'mytheme');

// プレースホルダーを使う場合は translators コメントを付ける
/* translators: %s: ユーザー名 */
printf(esc_html__('Welcome, %s', 'mytheme'), esc_html($username));

// 単数・複数
printf(
  esc_html(_n('%d comment', '%d comments', $count, 'mytheme')),
  $count
);

出力は、_e()や__()のままではなく、esc_html_e()、esc_html__()、esc_attr__()などのエスケープ付きを使います。翻訳ファイルの内容がそのままHTMLとして出力されるのを防ぐためです。

主な関数の使い分けは次のとおりです。

  • __():翻訳した文字列を返す
  • _e():翻訳した文字列をそのまま出力する(エスケープなし)
  • esc_html__() / esc_html_e():HTMLエスケープして返す/出力する
  • esc_attr__():属性値用にエスケープして返す
  • _x():文脈(context)を加えて、同じ英単語の別の意味を区別する
  • _n():単数形・複数形を切り替える

プレースホルダー(%sなど)を使う場合は、直前に/* translators: … */のコメントを書いておくと、翻訳者が何が入るかを把握できます。プレースホルダーが複数あるときは%1$s、%2$sのように番号を付けます。

テキストドメインを決めて読み込む

すべてのgettext関数には、テキストドメインを渡します。ドメインは、テーマのstyle.cssのヘッダーにあるText Domainと同じ文字列にします。通常はテーマのフォルダ名と同じにします。

style.css:テーマのヘッダー
/*
Theme Name: My Theme
Text Domain: mytheme
Domain Path: /languages
*/

翻訳ファイルを自分のテーマに置く場合は、after_setup_themeで読み込みます。

functions.php:テキストドメインの読み込み
<?php
add_action('after_setup_theme', function () {
  load_theme_textdomain('mytheme', get_template_directory() . '/languages');
});

WordPress 6.7以降は、翻訳の読み込みが早すぎる(after_setup_themeより前)と警告が出ます。after_setup_themeか、それ以降のフックで読み込んでください。

子テーマの場合

load_theme_textdomain()の第2引数にget_template_directory()を使うと、親テーマのフォルダを指します。子テーマ側に翻訳ファイルを置くときは、load_child_theme_textdomain()とget_stylesheet_directory()を使います。子テーマの基本は子テーマでfunctions.phpを正しく拡張する方法、コードの分け方はfunctions.phpを分割して管理する方法で解説しています。

functions.php:子テーマでの読み込み
<?php
// 子テーマ側のlanguagesフォルダを読み込む場合
add_action('after_setup_theme', function () {
  load_child_theme_textdomain('mytheme-child', get_stylesheet_directory() . '/languages');
});

.potと.po/.moファイルを作る

翻訳ファイルは、役割の違う三つの形式があります。

  • .pot:コードから抽出した原文の一覧(翻訳の雛形)
  • .po:人が編集する翻訳ファイル(テキスト形式)
  • .mo:.poをコンパイルしたバイナリ。WordPressが実際に読み込む

作り方は、GUIのPoeditを使う方法と、WP-CLIを使う方法があります。Poeditでは、新規カタログを作成して、ソースコードをスキャンし、翻訳を入力して保存します。保存すると、.poと.moが同時に出力されます。WP-CLIでは次のように作成します。

Shell:WP-CLIで.potと.moを作る
# .pot(翻訳の雛形)を作る
wp i18n make-pot . languages/mytheme.pot --exclude=node_modules

# .po から .mo を作る
wp i18n make-mo languages/

WordPress 6.5以降は、.moに加えて、高速な.l10n.php形式の翻訳ファイルも読み込まれます。

翻訳ファイルの名前と置き場所

テーマのlanguagesフォルダに置く場合、ファイル名はロケールだけにします。日本語ならja.poとja.moです。

ディレクトリ構成
mytheme/
├─ style.css
├─ functions.php
└─ languages/
   ├─ mytheme.pot
   ├─ ja.po
   └─ ja.mo

この命名規則は、テーマ内に置く場合のものです。WordPress本体の言語ディレクトリ(WP_LANG_DIR)側に置く翻訳ファイルはテキストドメイン-ロケール.moの形式になります。この違いは、WordPress本体のコードでも分岐しています。サイトの言語設定が日本語なら、ja.moが自動的に適用されます。

翻訳対応のポイント

  • テーマ内の表示文字列は、すべてgettext関数で囲む
  • 原文は英語で書き、翻訳は.poで行う
  • テキストドメインはstyle.cssのText Domainと一致させる
  • 出力時はesc_html__()などのエスケープ付き関数を使う
  • 文字列を連結せず、1つの文として渡す(語順が言語で変わるため)
  • 同じ単語でも意味が違うときは_x()、数に応じて変えるときは_n()を使う

よくある質問(FAQ)

Q.po/.moファイルの作り方は?
APoeditで.potから新規カタログを作って翻訳し、保存すると.poと.moが出力されます。WP-CLIならwp i18n make-potで.potを作り、.poを翻訳後にwp i18n make-moで.moを作ります。テーマのlanguagesフォルダに置いてください。
Q__()と_e()の使い分けは?
A__()は翻訳した文字列を返し、_e()は出力します。ただしどちらもエスケープはしません。実際のテーマでは、esc_html__()やesc_html_e()のようなエスケープ付きの関数を使ってください。
Q翻訳ファイルが反映されない場合の原因は?
Aよくある原因は三つあります。テキストドメインがstyle.cssや関数の引数と一致していないこと、load_theme_textdomain()のパスが正しくないこと、.moのファイル名が違うことです。テーマ内のlanguagesフォルダに置く場合、.moはja.moのようにロケール名だけにします。mytheme-ja.moはWP_LANG_DIR側の命名なので、テーマ内に置いても読み込まれません。.poを編集した後は、.moの再生成も忘れずに行ってください。

まとめ

テーマを翻訳対応にするには、英語の原文をエスケープ付きのgettext関数で囲み、テキストドメインを読み込み、.pot/.po/.moを用意します。テーマ内の翻訳ファイルはロケール名だけ(ja.mo)にすること、子テーマでは読み込み関数とパスが変わることに注意してください。