Programmer's Reference Guide

Zend_Translate のアダプタ

翻訳アダプタの使用法

次は、アダプタをコード内で使用する方法です。

例1 単一言語の PHP コードの例

  1. print "Example\n";
  2. print "=======\n";
  3. print "Here is line one\n";
  4. print "Today is the " . date("d.m.Y") . "\n";
  5. print "\n";
  6. print "Here is line two\n";

上の例の出力は、翻訳に対応していません。 おそらく実際はあなたの母国語でコードを書くでしょう。 翻訳しなければならないのは、普通は出力内容だけではありません。 たとえばエラーメッセージやログメッセージなども対象となります。

次のステップは、既存のコードに Zend Translate を組み込むことです。 もちろん、あとからコードを変更するよりも 最初から翻訳を意識したコードを書くほうがずっと簡単です。

例2 多言語対応の PHP コードの例

  1. $translate = new Zend_Translate('gettext', '/my/path/source-de.mo', 'de');
  2. $translate->addTranslation('/path/to/translation/fr-source.mo', 'fr');
  3.  
  4. print $translate->_("Example") . "\n";
  5. print "=======\n";
  6. print $translate->_("Here is line one") . "\n";
  7. printf($translate->_("Today is the %1\$s") . "\n", date('d.m.Y'));
  8. print "\n";
  9.  
  10. $translate->setLocale('fr');
  11. print $translate->_("Here is line two") . "\n";

では、何が起こっているのか、そしてどうやって Zend_Translate をコードに組み込むのかについて もうすこし詳しく見ていきましょう。

新しい Zend_Translate オブジェクトを作成し、もととなるアダプタを定義します。

  1. $translate = new Zend_Translate
  2.     'gettext',
  3.     '/path/to/translation/source-de.mo',
  4.     'de'
  5. );
この例では、 Gettext アダプタ を使うことにしました。 source-de.mo というファイルを /path/to/translation に置いています。 この gettext ファイルにはドイツ語の翻訳が含まれています。 また、その後で別途フランス語のファイルも追加しています。

次に行うのは、翻訳対象の文字列をすべてラップすることです。 一番シンプルな手法は、このように文字列や文章を囲むことです。

  1. print $translate->_("Example") . "\n";
  2. print "=======\n";
  3. print $translate->_("Here is line one") . "\n";
中には翻訳する必要のない文字列もあるでしょう。 区切り線などは、たとえ言語が何であっても同じです。

データの値を翻訳文字列に組み込むこともできます。 この場合は埋め込みパラメータを使用します。

  1. printf($translate->_("Today is the %1\$s") . "\n", date("d.m.Y"));
print() の代わりに printf() 関数を使用し、 すべてのパラメータを %1\$s のように置き換えます。 最初のパラメータが %1\$s、その次が %2\$s、 といったようになります。 これにより、実際の値を知らなくても翻訳を進めることができます。 今回の例では、日付は常に今日の日付になります。 しかし、文字列を翻訳する際には、実際の日付が何であるかを知る必要はありません。

各文字列は、メッセージ ID によって識別します。 文字列の代わりに、コード内でメッセージ ID を指定することもできます。 その場合は、このようになります。

  1. print $translate->_(1) . "\n";
  2. print "=======\n";
  3. print $translate->_(2) . "\n";
しかし、この方法にはいくつか欠点があります。

コードを見ただけでは、そこでどんな内容が出力されるのかがわかりません。

また、文字列の一部が翻訳されていない場合にも問題が起こるでしょう。 翻訳の動作原理について考えてみましょう。 まず Zend_Translate は、指定した ID あるいは文字列に対応する翻訳が その言語に存在するかどうかを探します。 翻訳文字列が見つからない場合は、Zend_Locale で定義されているその次の言語の翻訳を探します。 つまり "de_AT" の場合は "de" のみで探します。 "de" の翻訳も見つからない場合は、 もとのメッセージをそのまま返します。 このようにして、たとえ翻訳文字列がなくても何らかの出力を得ることになっています。 Zend_Translate は、文字列の翻訳の際に エラーや例外を発生させることはありません。

翻訳ソースの構造

次に行うのは、翻訳したい言語用の翻訳ソースを作成することです。 それぞれのアダプタには個別の方法があるので、それをここで説明します。 その前に、すべてのアダプタに共通する一般的な事項について説明しておきます。

どこに翻訳ソースファイルを保存すべきなのかを知っておきましょう。 Zend_Translate では特に制限はありませんが、 以下のような構造を推奨します。

  • 単一構造のソース

    /application/
    /languages/
    /languages/lang.en
    /languages/lang.de
    /library/
    

    利点: すべての言語のソースファイルを同じディレクトリに配置できます。 関連するファイルを分割する必要がありません。

  • 言語ごとに分けた構造

    /application/
    /languages/
    /languages/en/
    /languages/en/first.en
    /languages/en/second.en
    /languages/de/
    /languages/de/first.de
    /languages/de/second.de
    /library
    

    利点: すべての言語がひとつのディレクトリにまとめられます。 各言語のチームは、ひとつのディレクトリを翻訳するだけですみます。 また、複数のファイルを透過的に使用できます。

  • アプリケーションごとに分けた構造

    /application/
    /application/languages/
    /application/languages/first.en
    /application/languages/first.de
    /application/languages/second.en
    /application/languages/second.de
    /library/
    

    利点: すべての言語のソースファイルを同じディレクトリに配置できます。 関連するファイルを分割する必要がありません。

    欠点: 同じ言語で複数のファイルを使用する場合に問題が発生します。

  • Gettext 形式の構造

    /application/
    /languages/
    /languages/de/
    /languages/de/LC_MESSAGES/
    /languages/de/LC_MESSAGES/first.mo
    /languages/de/LC_MESSAGES/second.mo
    /languages/en/
    /languages/en/LC_MESSAGES/
    /languages/en/LC_MESSAGES/first.mo
    /languages/en/LC_MESSAGES/second.mo
    /library/
    

    利点: 以前から使っている gettext 形式のソースを、 そのままの形式で使用できます。

    欠点: これまでに gettext を使ったことがない人たちにとって、 サブディレクトリの中にまたサブディレクトリという構造は不可解でしょう。

  • ファイル構造のソース

    /application/
    /application/models/
    /application/models/MyModel.php
    /application/models/MyModel.de
    /application/models/MyModel.en
    /application/controllers/
    /application/controllers/MyController.php
    /application/controllers/MyController.de
    /application/controllers/MyController.en
    /library/
    

    利点: すべてのファイルについて、翻訳ソースを関連付けられます。

    欠点: 小さな翻訳ファイルがあちこちに散らばってしまうので、翻訳が面倒です。 また、すべてのファイルに対して翻訳ソースを追加する必要があります。

Zend_Translate で最も便利なのは、単一構造か 言語ごとに分けた構造でしょう。

さあ、これでどんな構造でいくかが決まりました。 次に翻訳ソースファイルを作っていきましょう。

Array ソースファイルの作成

Array ソースファイルは、単なる配列です。 しかし、専用のツールはないので自分でそれを定義しなければなりません。 とは言え、この配列は非常にシンプルです。 コードが期待通りに動作しているかを確認するのにも最も手っ取り早いでしょう。 翻訳作業を始めるにあたっては、一般的に最適なアダプタであるといえます。

  1. $english = array(
  2.     'message1' => 'message1',
  3.     'message2' => 'message2',
  4.     'message3' => 'message3');
  5.  
  6. $german = array(
  7.     'message1' => 'Nachricht1',
  8.     'message2' => 'Nachricht2',
  9.     'message3' => 'Nachricht3');
  10.  
  11. $translate = new Zend_Translate('array', $english, 'en');
  12. $translate->addTranslation($deutsch, 'de');

リリース 1.5 以降では、配列を外部のファイルに含めることもサポートしています。 ファイル名を指定すると、Zend_Translate が自動的にそれをインクルードして配列を探します。 詳細は、次の例を参照ください。

  1. // myarray.php
  2. return array(
  3.     'message1' => 'Nachricht1',
  4.     'message2' => 'Nachricht2',
  5.     'message3' => 'Nachricht3');
  6.  
  7. // コントローラ
  8. $translate = new Zend_Translate('array', '/path/to/myarray.php', 'de');

注意: 配列を返さないファイルはインクルードできません。 また、このファイル内での出力はすべて無視されます。

Gettext ソースファイルの作成

Gettext ソースファイルは、GNU の gettext ライブラリで作成します。 あなたのコードをパースして gettext ソースファイルを作成してくれるツールが、 フリーで公開されています。このファイルは、拡張子が *.mo のバイナリファイルとなります。 ファイルを作成するためのフリーソフトウェアのひとつに » poEdit があります。これは、ファイルの作成だけでなく翻訳作業自体もサポートしています。

  1. // mo ファイルを作成し、翻訳を済ませているものとします
  2. $translate = new Zend_Translate('gettext', '/path/to/english.mo', 'en');
  3. $translate->addTranslation('/path/to/german.mo', 'de');

ご覧の通り、アダプタの使用法はまったく同じです。 違っているのはたったの一点だけ。 arraygettext になっているということです。 その他の部分は、どのアダプタを使用してもまったく同じになります。 gettext アダプタを使用する際には、 gettext の標準的なディレクトリ構造や bindtextdomain、 textdomain にこだわる必要はありません。 単にパスとファイル名をアダプタに渡せばいいのです。

注意: ソースのエンコーディングには常に UTF-8 を使用しなければなりません。そうしないと、 複数のソースエンコーディングを使用することで問題が発生します。 たとえば、あるソースファイルは ISO-8815-11 でエンコードされており、 他のファイルは CP815 でエンコードされているとしましょう。 ソースファイルのエンコーディングはひとつしか指定できないので、 どちらか一方は正しく表示されなくなります。
UTF-8 は可搬性の高いフォーマットで、全言語をサポートしています。 すべての言語で UTF-8 を使用することで、 エンコーディングの非互換性による問題をなくすことができます。

gettext エディタの多くは、 空の翻訳文字列という形式で追加情報を記録します。 そのため、gettext アダプタは空の文字列の翻訳を行いません。 これらの情報は翻訳テーブルからは削除され、かわりに getAdapterInfo() メソッドで取得できるようになります。 このメソッドは、追加された gettext ファイルすべてから取得した情報を配列で返します。 ファイル名を配列のキーとして使用します。

  1. // アダプタの情報を取得する方法
  2. $translate = new Zend_Translate('gettext', '/path/to/english.mo', 'en');
  3. print_r($translate->getAdapterInfo());

TMX ソースファイルの作成

TMX ソースファイルは、新しい業界標準です。 XML ファイルを使用しているので、どんなエディタでも読み込め、 かつ人間にも読める形式であるという利点があります。 TMX ファイルはテキストエディタで作成することもできますし、 ツールを用いて作成することもできます。しかし、 現在使用できる TMX ソースファイル作成ツールの多くは フリーソフトウェアではありません。

例3 TMX ファイルの例

  1. <?xml version="1.0" ?>
  2. <!DOCTYPE tmx SYSTEM "tmx14.dtd">
  3. <tmx version="1.4">
  4.  <header creationtoolversion="1.0.0" datatype="winres" segtype="sentence"
  5.          adminlang="en-us" srclang="de-at" o-tmf="abc"
  6.          creationtool="XYZTool" >
  7.  </header>
  8.  <body>
  9.   <tu tuid='message1'>
  10.    <tuv xml:lang="de"><seg>Nachricht1</seg></tuv>
  11.    <tuv xml:lang="en"><seg>message1</seg></tuv>
  12.   </tu>
  13.   <tu tuid='message2'>
  14.    <tuv xml:lang="en"><seg>message2</seg></tuv>
  15.    <tuv xml:lang="de"><seg>Nachricht2</seg></tuv>
  16.   </tu>
  1. $translate = new Zend_Translate('tmx', 'path/to/mytranslation.tmx', 'en');

TMX ファイルには、同一ファイルに複数の言語を含めることができます。 含まれている言語はすべて自動的に追加されるので、 addLanguage() をコールする必要はありません。

ソース中の特定の言語のみを翻訳対象としたい場合は、オプション defined_languageTRUE に設定します。このオプションを指定した場合は、 対象としたい言語を addLanguage() で明示的に指定します。このオプションのデフォルト設定は、 全言語を対象とするようになっています。

CSV ソースファイルの作成

CSV ソースファイルは、サイズが小さく可読性があります。 顧客が自分で翻訳をしたいということなら、 おそらく CSV アダプタを使用することになるでしょう。

例4 CSV ファイルの例

# csv ファイルの例
message1;Nachricht1
message2;Nachricht2
  1. $translate = new Zend_Translate('csv', '/path/to/mytranslation.csv', 'de');
  2. $translate->addTranslation('path/to/other.csv', 'fr');

CSV アダプタで使用できるオプションは、 'delimiter' と 'limit' そして 'enclosure' の 3 つです。

CSV 文字列の標準の区切り文字は ';' 記号です [1] 。 が、オプション 'delimiter' を使用すると、 別の区切り文字を使用することができます。

CSV ファイルのデフォルトの行長制限は '0' です。 この場合は、CSV ファイルの行末を自動的に検出します。 'limit' オプションに何らかの値を設定すると CSV ファイルの読み込みが高速になりますが、 その長さを超えた部分は切り捨てられます。

CSV ファイルの値を囲む文字のデフォルトは '"' です。 別の文字を使用する場合は 'enclosure' オプションを設定します。

例5 CSV ファイルの例 その 2

# csv ファイルの例
"message,1",Nachricht1
message2,"Nachricht,2"
"message3,",Nachricht3
  1. $translate = new Zend_Translate(
  2.     'csv',
  3.     '/path/to/mytranslation.csv',
  4.     'de',
  5.     array('delimiter' => ','));
  6.  
  7. $translate->addTranslation('/path/to/other.csv', 'fr');

INI ソースファイルの作成

INI ソースファイルは可読性がありますが、 翻訳以外のデータを含めるとサイズはそれほど小さくなりません。 直接顧客に変更させるようなデータの場合にも INI アダプタが使えるでしょう。

例6 INI ファイルの例

[Test]
;TestPage Comment
Message_1="Nachricht 1 (de)"
Message_2="Nachricht 2 (de)"
Message_3="Nachricht :3 (de)"
  1. $translate = new Zend_Translate('ini', '/path/to/mytranslation.ini', 'de');
  2. $translate->addTranslation('/path/to/other.ini', 'it');

INI にはいくつかの制約があります。 INI ファイルの値に非英数字が含まれる場合は、値をダブルクォート (") で囲まなければなりません。また、ini ファイルのキーとしては使えない予約語があります。 予約語には NULL, yes, no, TRUE そして FALSE があります。 値に NULL, no および FALSE を指定するとそれは "" と同じ扱いとなり、yesTRUE を指定するとそれは 1 と同じ扱いとなります。 {}|&~![()" といった文字はキーの中では使えず、 また値の中で使用すると特別な意味を持つことになります。 予期せぬ挙動を起こすようなら、このアダプタを使わないようにしましょう。

アダプタのオプション

すべてのアダプタで、オプションを使用することができます。 もちろん、アダプタによってオプションは異なります。 アダプタを作成する際に、オプションを設定することができます。 すべてのアダプタで共通のオプションは 'clear' で、これは、翻訳データを既存のものに追記するのかしないのかを指定します。 標準の動作は、新しい翻訳を既存の翻訳に追記します。 しかし、これを指定すると、既存の翻訳データをいったん消去して 新しいデータを追加します。消去されるのは選択した言語のみであり、 その他の言語は影響を受けません。

オプションを一時的に設定するには、 addTranslation($data, $locale, array $options = array()) とオプションの三番目のパラメータを指定します。 setOptions() 関数でオプションを設定することもできます。

例7 翻訳オプションの使用

  1. // ':' を、翻訳ソースファイルの区切り文字として指定します
  2. $options = array('delimiter' => ':');
  3. $translate = new Zend_Translate(
  4.     'csv',
  5.     '/path/to/mytranslation.csv',
  6.     'de',
  7.     $options);
  8.  
  9. ...
  10.  
  11. // 定義されている言語を消去し、新しい翻訳データを使用します
  12. $options = array('clear' => true);
  13. $translate->addTranslation('/path/to/new.csv', 'fr', $options);

各アダプタで使用できるオプションについて、 その使用法を以下にまとめます。

翻訳アダプタのオプション
オプション アダプタ 説明 デフォルト値
clear すべて true にすると、既に読み込んでいる翻訳を消去します。 新しい翻訳データを読み込む際に、 新しいインスタンスを作成する代わりに使用します。 false
disableNotices すべて true に設定すると、翻訳が存在しないことについての注意メッセージを無効にします。 実運用環境ではこのオプションを true に設定しなければなりません。 false
ignore すべて このプレフィックスで始まるすべてのディレクトリとファイルが、 ファイルを探す際に無視されます。この値のデフォルトは '.' で、すべての隠しファイルを無視するようになります。 この値を 'tmp' に設定すると、たとえば 'tmpImages' や 'tmpFiles' といった名前のファイルやディレクトリ (とその配下のすべてのディレクトリ) を無視します。 .
log すべて 未翻訳のメッセージや注意が書き込まれる Zend_Log のインスタンス null
logMessage all ログに書き込まれるメッセージ Untranslated message within '%locale%': %message%
logUntranslated すべて このオプションを true に設定すると、翻訳できなかったすべてのメッセージ ID が添付のログに書き込まれます。 false
scan すべて null にすると、ディレクトリ構造のスキャンを行いません。 Zend_Translate::LOCALE_DIRECTORY にすると、 ディレクトリからロケールを検出します。 Zend_Translate::LOCALE_FILENAME にすると、 ファイル名からロケールを検出します。 詳細は 自動的なソースの検出 を参照ください。 null
delimiter Csv ソースと翻訳を区切る際に使用する記号を指定します。 ;
enclosure Csv 値を囲むための文字を定義します。デフォルトはダブルクォートです。 "
length Csv CSV の行の長さの最大値を定義します。0 にすると、自動的に検出します。 0

自分でオプションを定義すれば、それをすべてのアダプタで使用することができます。 オプションを定義するには setOptions() メソッドを使用します。setOptions() には、指定したいオプションの配列を渡します。 指定したオプションがすでに存在する場合は、上書きされます。 存在しないオプションを指定した場合はアダプタは何もしないので、 必要となるであろうオプションはすべて指定しておくことができます。 アダプタが使用している既存オプションは上書きされないことに注意してください。

現在設定されているオプションを取得するには getOptions() メソッドを使用します。getOptions() をパラメータなしでコールすると、すべてのオプションを返します。 オプションのパラメータを指定した場合は、 特定のオプションの内容のみを返します。

言語の処理

複数の言語を使用する場合に便利なメソッドを紹介します。

getLocale() メソッドを使用すると、 実際に設定されている言語を取得することができます。 これは、Zend_Locale のインスタンスかロケール ID のいずれかとなります。

setLocale() メソッドは、 翻訳用の新しい標準言語を設定します。 これを使用すると、translate() に毎回オプションの言語パラメータを指定する必要がなくなります。 指定した言語が存在しない場合やその言語用の翻訳データがない場合、 もし地域指定のない言語があれば setLocale() は代わりにそれを使用しようとします。つまり、たとえば en_US の場合だと代わりに en を使用するということです。これも見つからない場合は、 例外をスローします。

isAvailable() メソッドは、 指定した言語が既に存在するかどうかを調べます。 指定した言語のデータが存在する場合に TRUE を返します。

また、getList() メソッドを使用すると、 そのアダプタに設定されている言語の一覧を配列で取得できます。

例8 アダプタの言語の処理

  1. // 現在設定されている言語を返します
  2. $actual = $translate->getLocale();
  3.  
  4. // 翻訳時にオプションのパラメータで言語を指定することができます
  5. echo $translate->_("my_text", "fr");
  6. // あるいは新しい言語を設定することもできます
  7. $translate->setLocale("fr");
  8. echo $translate->_("my_text");
  9. // 基底言語を参照します
  10. // fr_CH は fr となります
  11. $translate->setLocale("fr_CH");
  12. echo $translate->_("my_text");
  13.  
  14. // この言語が存在するかどうかを調べます
  15. if ($translate->isAvailable("fr")) {
  16.     // 存在します
  17. }

言語の自動処理

新しい翻訳ソースの追加を addTranslation() メソッドでのみ行っている場合は、自動ロケール 'auto' あるいは 'browser' を使用していれば Zend_Translate が環境にあわせて適切な言語を自動設定します。 つまり、通常は setLocale() をコールする必要はありません。 これは、自動ソース検出と組み合わせて使用しなければなりません。

ユーザのブラウザやサーバの環境に応じて、最適なロケールを探します。 詳細は、以下の例を参照ください。

例9 言語の自動検出の動作例

  1. // ブラウザから返される言語設定は次のようなものであると仮定します
  2. // HTTP_ACCEPT_LANGUAGE = "de_AT=1;fr=1;en_US=0.8";
  3.  
  4. // 例 1:
  5. // 適切な言語がみつからないので、メッセージ ID を返します
  6. $translate = new Zend_Translate(
  7.     'gettext',
  8.     'my_it.mo',
  9.     'auto',
  10.     array('scan' => Zend_Translate::LOCALE_FILENAME));
  11.  
  12. // 例 2:
  13. // 適切な言語は 'fr' となります
  14. $translate = new Zend_Translate(
  15.     'gettext',
  16.     'my_fr.mo',
  17.     'auto',
  18.     array('scan' => Zend_Translate::LOCALE_FILENAME));
  19.  
  20. // 例 3:
  21. // 適切な言語は 'de' となります。'de_AT' の代替言語は 'de' だからです
  22. $translate = new Zend_Translate(
  23.     'gettext',
  24.     'my_de.mo',
  25.     'auto',
  26.     array('scan' => Zend_Translate::LOCALE_FILENAME));
  27.  
  28. // 例 4:
  29. // 翻訳ソースとして 'it' を返し、自動設定を上書きします
  30. $translate = new Zend_Translate(
  31.     'gettext',
  32.     'my_it.mo',
  33.     'auto',
  34.     array('scan' => Zend_Translate::LOCALE_FILENAME));
  35.  
  36. $translate->addTranslation('my_ru.mo', 'ru');
  37. $translate->setLocale('it_IT');

setLocale() メソッドで言語を手動設定したら、 自動設定機能は無効となります。

自動検出を再度有効にしたい場合は、setLocale() で言語として auto を指定します。これにより、Zend_Translate の自動検出機能が再度有効になります。

Zend Framework 1.7.0 以降では、Zend_Translate はアプリケーション単位でのロケールの使用にも対応します。 そのためには、Zend_Locale のインスタンスを以下のようにレジストリに登録します。 このようにすれば、同じロケールを何度も使用したいときに 各インスタンスで毎回ロケールを設定する手間を省けます。

  1. // 起動ファイルで
  2. $locale = new Zend_Locale();
  3. Zend_Registry::set('Zend_Locale', $locale);
  4.  
  5. // リクエストされた言語が存在しない場合のデフォルト言語
  6. $defaultlanguage = 'en';
  7.  
  8. // アプリケーションのどこかで
  9. $translate = new Zend_Translate('gettext', '\my_de.mo');
  10. if (!$translate->isAvailable($locale->getLanguage())) {
  11.     // 存在しない言語をリクエストされた場合はデフォルト設定を使用します
  12.     $translate->setLocale($defaultlanguage);
  13. }
  14.  
  15. $translate->getLocale();

自動的なソースの検出

Zend_Translate は、翻訳ソースを自動的に検出することができます。 つまり、各ソースファイルを手動で宣言する必要はないということです。 そんな作業は Zend_Translate に任せてしまい、 ディレクトリ内からソースファイルを見つけさせることができるのです。

注意: 自動的なソース検出機能は、Zend Framework バージョン 1.5 以降で使用可能です。

使用法は、翻訳ソースを個別に登録していくのとほとんど同じですが、 ひとつだけ違う点があります。ファイル名の代わりに、 ソースを探すディレクトリを指定するのです。

例10 ディレクトリを指定してソースを探す

  1. // 以下のようなディレクトリ構造があることを想定しています
  2. //  /language/
  3. //  /language/de/login/login.mo
  4. //  /language/de/error/loginerror.mo
  5. //  /language/en/login/login.mo
  6. //  /language/en/error/loginerror.mo
  7.  
  8. $translate = new Zend_Translate('tmx', '/language');

Zend_Translate は、指定したディレクトリだけでなく そのサブディレクトリすべてから翻訳ソースファイルを探します。 おかげで、非常に簡単に使用できるようになっています。 しかし、Zend_Translate では ソースを含まないファイルは無視します。 また翻訳データの読み込みに失敗した場合もそのファイルを無視します。 つまり、翻訳ソースが正しい形式であることと 読み込み可能であることを確認しておく必要があります。 ファイルの形式が間違っていたり読み込みに失敗したりした場合でもエラーは発生しないからです。

注意: ディレクトリ階層の深さやその中のファイルの数によっては、 Zend_Translate の処理に長い時間がかかることもあります。

この例では TMX フォーマットを使用しており、言語の情報をソース内に含んでいます。 しかし、他のフォーマットの多くは言語の情報をファイル内に持たせることができません。 そんなソースであっても自動検索させることができます。 ただし、次に示す条件を満たす必要があります。

ディレクトリ名からの言語の取得

自動的に言語を検出させる方法のひとつは、 言語名を表すディレクトリの配下にソースファイルを配置することです。 これはもっとも簡単な方法であり、標準的な gettext の実装でも用いられています。

Zend_Translate に 'scan' オプションを指定すると、 ディレクトリ名から言語を検出させることができます。 詳細は次の例を参照ください。

例11 ディレクトリ名による言語の検出

  1. // 以下のようなディレクトリ構造があることを想定しています
  2. //  /language/
  3. //  /language/login/login_en.mo
  4. //  /language/login/login_de.mo
  5. //  /language/error/loginerror_en.mo
  6. //  /language/error/loginerror_de.mo
  7.  
  8. $translate = new Zend_Translate(
  9.     'gettext',
  10.     '/language',
  11.     null,
  12.     array('scan' => Zend_Translate::LOCALE_DIRECTORY));

注意: これが動作するのは、 ソースファイル中に言語情報を持たないフォーマットを使用している場合のみです。 たとえば TMX などでこのオプションを使用しても、無視されます。 また、このオプションを使用した場合は ファイル名による言語の自動検出は無視されます。

注意: 同じ構造のもとで複数のサブディレクトリがある場合は注意が必要です。 たとえば /language/module/de/en/file.mo のような構造を考えてみましょう。 このパスには、ロケールと検出されうる文字列が複数含まれています。 deen です。 このような場合は、ファイル名による検出を用いることを推奨します。

ファイル名からの言語の取得

言語を自動検出するもうひとつの方法は、特別なファイル名を使用することです。 ファイル名を言語名そのものにするか、あるいはその一部に言語名を含めます。 この方式を使用する場合は、初期化時に 'scan' オプションを設定する必要があります。 ファイル名のつけかたには、以下に示すようにいくつかの方法があります。

例12 ファイル名からの言語の取得

  1. // 以下のようなディレクトリ構造があることを想定しています
  2. //  /language/
  3. //  /language/login/login_en.mo
  4. //  /language/login/login_de.mo
  5. //  /language/error/loginerror_en.mo
  6. //  /language/error/loginerror_de.mo
  7.  
  8. $translate = new Zend_Translate(
  9.     'gettext',
  10.     '/language',
  11.     null,
  12.     array('scan' => Zend_Translate::LOCALE_FILENAME));

ファイル名全体

言語名そのものをファイル名にしてしまうのは一番シンプルな方法ですが、 同一ディレクトリにソースファイルがひとつだけの場合にしか使用できません。

/languages/
/languages/en.mo
/languages/de.mo
/languages/es.mo

ファイルの拡張子

もうひとつのシンプルな方法としては、 ファイル名の拡張子を用いて言語を検出させるというものがあります。 しかしこの方法にも問題があり、本来の拡張子が何であったのかがわからなくなります。

/languages/
/languages/view.en
/languages/view.de
/languages/view.es

ファイル名の一部

Zend_Translate は、 ファイル名の一部に言語名が含まれている場合にもそれを検出することができます。 しかし、この方式を使用する場合は言語名をトークンで分割する必要があります。 トークンとしてサポートされているのは、小数点 '.' かアンダーライン '_'、 あるいはハイフン '=' のいずれかです。

/languages/
/languages/view_en.mo -> 英語となります
/languages/view_de.mo -> ドイツ語となります
/languages/view_it.mo -> イタリア語となります

ロケールとして判断できる部分が複数あった場合は、 最初に見つかったものを使用します。詳細は次の例でご確認ください。

/languages/
/languages/view_en_de.mo -> 英語となります
/languages/view_en_es.mo -> 英語となり、最初のファイルを上書きします
/languages/view_it_it.mo -> イタリア語となります

3 種類のトークンのどれを用いても言語を検出することができます。 まず最初に使用するのが小数点 '.'、次に使用するのがアンダーライン '_'、そして最後に使用するのがハイフン '-' となります。 ひとつのファイル名の中に複数のトークンが用いられている場合、 トークンの優先順位の順に調べて最初に見つかったものを使用します。 詳細は次の例でご確認ください。

/languages/
/languages/view_en-it.mo -> 英語となります。'_' のほうが '-' より優先されるからです
/languages/view-en_it.mo -> イタリア語となります。'_' のほうが '-' より優先されるからです
/languages/view_en.it.mo -> イタリア語となります。'.' のほうが '_' より優先されるからです

翻訳の確認

通常は、テキストが翻訳されているかどうかを気にすることはありません。 しかし、そのテキストが翻訳されているかどうかを、ソースコードから調べたいこともあるでしょう。 そんな場合に使用するメソッドが isTranslated() です。

isTranslated($messageId, $original = false, $locale = null) の最初のパラメータには、翻訳されているかどうかを調べたいテキストを指定します。 また、オプションの三番目のパラメータには、翻訳を調べたいロケールを指定します。 オプションの二番目のパラメータで指定するのは、 その言語に完全に一致した翻訳があるのか、あるいはもう少し広い範囲の翻訳を使用するのかという内容です。 たとえば、あるテキストについて 'en' の翻訳はあるが 'en_US' の翻訳はないといった場合、 通常は 'en' の翻訳を取得することになるでしょう。しかし $original を true にしておくと、このような場合は isTranslated() は false を返すようになります。

例13 テキストの翻訳が存在するかどうかの確認

  1. $english = array(
  2.     'message1' => 'Nachricht 1',
  3.     'message2' => 'Nachricht 2',
  4.     'message3' => 'Nachricht 3');
  5.  
  6. $translate = new Zend_Translate('array', $english, 'de_AT');
  7.  
  8. if ($translate->isTranslated('message1')) {
  9.     print "'message1' の翻訳が存在します";
  10. }
  11.  
  12. if (!($translate->isTranslated('message1', true, 'de'))) {
  13.     print "'message1' は 'de' に翻訳することはできません。"
  14.         . "'de_AT' 用の翻訳しかありません";
  15. }
  16.  
  17. if ($translate->isTranslated('message1', false, 'de')) {
  18.     print "'message1' は 'de_AT' に翻訳できます。もし存在しない場合は代替として 'de' を使用できます";
  19. }

見つからなかった翻訳をログに記録する方法

大規模なサイトを管理していたり翻訳ファイルを手作業で作ったりしている場合に、 うまく翻訳ができないメッセージに悩まされることがよくあるでしょう。 Zend_Translate を使っていれば、こんなときにも簡単な解決方法があります。

そのためには、次の 2、3 のステップに従う必要があります。 まず Zend_Log のインスタンスを作成します。 そして、そのインスタンスを Zend_Translate にアタッチします。 次の例を参照ください。

例14 翻訳のログ出力

  1. $translate = new Zend_Translate('gettext', $path, 'de');
  2.  
  3. // log のインスタンスを作成します
  4. $writer = new Zend_Log_Writer_Stream('/path/to/file.log');
  5. $log    = new Zend_Log($writer);
  6.  
  7. // それを translation インスタンスにアタッチします
  8. $translate->setOptions(array(
  9.     'log'             => $log,
  10.     'logUntranslated' => true));
  11.  
  12. $translate->translate('unknown string');

これで、Untranslated message within 'de': unknown string のような注意メッセージがログに記録されるようになるでしょう。

注意: 翻訳が見つからなかったものがすべてログに記録されることに注意しましょう。 つまり、サポートしていない言語でのリクエストがユーザからあった場合は、 すべての翻訳がログに記録されるということです。また、 翻訳不能なメッセージへのリクエストは毎回ログに記録されます。 つまり、100 人の人が同じ翻訳をリクエストしたら、 100 件のログが記録されるというわけです。

この機能はメッセージのログ出力だけに使うことはできず、 同時にこの「未翻訳」メッセージを空の翻訳ファイルにアタッチします。 これを記録するには、好みの書式で書き出して先頭の "Untranslated message" を除去するログライターを自作しなければなりません。

'logMessage' オプションを設定すると、 独自のログメッセージを使用することができます。 '%message%' トークンはログメッセージ内で messageId に置き換えられ、'%locale%' トークンは 要求されたロケールに置き換えられます。 ログメッセージを自分で定義する方法については次の例を参照ください。

例15 自分で定義したログメッセージ

  1. $translate = new Zend_Translate('gettext', $path, 'de');
  2.  
  3. // log のインスタンスを作成します
  4. $writer = new Zend_Log_Writer_Stream('/path/to/file.log');
  5. $log    = new Zend_Log($writer);
  6.  
  7. // それを translation インスタンスにアタッチします
  8. $translate->setOptions(array(
  9.     'log'             => $log,
  10.     'logMessage'      => "Missing '%message%' within locale '%locale%'",
  11.     'logUntranslated' => true));
  12.  
  13. $translate->translate('unknown string');

ソースデータへのアクセス

時には、翻訳前のソースデータにアクセスしたいこともあるでしょう。 そんなときのためにふたつのメソッドを用意しています。

getMessageIds($locale = null) メソッドは、 すべてのメッセージの ID を配列で返します。

そして、getMessages($locale = null) メソッドは 翻訳前のソースを配列で返します。メッセージ ID がキー、 それに対応するデータが値となります。

どちらのメソッドについても、オプションのパラメータ $locale を指定することができます。これを指定すると、 指定した言語についての翻訳情報を返します。 このパラメータを省略した場合は、実際に設定されている言語を対象とします。 注意してほしいのは、普通はすべての言語ですべての翻訳が存在すべきであるということです。 つまり、通常はこのパラメータを指定する必要はないはずです。

さらに、getMessages() メソッドで翻訳辞書全体を返すこともできます。 その際には、疑似ロケール 'all' を指定します。 これを指定すると、追加された各ロケールについてのすべての翻訳データを返します。

注意: 注意: 追加されているロケールの数や翻訳データの量によっては、 返される配列は 非常に大きな ものとなります。

例16 アダプタでの言語の処理

  1. // すべてのメッセージ ID を返します
  2. $messageIds = $translate->getMessageIds();
  3. print_r($messageIds);
  4.  
  5. // あるいは指定した言語の ID を返します
  6. $messageIds = $translate->getMessageIds('en_US');
  7. print_r($messageIds);
  8.  
  9. // すべての翻訳データを返します
  10. $source = $translate->getMessages();
  11. print_r($source);
[1] 訳注: カンマ以外で区切ったら、もはや CSV でも何でもないのですが、 そのあたりはあまり気にしない方向で :-)

Zend_Translate のアダプタ
blog comments powered by Disqus

Select a Version

Languages Available

Components

Search the Manual