多言語アプリの構築方法:PHPとGettextを使ったデモ
ウェブサイトを構築する場合でも、本格的なwebアプリケーションを構築する場合でも、より多くのユーザーがアクセスできるようにするには、異なる言語やロケールで利用できるようにする必要があります。
ほとんどの人間の言語の基本的な違いは、これを簡単にします。 文法規則、言語のニュアンス、日付形式などの違いは、ローカライズをユニークで手ごわい課題にするために組み合わせます。
この単純な例を考えてみましょう。
英語での複数形のルールは非常に簡単です:あなたは単語の単数形または単語の複数形を持つことができます。
ただし、スラヴ語のような他の言語では、単数形に加えて2つの複数形があります。 スロベニア語、アイルランド語、アラビア語など、合計4、5、または6つの複数形を持つ言語を見つけることもできます。
コードの編成方法、コンポーネントとインターフェイスの設計方法は、アプリケーションをどのように簡単にローカライズできるかを決定する上で重要な役
コードベースの国際化(i18n)は、異なる言語や地域に比較的容易に適応できるようにするのに役立ちます。 国際化は通常、できればプロジェクトの開始時に一度行われ、ソースコードに大きな変更が必要にならないようにします。
コードベースが国際化されると、ローカライズ(l10n)は、アプリケーションの内容を特定の言語/ロケールに翻訳する問題になります。
ローカライズは、新しい言語または地域をサポートする必要があるたびに実行する必要があります。 また、インターフェイスの一部(テキストを含む)が更新されるたびに、新しいコンテンツが利用可能になり、サポートされているすべてのロケールにローカライズ(
この記事では、PHPで書かれたソフトウェアを国際化し、ローカライズする方法を学びます。 私たちは、さまざまな実装オプションとプロセスを容易にするために私たちの処分で利用可能なさまざまなツールを通過します。
国際化のためのツール
PHPソフトウェアを国際化する最も簡単な方法は、配列ファイルを使用することです。 配列には翻訳された文字列が入力され、テンプレート内から検索することができます:
<h1><?=$TRANS?></h1>
しかし、これは重大なプロジェクトのための推奨される方法ではありません。 変数補間や名詞の複数形化などのサポートの欠如など、最初の段階でいくつかの問題が発生する可能性があります。
最も古典的なツールの一つ(多くの場合、i18nとl10nの参照として取られます)Gettextと呼ばれるUnixツールです.
1995年にさかのぼりますが、それはまだ使いやすいソフトウェアを翻訳するための包括的なツールです。 使い始めるのは非常に簡単ですが、それでも強力なサポートツールがあります。
Gettextは、この記事で使用するものです。 私たちは、簡単にそれによってコマンドラインに対処する必要性を回避し、あなたのl10nソースファイルを更新するために使用することができ、素晴ら
Gettextやi18nの他の実装をサポートする主要なPHP webフレームワークやライブラリがあります。 このドキュメントでは、PHPコアで提供されているツールに焦点を当てていますが、ここでは言及する価値のある他のいくつかのリスト:
-
oscarotero/Gettext:オブジェクト指向インターフェイスでのGettextのサポート; 改善されたヘルパー関数、いくつかのファイル形式の強力な抽出器が含まれています(そのうちのいくつかは
gettext
コマンドでネイティブにサポートされてい また、.mo/.poファイルだけでなく、JavaScriptインターフェイスなど、システムの他の部分に翻訳ファイルを統合する必要がある場合に便利な形式にエクスポートする -
symfony/translation:多くの異なる形式をサポートしていますが、冗長なXLIFFの使用を推奨しています。ヘルパー関数や組み込みの抽出器は含まれていませんが、内部的に
strtr()
を使用するプレースホルダをサポートしています。 -
zend/i18n: 配列およびINIファイル、またはGettext形式をサポートします。 毎回ファイルシステムを読み取る必要がないように、キャッシュ層を実装します。 また、ビューヘルパー、ロケール対応の入力フィルタとバリデータも含まれています。 ただし、メッセージ抽出機能はありません。
他のフレームワークにもi18nモジュールが含まれていますが、それらはコードベースの外では利用できません:
-
自動抽出はありませんが、テンプレートファイル用の
@lang
ヘルパーが含まれています。 -
Yii: 配列、Gettext、およびデータベースベースの翻訳をサポートし、メッセージ抽出機能を備えています。 PHP5.3以降で利用可能な
Intl
拡張機能に支えられ、ICUプロジェクトに基づいています。 これにより、Yiiは、数字のスペルアウト、日付、時刻、間隔、通貨、序数の書式設定などの強力な置換を実行することができます。
抽出器を提供しないライブラリのいずれかを選択する場合は、gettext形式を使用して、この章の残りの部分で説明したように元のGettextツールチェーン(Poeditを含む)を
Gettextのインストール
apt-getやyumなどのパッケージマネージャーを使用して、Gettextと関連するPHPライブラリをインストールする必要がある場合があります。 インストール後、php.ini
ファイルにextension=gettext.so
(Linux/Unix)またはextension=php_gettext.dll
(Windows)を追加して有効にします。
ここでは、Poeditを使用して翻訳ファイルを作成します。 それはUnix、Mac、およびWindowsで利用可能であり、同様にそのウェブサイト上で無料でダウンロードすることができます。
Gettextファイルの種類
Gettextを操作するときに通常扱うファイルの種類が三つあります。
主なものはPO(Portable Object)ファイルとMO(Machine Object)ファイルで、最初は読み取り可能な”翻訳されたオブジェクト”のリストであり、第二は対応するバイナリです(ローカリゼーションを行うときにGettextによって解釈される)。 ソースファイルからの既存のすべてのキーを単純に含むPOT(POテンプレート)ファイルもあり、すべてのPOファイルを生成および更新するためのガイドとし
テンプレートファイルは必須ではありません; l10nを実行するために使用しているツールによっては、PO/MOファイルのみで問題ありません。 言語と地域ごとに1組のPO/MOファイルがありますが、ドメインごとに1つのPOTのみがあります。
ドメインの分離
大きなプロジェクトでは、同じ単語が異なる文脈で異なる意味を伝えるときに翻訳を分離する必要がある場合があります。
そのような場合は、それらを異なる”ドメイン”に分割する必要があります。
小規模および中規模のプロジェクト通常、簡単にするために、一つのドメインのみを使用します。
Symfonyプロジェクトでは、たとえば、検証メッセージの翻訳を分離するためにドメインが使用されます。
ロケールコード
ロケールは、単に言語のあるバージョンを識別するコードです。 これは、ISO639-1およびISO3166-1alpha-2仕様に従って定義されています:言語の2つの小文字、オプションでアンダースコアと国または地域コードを識別する2つの大文字が続きます。
まれな言語の場合、三つの文字が使用されます。
一部の話者にとっては、国の部分は冗長に見えるかもしれません。 実際には、いくつかの言語は、オーストリアドイツ語(de_at)やブラジルポルトガル語(pt_br)など、異なる国で方言を持っています。 第二の部分は、これらの方言を区別するために使用されます-それが存在しない場合、それは言語の”一般的な”または”ハイブリッド”バージョンとして取られ
ディレクトリ構造
Gettextを使用するには、フォルダの特定の構造を遵守する必要があります。
まず、ソースリポジトリ内のl10nファイルの任意のルートを選択する必要があります。 その中には、必要なロケールごとのフォルダと、すべてのPO/MOペアを含む固定の「LC_MESSAGES」フォルダがあります。
複数形
序文で述べたように、異なる言語は異なる複数形の規則をスポーツするかもしれません。 しかし、Gettextは私たちにこの問題を保存します。
新規作成時。poファイルでは、その言語の複数形のルールを宣言する必要があり、複数形に敏感な翻訳された部分は、それらのルールごとに異なる形式を持ちます。
コードでGettextを呼び出すときは、文に関連する番号を指定する必要があります(たとえば、”あなたはn個のメッセージを持っています。n)の値を指定する必要があり、必要に応じて文字列置換を使用しても、使用する正しい形式が機能します。
複数のルールは、各ルールのブール値テストで必要なルールの数で構成されます(最大で一つのルールのテストは省略できます)。 例えば:
-
日本語:
nplurals=1; plural=0;
-一つのルール:複数形はありません -
英語:
nplurals=2; plural=(n != 1);
-二つのルール:nが1でない場合にのみ複数形を使用し、それ以外の場合は単数形を使用します。 -
ブラジルポルトガル語:
nplurals=2; plural=(n > 1);
-二つのルール、nが1より大きい場合にのみ複数形を使用し、それ以外の場合は単数形を使用します。
より深い説明のために、オンラインで利用可能な有益なLingoHubのチュートリアルがあります。
Gettextは、指定された番号に基づいて使用するルールを決定し、文字列の正しいローカライズされたバージョンを使用します。 複数形を処理する必要がある文字列の場合は、に含める必要があります。poファイル定義された複数のルールごとに異なる文。
サンプル実装
すべての理論の後、少し実用的にしましょう。 ここにaの抜粋があります。poファイル(構文についてはあまり心配しないでくださいが、全体的なコンテンツの感覚を得るだけです):
msgid ""msgstr """Language: pt_BR\n""Content-Type: text/plain; charset=UTF-8\n""Plural-Forms: nplurals=2; plural=(n > 1);\n"msgid "We're now translating some strings"msgstr "Nós estamos traduzindo algumas strings agora"msgid "Hello %1$s! Your last visit was on %2$s"msgstr "Olá %1$s! Sua última visita foi em %2$s"msgid "Only one unread message"msgid_plural "%d unread messages"msgstr "Só uma mensagem não lida"msgstr "%d mensagens não lidas"
最初のセクションはヘッダーのように機能し、msgid
とmsgstr
は空です。
これは、ファイルエンコーディング、複数形、および他のいくつかのことを記述します。 第二のセクションでは、英語からブラジルポルトガル語への単純な文字列を変換し、第三のセクションでは、同じことを行いますが、sprintf
からの文字列置換を活用し、翻訳にユーザー名と訪問日を含めることができます。
最後のセクションは複数形のサンプルであり、英語では単数形と複数形をmsgid
、対応する翻訳をmsgstr
0と1として表示しています(複数形の規則で与えら
そこでは、文字列の置換も使用されるため、%d
を使用することで、文の中で数字を直接見ることができます。 複数形は常に二つのmsgid
(単数形と複数形)を持っているので、翻訳のソースとして複雑な言語を使用しないことをお勧めします。
ローカリゼーションキー
お気づきのように、実際の英語の文をソースIDとして使用しています。 そのmsgid
は、すべてのあなたを通して使用されるのと同じです。つまり、他の言語は同じ形式と同じmsgid
フィールドを持ちますが、翻訳されたmsgstr
行を持ちます。
翻訳キーといえば、ここでは二つの標準的な”哲学的”アプローチがあります:
1。 実際の文としてのmsgid
このアプローチの主な利点は次のとおりです:
-
任意の言語で翻訳されていないソフトウェアの部分がある場合、表示されるキーはまだいくつかの意味を維持します。 たとえば、英語からスペイン語への翻訳方法を知っているが、フランス語への翻訳の助けが必要な場合は、フランス語の文章が欠落している新しいページを公開し、ウェブサイトの一部が代わりに英語で表示される可能性があります。
-
翻訳者が何が起こっているのかを理解し、
msgid
に基づいて適切な翻訳を行う方がはるかに簡単です。 -
ソース言語-それはあなたに一つの言語のための”自由な”l10nを提供します。
一方、主な欠点は、実際のテキストを変更する必要がある場合、複数の言語ファイルで同じmsgid
を置き換える必要があることです。
2. 一意の構造化キーとしてのmsgid
これは、文字列がその内容の代わりに配置されているテンプレートまたは部分を含む、構造化された方法でアプリケーション内の文の役割を記述します。
これは、テキストコンテンツをテンプレートロジックから分離して、コードを整理するのに最適な方法です。 しかし、それは文脈を逃す翻訳者に問題をもたらす可能性があります。
他の翻訳の基礎としてソース言語ファイルが必要になります。 たとえば、開発者は理想的には”en”を持っています。po”ファイル、翻訳者が読むだろうことを理解するために何を書くか”fr.po”。
翻訳がないと、無意味なキーが画面に表示されます(”top_menu.ようこそ”の代わりに”こんにちはあり、ユーザー!”と言われたフランス語の未翻訳のページで)。
それは出版する前に翻訳を完全に強制するので良いことです-しかし、翻訳の問題はインターフェイスで本当にひどいでしょうとして悪いです。 しかし、一部のライブラリには、特定の言語を”フォールバック”として指定するオプションが含まれており、他のアプローチと同様の動作をしています。
Gettextマニュアルは、一般的に、トラブルが発生した場合に翻訳者やユーザーにとって簡単であるため、最初のアプローチを支持しています。 これが、ここでも使用するアプローチです。
ただし、Symfonyのドキュメントはキーワードベースの翻訳を優先し、テンプレートにも影響を与えずにすべての翻訳を独立した変更できるようにしています。
日常的な使用法
一般的なアプリケーションでは、ページに静的テキストを書き込むときにGettext関数を使用します。
これらの文は、その後に表示されます。poファイルは、翻訳され、.moファイルにコンパイルされ、実際のインターフェイスをレンダリングするときにGettextによって使用されます。 それを考えると、これまでに説明したことを段階的な例で結び付けてみましょう:
1. いくつかの異なるgettext呼び出しを含むサンプルテンプレートファイ
<?php include 'i18n_setup.php' ?><div> <h1><?=sprintf(gettext('Welcome, %s!'), $name)?></h1> <!-- code indented this way only for legibility → <?php if ($unread): ?> <h2> <?=sprintf( ngettext('Only one unread message', '%d unread messages', $unread), $unread )?> </h2> <?php endif ?></div><h1><?=gettext('Introduction')?></h1><p><?=gettext('We\'re now translating some strings')?></p>
-
gettext()
指定された言語のmsgid
を対応するmsgstr
に単純に変換します。 同じように動作する省略形関数_()
もあります -
ngettext()
同じことをしますが、複数のルールがあります -
また、
dgettext()
とdngettext()
があり、単一の呼び出しでドメインを上書きできます(次の例ではドメイン設定の詳細を参照してください)
2. サンプル設定ファイル(i18n_setup.正しいロケールを選択し、Gettext
をGettextを使用して設定するには、少し定型コードが必要ですが、主にlocalesディレクトリを設定し、適切なパラメータ(ロケールとドメイン)を選択することです。
<?php/** * Verifies if the given $locale is supported in the project * @param string $locale * @return bool */function valid($locale) { return in_array($locale, ) && valid($_GET)) { // the locale can be changed through the query-string $lang = $_GET; //you should sanitize this! setcookie('lang', $lang); //it's stored in a cookie so it can be reused} elseif (isset($_COOKIE) && valid($_COOKIE)) { // if the cookie is present instead, let's just keep it $lang = $_COOKIE; //you should sanitize this!} elseif (isset($_SERVER)) { // default: look for the languages the browser says the user accepts $langs = explode(',', $_SERVER); array_walk($langs, function (&$lang) { $lang = strtr(strtok($lang, ';'), ); }); foreach ($langs as $browser_lang) { if (valid($browser_lang)) { $lang = $browser_lang; break; } }}// here we define the global system locale given the found languageputenv("LANG=$lang");// this might be useful for date functions (LC_TIME) or money formatting (LC_MONETARY), for instancesetlocale(LC_ALL, $lang);// this will make Gettext look for ../locales/<lang>/LC_MESSAGES/main.mobindtextdomain('main', '../locales');// indicates in what encoding the file should be readbind_textdomain_codeset('main', 'UTF-8');// if your application has additional domains, as cited before, you should bind them here as wellbindtextdomain('forum', '../locales');bind_textdomain_codeset('forum', 'UTF-8');// here we indicate the default domain the gettext() calls will respond totextdomain('main');// this would look for the string in forum.mo instead of main.mo// echo dgettext('forum', 'Welcome back!');?>
3. 最初の実行のための翻訳の準備
カスタムフレームワークi18nパッケージよりもGettextが持つ大きな利点の一つは、その広範かつ強力なファイル形式です。
おそらく、あなたは”ああ、それは手で理解して編集するのは非常に難しいです、単純な配列は簡単でしょう!”間違いなく、Poeditのようなアプリケーションが助けるためにここにある-多くの。 あなたは彼らのウェブサイトからプログラムを得ることができます、それは無料で、すべてのプラ それは慣れるのが非常に簡単なツールであり、同時に非常に強力なツールです-Gettextが利用可能なすべての機能を使用しています。 ここでは、最新バージョンのPoedit1.8で作業します。
最初の実行では、メニューから”ファイル>新規…”を選択する必要があります。 あなたは言語を求められます; 翻訳したい言語を選択/フィルタリングするか、en_US
やpt_BR
などの前に述べた形式を使用します。
ここで、ファイルを保存します-私たちも言及したそのディレクトリ構造を使用します。 次に、「ソースから抽出」をクリックする必要があり、ここで抽出および翻訳タスクのさまざまな設定を構成します。 これらのすべては、後で”カタログ>プロパティ”で見つけることができます”:
-
ソースパス:
gettext()
(および兄弟)が呼び出されているプロジェクトのすべてのフォルダを含めます-これは通常、templates/viewsフォルダです。 これが唯一の必須設定です。 -
翻訳プロパティ:
- プロジェクト名とバージョン,チームとチームの電子メールアドレス:に行く有用な情報.poファイルのヘッダー。
- 複数形:これらは前に述べた規則です。 Poeditにはすでに多くの言語の複数のルールの便利なデータベースが含まれているため、ほとんどの場合、デフォルトオプションを残しておくことができます。
- 文字セット:UTF-8、できれば。
- ソースコードの文字セット:あなたのコードベースで使用される文字セット-おそらくUTF-8もそうですよね?
-
ソースキーワード:基礎となるソフトウェアは、
gettext()
と同様の関数呼び出しがいくつかのプログラミング言語でどのように見えるかを知っていますが、独自の翻訳関数を作成することもできます。 それはあなたがそれらの他の方法を追加しますここになります。 これについては、”Tips”セクションの後半で説明します。
これらのプロパティを設定した後、Poeditはソースファイルをスキャンしてすべてのローカライズ呼び出しを検索します。 すべてのスキャンの後、Poeditは何が見つかったのか、何がソースファイルから削除されたのかの概要を表示します。 新しいエントリは翻訳テーブルに空になり、それらの文字列のローカライズされたバージョンを入力できます。 それを保存すると、.moファイルが同じフォルダに(再)コンパイルされ、presto! あなたのプロジェクトは国際化されています!
Poeditはまた、webから、以前のファイルからの一般的な翻訳を提案することができます。 それは便利なので、あなたは彼らが意味をなさないかどうかを確認し、それらを受け入れるだけです。 翻訳がわからない場合は、あいまいとしてマークすることができ、黄色で表示されます。 青色のエントリは、翻訳がないものです。
4. 文字列の翻訳
お気づきかもしれませんが、ローカライズされた文字列には主に二つのタイプがあります:単純なものと複数形を持つもの。
単純なものには、ソースとローカライズされた文字列の二つのボックスしかありません。 Gettext/Poeditにはソースファイルを変更する機能が含まれていないため、ソース文字列を変更することはできません。 (先端: 翻訳行を右クリックすると、その文字列が使用されているソースファイルと行のヒントが表示されます。)
複数形の文字列には、二つのソース文字列を表示するための二つのボックスと、異なる最終形を設定できるようにタブが含まれています。
Poeditの複数形を持つ文字列の例で、それぞれの翻訳タブを示しています。
ソースコードファイルを変更し、翻訳を更新する必要があるときはいつでも、Refreshを押すとPoeditはコードを再スキャンし、存在しないエントリを削除し、変更されたものをマージし、新しいものを追加します。
Poeditはまた、あなたが行った他のものに基づいて、いくつかの翻訳を推測しようとするかもしれません。 これらの推測と変更されたエントリには、レビューが必要であることを示す”ファジー”マーカーが表示され、リストに黄色で表示されます。
あなたが翻訳チームを持っていて、誰かが彼らがわからないことを書こうとする場合にも便利です: それを曖昧にマークするだけで、他の誰かが後でそれを確認します。
最後に、”View>Untranslated entries first”のマークを残しておくことをお勧めします。 そのメニューから、必要に応じて翻訳者のためのコンテキスト情報を残すことができるUIの部分を開くこともできます。
ヒント&トリック
Webサーバーは.moファイルをキャッシュしてしまう可能性があります。
Apache(mod_php)でPHPをモジュールとして実行している場合、.moファイルがキャッシュされている問題に直面する可能性があります。 最初に読み込まれたときに発生し、それを更新するには、サーバーを再起動する必要がある場合があります。
NginxとPHP5では、通常、翻訳キャッシュを更新するのに数回のページ更新しか必要なく、PHP7ではほとんど必要ありません。
ライブラリはローカリゼーションコードを短くするためのヘルパー関数を提供します。
多くの人が好むように、gettext()
の代わりに_()
を使用する方が簡単です。 フレームワークの多くのカスタムi18nライブラリは、翻訳されたコードを短くするために、t()
に似たものを使用します。 しかし、それはショートカットを遊ばす唯一の機能です。
プロジェクトに__()
や_n()
など、ngettext()
のような他のものを追加したり、gettext()
やsprintf()
呼び出しに参加する派手な_r()
を追加したりすることができます。 OscaroteroのGettextのような他のライブラリも、これらのようなヘルパー関数を提供します。
そのような場合は、Gettextユーティリティに、これらの新しい関数から文字列を抽出する方法を指示する必要があります。 恐れてはいけない、それは非常に簡単です。 それはただのフィールドです。poファイルまたはPoeditの設定画面(エディタでは、そのオプションは”Catalog>Properties>Sources keywords”内にあります)。
: Gettextはすでに多くの言語のデフォルト関数を知っているので、そのリストが空のように見える場合は心配しないでください。 そのリストには、次の特定の形式に従って、新しい関数の仕様を含める必要があります:
-
文字列の変換を返すだけの
t()
ようなものを作成する場合は、t
として指定できます。 Gettextは、関数の引数が翻訳される文字列だけであることを知っています; -
関数に複数の引数がある場合は、最初の文字列がどれであるかを指定し、必要に応じて複数形も指定できます。 たとえば、関数シグネチャが
__('one user', '%d users', $number)
の場合、仕様は__:1,2
になり、最初の形式が最初の引数であり、2番目の形式が2番目の引数です。 代わりに番号が最初の引数として指定されている場合、仕様は__:2,3
になり、最初の形式が2番目の引数であることを示します。
にそれらの新しいルールを含めた後.poファイル、新しいスキャンは、以前と同じように簡単にあなたの新しい文字列をもたらすでしょう。
GettextでPHPアプリを多言語にする
GettextはPHPプロジェクトを国際化するための非常に強力なツールです。 多数の人間の言語をサポートできる柔軟性を超えて、20以上のプログラミング言語をサポートすることで、PHPで使用する知識をPython、Java、C#などの他の言語に簡
さらに、Poeditはコードと翻訳された文字列との間のパスを滑らかにするのに役立ち、プロセスをより簡単かつ簡単にすることができます。 また、Crowdinの統合により、共有翻訳作業を合理化することができます。
可能な限り、ユーザーが話す可能性のある他の言語を検討してください。 これは主に英語以外のプロジェクトで重要です:あなたは英語だけでなく、あなたの母国語でそれをリリースした場合、あなたのユーザーアクセスを高める
もちろん、すべてのプロジェクトが国際化の必要性を持っているわけではありませんが、プロジェクトの初期段階でi18nを開始する方が、最初 また、GettextやPoeditのようなツールを使用すると、これまで以上に簡単になります。