![]() |
|
symfony Forms in Action第8章 - 国際化とローカライズ |
|
You are currently reading "symfony Forms in Action" which is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported License license.

|
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License. |
人気のあるWebアプリケーションの多くはさまざまな言語で利用可能で、時にユーザーのcultureに基づいてカスタマイズされます。symfonyはこれらの機能の管理を楽にする組み込みのフレームワークを搭載しています(symfony bookの"国際化と ローカライズ"の章を参照)。
フォームフレームワークはユーザーのインターフェイス用の組み込みサポートを備えており国際化オブジェクトの管理を楽にする方法を提供します。
symfonyのフォームはデフォルトで国際化が可能です。翻訳ファイルを編集することでラベル、ヘルパーテキスト、とエラーメッセージを翻訳できます。これらはXLIFF、gettext、もしくはsymfonyがサポートするその他のフォーマットです。
リスト8-1は以前の章で開発したコンタクトフォームを示します。
リスト8-1 - コンタクトフォーム
class ContactForm extends sfForm { public function configure() { $this->setWidgets(array( 'name' => new sfWidgetFormInput(), // デフォルトのラベルは"Name" 'email' => new sfWidgetFormInput(), // デフォルトのラベルは"Email" 'body' => new sfWidgetFormTextarea(), // デフォルトのラベルは"Body" )); // emailのウィジェットのラベルを変更する $this->widgetSchema->setLabel('email', 'Email address'); } }
リスト8-2で示されるようにラベルのフランス語訳をXLIFFで定義します。
リスト8-2 - XLIFF翻訳ファイル
// apps/frontend/i18n/messages.fr.xml <?xml version="1.0" ?> <xliff version="1.0"> <file original="global" source-language="en" datatype="plaintext"> <body> <trans-unit> <source>Name</source> <target>Nom</target> </trans-unit> <trans-unit> <source>Email address</source> <target>Adresse email</target> </trans-unit> <trans-unit> <source>Body</source> <target>Message</target> </trans-unit> </body> </file> </xliff>
symfonyの国際化フレームワークの機能を利用する場合 (http://www.symfony-project.org/book/1_1/13-I18n-and-L10n#Managing%20Dictionaries)、フォームを特定のカタログにバインドできます。リスト8-3において、ContactFormフォームをcontact_formカタログに関連づけします。ですので、フォーム要素の翻訳はcontact_form.fr.xmlファイルで捜索されます。
リスト8-3 - 翻訳カタログのカスタマイズ
class ContactForm extends sfForm { public function configure() { // ... $this->widgetSchema->getFormFormatter()->setTranslationCatalogue('contact_form'); } }
カタログを利用することで例えばフォームごとに1つのファイルを使用する翻訳の編成がよりベターになります。
時々、エラーメッセージにはユーザーによって投稿された値が埋め込まれます(例えば、"Eメールアドレスのuser@domainは有効ではありません。")。これをフォームクラスの中でカスタマイズされたエラーメッセージを定義してユーザーが投稿した値を簡単に利用できることを2章ですでに見ました。これらの参照は%parameter_name%のパターンに従います。
リスト8-4はこの原則をコンタクトフォームのnameフィールドに適用する方法を示しています。
リスト8-4 - エラーメッセージの国際化
class ContactForm extends sfForm { public function configure() { // ... $this->validatorSchema['name'] = new sfValidatorEmail( array('min_length' => 2, 'max_length' => 45), array('min_length' => 'Name "%value%" must be at least %min_length% characters.', 'max_length' => 'Name "%value%" must not exceed %max_length% characters.', ), ); } }
リスト8-5で示されるようにXLIFFファイルを編集することでこれらのエラーメッセージを翻訳できます。
リスト8-5 - エラーメッセージ用のXLIFF翻訳ファイル
<trans-unit> <source>Name "%value%" must be at least %min_length% characters</source> <target>Le nom "%value%" doit comporter un minimum de %min_length% caractères</target> </trans-unit> <trans-unit> <source>Name "%value%" must not exceed %max_length% characters</source> <target>Le nom "%value%" ne peut comporter plus de %max_length% caractères</target> </trans-unit>
symfonyの国際化フレームワーク無しでsymfonyのフォームフレームワークを使いたい場合、翻訳オブジェクトを提供する必要があります。
翻訳オブジェクトはPHPの単なるcallableです。これは次の3つのものの1つです:
my_functionのような関数の名前を表す文字列
array($anObject, 'oneOfItsMethodsName')のような、クラスのインスタンスとメソッドの1つの名前への参照を伴う配列
sfCallableインスタンス。このカプセルは一貫した方法でPHPのcallableをカプセル化します。
PHPのcallableは関数もしくはメソッドのインスタンスへの参照です。これは
is_callable()関数に渡されるときにtrueを返すPHP変数でもあります。
リスト8-6で示されるクラスによって提供される独自の国際化メカニズムをすでに持つプロジェクトをマイグレートしなければならない場合を考えてみましょう。
リスト8-6 - カスタムの国際化クラス
class myI18n { static protected $default_culture = 'en'; static protected $messages = array('fr' => array( 'Name' => 'Nom', 'Email' => 'Courrier électronique', 'Subject' => 'Sujet', 'Body' => 'Message', )); static public function translateText($text) { $culture = isset($_SESSION['culture']) ? $_SESSION['culture'] : self::$default_culture; if (array_key_exists($culture, self::$messages) && array_key_exists($text, self::$messages[$culture])) { return self::$messages[$_SESSION['culture']][$text]; } return $text; } } // クラスの使い方 $myI18n = new myI18n(); $_SESSION['culture'] = 'en'; echo $myI18n->translateText('Subject'); // => display "Subject" $_SESSION['culture'] = 'fr'; echo $myI18n->translateText('Subject'); // => display "Sujet"
リスト8-7で示されるようにそれぞれのフォームはフォームの要素の国際化を管理する独自のcallableを定義できます。
リスト8-7 - フォームのための国際化メソッドをオーバーライドする
class ContactForm extends sfForm { public function configure() { // ... $this->widgetSchema->getFormFormatter()->setTranslationCallable(array(new myI18n(), 'translateText')); } }
翻訳のcallableは3つの引数を取ります:
翻訳するテキスト;
オリジナルのテキストの範囲で置き換える引数の連想配列。よくあるのはこの章で以前見たように動的な引数を置き換えるため;
テキストを翻訳するときに使うカタログの名前。
下記のコードは翻訳のcallableを呼び出すためにsfFormWidgetSchemaFormatter::translate()メソッドによって使われる呼び出しです:
return call_user_func(self::$translationCallable, $subject, $parameters, $catalogue);
self::$translationCallableは翻訳のcallableへの参照です。ですので、以前のコードは下記のものと同等です:
$myI18n->translateText($subject, $parameters, $catalogue);
下記のコードはこれらの追加の引数をサポートするMyI18nクラスのアップデートされたバージョンです:
class myI18n { static protected $default_culture = 'en'; static protected $messages = array('fr' => array( 'messages' => array( 'Name' => 'Nom', 'Email' => 'Courrier électronique', 'Subject' => 'Sujet', 'Body' => 'Message', ), )); static public function translateText($text, $arguments = array(), $catalogue = 'messages') { $culture = isset($_SESSION['culture']) ? $_SESSION['culture'] : self::$default_culture; if (array_key_exists($culture, self::$messages) && array_key_exists($messages, self::$messages[$culture] && array_key_exists($text, self::$messages[$culture][$messages])) { $text = self::$messages[$_SESSION['culture']][$messages][$text]; $text = strtr($text, $arguments); } return $text; } }
翻訳処理をカスタマイズするのになぜ
sfWidgetFormSchemaFormatterを使うのか?2章で見てきたように、フォームフレームワークはMVCアーキテクチャに基づき
sfWidgetFormSchemaFormatterクラスはビューレイヤーに所属します。このクラスはすべてのテキストのレンダリングに関与するので、すべてのテキストの文字列をインターセプトしてこれらを即時に翻訳します。
フォームフレームワークは国際化されたPropelオブジェクト用の組み込みサポート機能を持ちます。これが動作する方法を説明するために国際化されたモデルの例を見てみましょう:
propel:
article:
id:
author: varchar(255)
created_at:
article_i18n:
title: varchar(255)
content: longvarchar
次のコマンドでPropelのクラスと関連するフォームクラスを生成できます:
$ php symfony build:model
$ php symfony build:forms
これらのコマンドはsymfonyのプロジェクトディレクトリの中で次のようなファイルを生成します:
lib/
form/
ArticleForm.class.php
ArticleI18nForm.class.php
BaseFormPropel.class.php
model/
Article.php
ArticlePeer.php
ArticleI18n.php
ArticleI18nPeer.php
リスト8-8は同じフォームでフランス語と英語の記事を編集できるようにするためにArticleFormを設定する方法を示しています。
リスト8-8 - 国際化されたPropelオブジェクト用の国際化フォーム
class ArticleForm extends BaseArticleForm { public function configure() { $this->embedI18n(array('en', 'fr')); } }
リスト8-9で示されるように次のコードをconfigure()メソッドに追加することでフォームの言語ラベルをカスタマイズすることもできます。
リスト8-9 - 言語のラベルのカスタマイズ
$this->widgetSchema->setLabel('en', 'English'); $this->widgetSchema->setLabel('fr', 'French');
図8-1 - 国際化されたPropelのフォーム

これでお終いです。フォームオブジェクトのsave()メソッドを呼び出すとき、関連するPropelオブジェクトと国際化オブジェクトは自動的に保存されます。
ユーザーのcultureをフォームに渡す方法は?
フォームを現在のユーザーのcultureにバインドしたい場合、フォームを作るときに追加の
cultureオプションを渡すことができます:class articleActions extends sfActions { public function executeCreate($request) { $this->form = new ArticleForm(null, array('culture' => $this->getUser()->getCulture())); if ($request->isMethod('post') && $this->form->bindAndSave($request->getParameter('article'))) { $this->redirect('article/created'); } } }
ArticleFormクラスにおいて、optionsの配列から値を取得できます:class ArticleForm extends BaseArticleForm { public function configure() { $this->embedI18n(array($this->getCurrentCulture())); } public function getCurrentCulture() { return isset($this->options['culture']) ? $this->options['culture'] : 'en'; } }
symfonyのフォームフレームワークは国際化を"認識する"(aware)いくつかのウィジェットを搭載しています。ユーザーのcultureに従ってウィジェットをローカライズするためにこれらを使うことができます。
日付をローカライズするために利用できるウィジェットは下記の通りです:
sfWidgetFormI18nDateウィジェットは日付用の入力を表示します(日にち、月、年):
$this->widgetSchema['published_on'] = new sfWidgetFormI18nDate(array('culture' => 'fr'));
3つの異なる値を受け取るmonth_formatオプションのおかげで、月の表示フォーマットも定義できます:
name(デフォルト)short_namenumber(1から12)sfWidgetFormI18nTimeウィジェットは時間用の入力を表示します(時、分、と秒):
$this->widgetSchema['published_on'] = new sfWidgetFormI18nTime(array('culture' => 'fr'));
sfWidgetFormI18nDateTimeウィジェットは日付と時間用の入力を表示します:
$this->widgetSchema['published_on'] = new sfWidgetFormI18nDateTime(array('culture' => 'fr'));
sfWidgetFormI18nSelectCountryウィジェットは国のリストで満たされた選択ボックスを表示します。国の名前は指定された言語に翻訳されます:
$this->widgetSchema['country'] = new sfWidgetFormI18nSelectCountry(array('culture' => 'fr'));
countriesオプションのおかげで、選択ボックス内の国を制限することもできます:
$countries = array('fr', 'en', 'es', 'de', 'nl'); $this->widgetSchema['country'] = new sfWidgetFormI18nSelectCountry(array('culture' => 'fr', 'countries' => $countries));
sfWidgetFormI18nSelectLanguageウィジェットは言語の一覧で満たされた選択ボックスを表示します。言語の名前は指定された言語に翻訳されます:
$this->widgetSchema['language'] = new sfWidgetFormI18nSelectLanguage(array('culture' => 'fr'));
languagesオプションのおかげで、選択ボックスの言語を制限することもできます:
$languages = array('fr', 'en', 'es', 'de', 'nl'); $this->widgetSchema['language'] = new sfWidgetFormI18nSelectLanguage(array('culture' => 'fr', 'languages' => $languages));
If you find a typo or an error, please register and open a ticket.
If you need support or have a technical question, please post to the official user mailing-list.