Skip to content
Caution: You are browsing the legacy symfony 1.x part of this website.

第8章 - 国際化とローカライズ

Symfony version
Language

人気のあるWebアプリケーションの多くはさまざまな言語で利用可能で、時にユーザーのcultureに基づいてカスタマイズされます。symfonyはこれらの機能の管理を楽にする組み込みのフレームワークを搭載しています(symfony bookの"国際化と ローカライズ"の章を参照)。

フォームフレームワークはユーザーのインターフェイス用の組み込みサポートを備えており国際化オブジェクトの管理を楽にする方法を提供します。

フォームの国際化

symfonyのフォームはデフォルトで国際化が可能です。翻訳ファイルを編集することでラベルヘルパーテキスト、とエラーメッセージを翻訳できます。これらはXLIFFgettext、もしくは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の国際化フレームワークの機能を利用する場合 (/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');
  }
}

note

カタログを利用することで例えばフォームごとに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をカプセル化します。

note

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

翻訳の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;
  }
}

sidebar

翻訳処理をカスタマイズするのになぜsfWidgetFormSchemaFormatterを使うのか?

2章で見てきたように、フォームフレームワークはMVCアーキテクチャに基づきsfWidgetFormSchemaFormatterクラスはビューレイヤーに所属します。このクラスはすべてのテキストのレンダリングに関与するので、すべてのテキストの文字列をインターセプトしてこれらを即時に翻訳します。

Propelのオブジェクトの国際化

フォームフレームワークは国際化された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のフォーム

国際化されたPropelのフォーム

これでお終いです。フォームオブジェクトのsave()メソッドを呼び出すとき、関連するPropelオブジェクトと国際化オブジェクトは自動的に保存されます。

sidebar

ユーザーの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_name
    • 月の番号を表示するためのnumber(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));

cultureセレクタ

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));

This work is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported License license.