プラグインを国際化する方法

アプリケーションで文字列を翻訳可能にするには、原文の文字列を特殊な関数の呼び出しでラップする必要があります。これらの関数を総称して「gettext」と呼びます。

Gettext の紹介

WordPress は国際化のために gettext ライブラリとツールを使用しますが、直接的には使用しません: 文字列の翻訳を可能にするためだけに作られた特別な関数のセットがあります。これらの関数を以下に示します。これらはプラグインで使うべき関数です。

gettext をもっと深く知りたい人は、gettext オンラインマニュアルをお読みください。

テキスト・ドメイン

プラグインに属するすべてのテキストを示すために「テキスト・ドメイン」を使用してください。テキスト・ドメインとは、WordPress がすべての読み込まれた翻訳を区別できる様にするための一意の識別子です。これにより、移植性が向上し、既存の WordPress ツールとの相性も良くなります。

テキスト・ドメインは、プラグインの スラッグ と一致しなければなりません。プラグインが my-plugin.php という単一のファイルであるか、my-plugin というフォルダーに含まれている場合、ドメイン名は my-plugin でなければなりません。プラグインが wordpress.org でホストされている場合は、プラグイン URL のスラッグ部分 (wordpress.org/plugins/my-plugin) でなければなりません。

テキスト・ドメイン名は、アンダースコアではなくダッシュを使用し、小文字で、スペースを含まないものでなければなりません。

テキスト・ドメインも、プラグインヘッダーに追加する必要があります。WordPress は、プラグインが無効になっている場合でも、プラグインのメタデータを国際化するためにこれを使用します。テキスト・ドメインは、テキスト・ドメインを読み込むときに使用したものと同じでなければなりません。

ヘッダー例

/* 
 * Plugin Name: My Plugin
 * Author: Plugin Author
 * Text Domain: my-plugin
 */
もう一度言いますが、”my-plugin” をプラグインのスラッグに変更します。
WordPress 4.6以降、Text Domain ヘッダーはプラグインのスラッグと同じでなければならないため、省略可能です。含めても害はありませんが、必須ではありません。

ドメイン・パス

ドメイン・パスは、プラグインの翻訳の場所を定義します。これにはいくつかの使い道があり、特にプラグインが無効になっているときでも WordPress が翻訳の場所を知っている様にします。デフォルトは、プラグインがあるフォルダーです。

たとえば、翻訳がプラグイン内の languages というフォルダーにある場合、ドメイン・パスは /languages となり、最初のスラッシュと一緒に記述する必要があります:

ヘッダー例

/*
 * Plugin Name: My Plugin
 * Author: Plugin Author
 * Text Domain: my-plugin
 * Domain Path: /languages
 */
プラグインが公式 WordPress プラグインディレクトリにある場合、Domain Path ヘッダーは省略できます。

基本文字列

基本文字列 (プレースホルダーや複数形のない文字列のこと) には、__() を使います。これは引数の翻訳を返します:

__( 'Blog Options', 'my-plugin' );
gettext 関数のテキスト・ドメイン部分には、変数名や定数を使用しないでください。たとえば: ショートカットとしてこれを使用しないでください:

__( 'Translate me.' , $text_domain );

取得した翻訳を出力するには、_e() を使います。つまり、以下のように書く代わりに:

echo __( 'WordPress is the best!', 'my-plugin' );

以下のように、書くことができます:

_e( 'WordPress is the best!', 'my-plugin' );

変数

もし次のような文字列があったら、どうでしょう:

echo 'Your city is $city.'

この場合、$city は変数であり、翻訳の一部であってはなりません。解決策としては、関数の printf ファミリーとともに変数のプレースホルダーを使うことです。特に便利なのは printf と sprintf です。これが適切な解決方法です:

printf(
  /* translators: %s: Name of a city */
  __( 'Your city is %s.', 'my-plugin' ),
  $city
);

ここでは、翻訳のための文字列はテンプレート "Your city is %s." だけであり、ソースでもランタイムでも同じであることに注意してください。

また、翻訳者がプレースホルダーの文脈を知るためのヒントもあります。

文字列内に複数のプレースホルダーがある場合は、引数の入れ替え を使用することを推奨します。この場合、文字列をシングルクオート (') で囲むことが必須で、ダブルクオート (") を使用すると、php が $s を変数 s として解釈してしまうからです。

printf(
  /* translators: 1: Name of a city 2: ZIP code */
  __( 'Your city is %1$s, and your zip code is %2$s.', 'my-plugin' ),
  $city,
  $zipcode
);

ここでは、郵便番号が都市名の後に表示されています。言語によっては、郵便番号と都市名を逆の順序で表示するほうが適切な場合もあります。上の例で接頭辞 %s を使うことで、そのようなケースに対応できます。これにより、翻訳は次のように書くことができます:

printf(
  /* translators: 1: Name of a city 2: ZIP code */
  __( 'Your zip code is %2$s, and your city is %1$s.', 'my-plugin' ),
  $city,
  $zipcode
);

重要 ! 以下のコードは間違っています:

// This is incorrect do not use.
_e( "Your city is $city.", 'my-plugin' );

翻訳用の文字列はソースから抽出されるので、翻訳者はこのフレーズを翻訳することになります: "Your city is $city."

しかし、アプリケーションでは _e は "Your city is London." のような引数で呼び出され、gettext はこの引数の適切な翻訳を見つけられず、引数 "Your city is London." を返します。残念ながら、これは正しく翻訳されていません。

複数形

基本的な複数形化

アイテム数が変わると変化する文字列がある場合、翻訳に反映させる方法が必要になります。たとえば、英語では "One comment" と "Two comments" があります。他の言語では複数の複数形があります。WordPress でこれを処理するには、_n() 関数を使います。

printf(
  _n(
    '%s comment',
    '%s comments',
    get_comments_number(),
    'my-plugin'
  ),
  number_format_i18n( get_comments_number() )
);

_n() は4つの引数を受け付けます:

  • singular – 文字列の単数形 (言語によっては1以外の数にも使えるので、'One item' の代わりに '%s item' を使うべきであることに注意)。
  • plural – 文字列の複数形。
  • count – 単数形と複数形のどちらを返すかを決定する、オブジェクトの数 (2つ以上の形式を持つ言語もあります)。
  • text domain – プラグインのテキスト・ドメイン。

関数の戻り値は、与えられた数に対応する正しい翻訳形です。

言語によっては、他の数に単数形を使うものもあります (たとえば、英語の「21st」や「31st」のように、21、31など)。単数形を特別扱いしたい場合は、特に確認してください:

if ( 1 === $count ) {
  printf( esc_html__( 'Last thing!', 'my-text-domain' ), $count );
} else {
  printf( esc_html( _n( '%d thing.', '%d things.', $count, 'my-text-domain' ) ), $count );
}

また、パラメータ $count は、しばしば2回使用されることに注意してください。まず $count を _n() に渡して、どの翻訳文字列を使うかを決定し、続いて $count を printf() に渡して、翻訳文字列に数値を代入します。

後で行われる複数形化

まず _n_noop() または _nx_noop() で複数の文字列を設定します。

$comments_plural = _n_noop(
  '%s comment.',
  '%s comments.'
);

続いてコードの後の方で、translate_nooped_plural() を使って文字列を読み込むことができます。

printf(
  translate_nooped_plural(
    $comments_plural,
    get_comments_number(),
    'my-plugin'
  ),
  number_format_i18n( get_comments_number() )
);

context によるあいまいさ回避

一つの単語が複数の文脈で使われることがあり、英語では一つの同じ単語であっても、他の言語では異なる翻訳をしなければならないことがあります。たとえば、Post という単語は、動詞 "Click here to post your comment" としても、名詞 "Edit this post" としても使えます。このような場合は _x() または _ex() 関数を使用します。これは __() や _e() と似ているが、追加の引数 — コンテキスト — があります:

_x( 'Post', 'noun', 'my-plugin' );
_x( 'Post', 'verb', 'my-plugin' );

両方のケースでこの方法を使うと、原文の文字列 Comment は得られますが、翻訳者には翻訳用の2つの Comment 文字列が、それぞれ異なる文脈で表示されることになります。

__() と同様に、_x() にも echo 版があることに注意してください: _ex() です。前の例は次のように書けます:

_ex( 'Post', 'noun', 'my-plugin' );
_ex( 'Post', 'verb', 'my-plugin' );

読みやすさとコーディングのしやすさを高めるとあなたが思うほうを使いましょう。

説明

翻訳者が __( 'g:i:s a' ) のような文字列をどのように翻訳すればよいのかわかる様に、ソースコードに明確なコメントを追加できます。これは gettext 呼び出しの直前に、単語 translators: で始まる、PHP コメントでなければなりません。以下に例を示します:

/* translators: draft saved date format, see http://php.net/date */
$saved_date_format = __( 'g:i:s a' );

また、_n_noop( '<strong>Version %1$s</strong> addressed %2$s bug.', '<strong>Version %1$s</strong> addressed %2$s bugs.' ) のように、文字列のプレースホルダーを説明するためにも使われます。

/* translators: 1: WordPress version number, 2: plural number of bugs. */
_n_noop( '<strong>Version %1$s</strong> addressed %2$s bug.','<strong>Version %1$s</strong>strong> addressed %2$s bugs.' );

改行文字

Gettext は、翻訳対象の文字列内に r (ASCII コード: 13) を好まないので、これを避けて代わりに n を使ってください。

空文字列

空文字列は、Gettext 内部で使用するために予約されているので、空文字列を国際化しようとしてはいけません。また、翻訳者はコンテキストを確認しないので、意味がありません。

空文字列を国際化する正当なユースケースがある場合は、翻訳者を助けると同時に Gettext システムと共存するためにコンテキストを追加します。

エスケープ文字列

すべての文字列をエスケープしておくと、翻訳者が悪意あるコードを実行することはありません。国際化関数と統合されたエスケープ関数がいくつかあります。

ローカライゼーション関数

基本関数

翻訳とエスケープ関数

翻訳が必要な文字列や、html タグの属性で使用されている文字列は、エスケープする必要があります。

日付および数値関数

文字列の書き方のベストプラクティス

文字列を書く際のベスト・プラクティスは、以下のようになります。

  • 適切な英語スタイルを使用する – スラングや略語は最小限にとどめましょう.
  • 文章全体を使う – ほとんどの言語では、語順は英語とは異なります。
  • 段落を分ける – 関連する文章を一つにまとめますが、1ページ分の文章を一つの文字列に含めないでください。
  • 翻訳可能なフレーズの先頭や末尾に、空白を残さないようにしましょう。
  • 文字列は、翻訳されると2倍の長さになる可能性があります。
  • 変則的なマークアップや 変則的な制御文字を避ける – テキストを囲むタグを含めないでください。
  • 翻訳された文字列に不必要な HTML マークアップを入れないでください。
  • 他の言語のバージョンがある場合を除き、URL は翻訳用に含めないでください。
  • 言語によってはプレースホルダーの位置が変わるので、文字列にプレースホルダーとして変数を追加する様にしてください。
printf(
  __( 'Search results for: %s', 'my-plugin' ),
  get_search_query()
);
  • 文字列連結の代わりにフォーマット文字列を使用する – 単語ではなくフレーズを翻訳する – printf( __( 'Your city is %1$s, and your zip code is %2$s.', 'my-plugin' ), $city, $zipcode ); は __( 'Your city is ', 'my-plugin' ) . $city . __( ', and your zip code is ', 'my-plugin' ) . $zipcode; より優れています。
  • __( 'Posts:', 'my-plugin' ); と __( 'Posts', 'my-plugin' ); のように、複数の文字列を翻訳する必要がない様、同じ単語と同じ記号を使う様にしてください。

文字列へのテキスト・ドメインの追加

__()_e()__n() の gettext 呼び出しの引数として、テキスト・ドメインを追加する必要があり、さもないと翻訳が正常に機能しません。

例:

  • __( 'Post' ) は __( 'Post', 'my-theme' ) になるはずです。
  • _e( 'Post' ) は _e( 'Post', 'my-theme' ) になるはずです。
  • _n( '%s post', '%s posts', $count ) は _n( '%s post', '%s posts', $count, 'my-theme' ) になるはずです。

WordPress のコア (例: 設定) でも使用されている文字列がプラグインにある場合でも、独自のテキストドメインを追加する必要があり、そうしないと、コアの文字列が変更された場合 (これはよくあること)、翻訳されなくなります。

コードを書くとき、手作業でテキスト・ドメインを追加するのは、継続して行わなければ重荷になりかねないからこそ、自動的に行うことができるのです:

  • テキスト・ドメインを追加したいファイルがあるフォルダーに、スクリプト add-textdomain.php をダウンロードします。
  • コマンドラインで、ファイルのあるディレクトリに移動します。
  • このコマンドを実行すると、テキスト・ドメインが追加された新しいファイルが作成されます:
php add-textdomain.php my-plugin my-plugin.php > new-my-plugin.php

add-textdomain.php を別のフォルダーに置きたい場合は、コマンドで場所を定義するだけです。

php /path/to/add-textdomain.php my-plugin my-plugin.php > new-my-plugin.php

新しいファイルを出力したくない場合は、このコマンドを使います:

php add-textdomain.php -i my-plugin my-plugin.php

ディレクトリ内の複数のファイルを変更したい場合は、スクリプトにディレクトリを渡すこともできます:

php add-textdomain.php -i my-plugin my-plugin-directory

これが終わると、ファイル内のすべての gettext 呼び出しの最後にテキスト・ドメインが追加されます。既存のテキスト・ドメインがある場合は、置き換えられません。

テキスト・ドメインの読み込み

たとえば、翻訳は、load_plugin_textdomain を使って読み込むことができます:

add_action( 'init', 'wpdocs_load_textdomain' );

function wpdocs_load_textdomain() {
  load_plugin_textdomain( 'wpdocs_textdomain', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' );
}

WordPress.org のプラグイン

WordPress 4.6以降、翻訳は translate.wordpress.org を優先するようになったので、translate.wordpress.org 経由で翻訳されるプラグインは load_plugin_textdomain() を必要としなくなりました。プラグインに load_plugin_textdomain() 呼び出しを追加したくない場合は、readme.txt の Requires at least: フィールドを4.6以上に設定する必要があります。

translate からではなく、自分の翻訳をロードしたい場合は、load_textdomain_mofile というフックフィルターを使う必要があります。

 プラグインの /languages/ ディレクトリに .mo ファイルを作成し、メインのプラグインファイルに、このコードを挿入します:

function my_plugin_load_my_own_textdomain( $mofile, $domain ) {
  if ( 'my-domain' === $domain && false !== strpos( $mofile, WP_LANG_DIR . '/plugins/' ) ) {
    $locale = apply_filters( 'plugin_locale', determine_locale(), $domain );
    $mofile = WP_PLUGIN_DIR . '/' . dirname( plugin_basename( __FILE__ ) ) . '/languages/' . $domain . '-' . $locale . '.mo';
  }
  return $mofile;
}
add_filter( 'load_textdomain_mofile', 'my_plugin_load_my_own_textdomain', 10, 2 );

JavaScript ファイルの取り扱い

共通 API ハンドブック の javascript の国際化 セクションをチェックして、翻訳ファイルを正しく読み込む方法を確認してください。Gutenburg プラグインのドキュメントページもあります。

言語パック

言語パックや translate.wordpress.org へのインポートに興味があるなら、翻訳についてのメタハンドブックのページを読んでください。

あなたのプロジェクトを翻訳するために Polyglots ハンドブックのプラグイン/テーマ作者ガイドも参照してください。

原文 / 日本語訳

s
検索
c
新規投稿を作成する
r
返信
e
編集
t
ページのトップへ
j
次の投稿やコメントに移動
k
前の投稿やコメントに移動
o
コメントの表示を切替
esc
投稿やコメントの編集をキャンセル