symfony book 日本語ドキュメント

第13章 - 国際化とローカライゼーション

国際的なアプリケーションを開発したことがあるのであれば、テキストの翻訳、ローカルスタンダードとローカライズされた内容のあらゆる面を処理することが悪夢であることはご存じでしょう。幸いにして、symfonyは国際化のすべての面をネイティブに自動化します。

国際化(internationalization)は長い単語なので、開発者はしばしばi18nと表記します(単語の文字数を数えてみればなぜなのかわかります)。ローカライゼーション(localization - 現地化)はl10nと短縮されます。これらは多言語のWebアプリケーションの2つの異なる面をカバーします。

国際化されたアプリケーションはさまざまな言語もしくはフォーマットで同じ内容のいくつかのバージョンを含みます。たとえば、Webメールのインターフェイスはインターフェイスを変更するだけでいくつもの言語で同じサービスを提供できます; インターフェイスだけ変わります。

ローカライズされたアプリケーションはブラウザーから得られた国の情報にしたがって相異なる情報を含みます。ポータルニュースを考えてみましょう。アメリカからブラウザーで見たとき、アメリカについての最新のヘッドラインを表示します。しかし、フランスからブラウザーで見たとき、ヘッドラインはフランスのニュースに関するものです。ローカライズされたアプリケーションは翻訳内容を提供するだけでなく、1つのローカライズされたバージョンから別のバージョンまで異なった内容を提供します。

全体で、国際化とローカライゼーションを扱うことはアプリケーションがつぎの内容を担当することを意味します:

この章ではsymfonyはこれらの要素と国際化とローカライズされたアプリケーションを開発するためにsymfonyを使う方法をカバーします。

ユーザーのculture

symfonyにおけるすべての組み込みの国際化機能はcultureと呼ばれるユーザーセッションのパラメーターに基づいています。cultureはユーザーの国と言語の組み合わせで、テキストとcultureに依存する情報を表示する方法を決定します。これはユーザーセッションのなかでシリアライズされるので、cultureはページのあいだで持続します。

デフォルトのcultureを設定する

デフォルトでは、新しいユーザーのcultureはdefault_cultureです。リスト13-1で示されるように、この設定をi18n.yml設定ファイルのなかで変更できます。

リスト13-1 - デフォルトのcultureを設定する(myapp/config/i18n.yml)

all:
  default_culture:     fr_FR

NOTE 開発期間において、i18n.ymlファイルのなかのcultureを変更してもブラウザーの現在のcultureが変更されないことに驚くかもしれません。セッションが前のページからすでにcultureを持っているからです。新しいデフォルトのcultureを持つアプリケーションを見たい場合、ドメインCookieをクリアする、もしくはブラウザーを再起動する必要があります。

言語と国の両方をcultureに保存しておくことが必要です。なぜなら、フランス、ベルギー、もしくはカナダからの異なったフランス語の翻訳、スペイン、もしくはメキシコからの異なるスペイン語の内容が存在するかもしれないからです。言語はISOの639-1標準規格にしたがって小文字の2文字表記されます(たとえばenは英語)。国はISOの3166-1標準規格にしたがって大文字の2文字で表記されます(たとえばGBはイギリス) 。

ユーザーのためのcultureを変更する

ユーザーのcultureはブラウジングセッションの間に変更できます。たとえば、ユーザーがアプリケーションを英語バージョンからフランス語バージョンに切り替えることを決心したとき、ユーザーがアプリケーションにログインしたとき、ユーザーのオプションに保存されている言語が使われます。sfUserクラスがユーザーのcultureのためにゲッターメソッドとセッターメソッドを提供する理由はそういうわけです。リスト13-2はアクションでこれらのメソッドを使う方法を示しています。

リスト13-2 - アクションのなかでcultureを設定して読みとる

[php]
// cultureのセッター
$this->getUser()->setCulture('en_US');

// cultureのゲッター
$culture = $this->getUser()->getCulture();
 => en_US

SIDEBAR URLのなかのculture

symfonyのローカライゼーションと国際化機能を使うとき、ページは単独のURLに対して異なるバージョンを持つ傾向にあります。これはすべてユーザーセッション次第です。このことによってページが検索エンジンにキャッシュされる、もしくはインデックスに登録されることを防ぎます。

1つの解決方法はcultureがすべてのURLに表示されるようにすることで、翻訳されたページは外部の世界に対して異なるURLとして見なされます。これを行うには、:sf_cultureトークンをアプリケーションのrouting.ymlのすべてのルールに追加します:

page:
  url: /:sf_culture/:page
  requirements: { sf_culture: (?:fr|en|de) }
  params: ...

article:
  url: /:sf_culture/:year/:month/:day/:slug
  requirements: { sf_culture: (?:fr|en|de) }
  params: ...

すべてのlink_to()内でsf_cultureリクエストパラメーターを手動で設定することを避けるために、自動的にsymfonyはユーザーのcultureをデフォルトのルーティングパラメーターに追加します。これは内部でも機能します。sf_cultureパラメーターがURLのなかで見つかる場合、自動的にユーザーのcultureを変更するからです。

cultureを自動的に決定する

多くのアプリケーションにおいて、ユーザーのcultureはブラウザーのオプションに基づいた最初のリクエストで定義されます。ユーザーはブラウザーで認められた言語のリストを定義することが可能で、HTTPのAccept-Languageヘッダーにおいて、各リクエストによってこのデータはサーバーに送信されます。sfRequestオブジェクトを通してこれをsymfony内部で読みとることができます。たとえば、アクションのなかでユーザーの選択言語のリストを取得するには、つぎのコードを記入します:

[php]
$languages = $this->getRequest()->getLanguages();

HTTPのヘッダーは文字列ですが、symfonyは自動的にこれを解析して配列に変換します。前の例ではユーザーの選択言語は$languages[0]でアクセスできます。

サイトのホームページもしくはすべてのページに対するフィルター内でユーザーのcultureを選択されたブラウザーの言語に自動的に設定するために便利です。

CAUTION HTTPのAccept-Languageヘッダーはあまり信用できる情報ではありません。ユーザーがブラウザーでそれを修正する方法をほとんど知らないからです。多くの場合、選択されたブラウザー言語はインターフェイスの言語で、ブラウザーはすべての言語で利用可能ではありません。ブラウザーが選択した言語にしたがってcultureを自動的に設定することを決める場合、かならず代わりの言語を選択する方法をユーザーに提供してください。

標準規格とフォーマット

Webアプリケーションの内部は文化の特殊性について考慮していません。たとえば、データベースはデータ、量などを保存する国際標準規格を使います。しかし、データがユーザーから送信されるもしくは読みとられる場合、変換を行う必要があります。ユーザーはタイムスタンプを理解しませんし、Frenchの代わりに母国語のFrançaisと表記することを望みます。ユーザーのcultureに基づいて、自動的に変換を行うための助けが必要です。

ユーザーのcultureでデータを出力する

いったんcultureが定義されると、これに依存するヘルパーは自動的に適切な出力を持ちます。リスト13-3で示されるように、たとえば、format_number()ヘルパーは自動的にユーザーが慣れ親しんでいる書式で数字を表示します。

リスト13-3 ユーザーのcultureに合わせて数字を表示する

[php]
<?php use_helper('Number') ?>

<?php $sf_user->setCulture('en_US') ?>
<?php echo format_number(12000.10) ?>
 => '12,000.10'

<?php $sf_user->setCulture('fr_FR') ?>
<?php echo format_number(12000.10) ?>
 => '12 000,10'

明示的にcultureをヘルパーに渡す必要はありません。ヘルパーは現在のセッションオブジェクトのなかでこれら自身でcultureを探します。リスト13-4は出力に対してユーザーのcultureを考慮しているヘルパーの一覧です。

リスト13-4 - cultureに依存するヘルパー

[php]
<?php use_helper('Date') ?>

<?php echo format_date(time()) ?>
 => '9/14/06'

<?php echo format_datetime(time()) ?>
 => 'September 14, 2006 6:11:07 PM CEST'

<?php use_helper('Number') ?>

<?php echo format_number(12000.10) ?>
 => '12,000.10'

<?php echo format_currency(1350, 'USD') ?>
 => '$1,350.00'

<?php use_helper('I18N') ?>

<?php echo format_country('US') ?>
 => 'United States'

<?php format_language('en') ?>
 => 'English'

<?php use_helper('Form') ?>

<?php echo input_date_tag('birth_date', mktime(0, 0, 0, 9, 14, 2006)) ?>
 => input type="text" name="birth_date" id="birth_date" value="9/14/06" size="11" />

<?php echo select_country_tag('country', 'US') ?>
 => <select name="country" id="country"><option value="AF">アフガニスタン</option>
      ...
      <option value="GB">イギリス</option>
      <option value="US" selected="selected">アメリカ合衆国</option>
      <option value="UM">合衆国領有小離島</option>
      <option value="UY">ウルグアイ</option>
      ...
    </select>

日付ヘルパーはcultureから独立した表示を強制する追加のフォーマットパラメーターを受けとることができますが、アプリケーションが国際化されている場合は使うべきではありません。

ローカライズされた入力からデータを取得する

データをユーザーのcultureに表示することが必要な場合、データを読みとることに関しては、可能なかぎり、すでに国際化されたデータを入力するようにアプリケーションのユーザーを後押しすべきです。このアプローチによって変化するフォーマットと不確定な地域によってデータを変換する方法を理解しなくてもすみます。たとえば、入力ボックスにコンマで区切られた通貨の値を入力しようとする人はいないでしょう。

実際のデータを隠す(select_country_tag()など)、もしくは複雑なデータの異なるコンポーネントをいくつかのシンプルな入力に分離することで、ユーザーの入力フォーマットをまとめることができます。

日付に関しては、しかしながら、これは利用できないことがよくあります。ユーザーは自身の文化上の書式で日付を入力することに慣れており、このようなデータは内部(と国際化)書式に変換できるようにする必要があります。これがsfI18Nクラスを適用する事例です。リスト13-5はこのクラスの使いかたを示しています。

リスト13-5 - アクションでローカライズされた書式からデータを取得する

[php]
$date= $this->getRequestParameter('birth_date');
$user_culture = $this->getUser()->getCulture();

// タイムスタンプを取得する
$timestamp = sfI18N::getTimestampForCulture($date, $user_culture);

// 構造化データを取得する
list($d, $m, $y) = sfI18N::getDateForCulture($date, $user_culture);

データベース内のテキスト情報

ローカライズされたアプリケーションはユーザーのcultureにしたがって異なる内容を提供します。たとえば、オンラインショップは製品を同じ価格で世界中に提供できますが、国ごとにカスタマイズされた説明が付属します。このことは、データベースは異なるバージョンのデータの任意の部分を保存できなければならず、そのためには、特定の方法でスキーマを設計しローカライズされたモデルオブジェクトを操作するたびにcultureを使う必要があります。

ローカライズされたスキーマを作成する

ローカライズされたいくつかのデータを含むそれぞれのテーブルに対して、テーブルを2つの部分に分割すべきです; 1つのテーブルは国際化カラムを持たず、一方のテーブルは国際化カラムだけを持ちます。2つのテーブルは一対多のリレーションによってリンクされます。モデルを変更しないことを求められたときに、このセットアップによってより多くの言語を追加できるようになります。たとえばProductテーブルを使うことを考えてみましょう。

最初に、リスト13-6で示されているように、テーブルをschema.ymlファイルのなかに作ります。

リスト13-6 - 国際化データのためのスキーマのサンプル(config/schema.yml)

my_connection:
  my_product:
    _attributes: { phpName: Product, isI18N: true, i18nTable: my_product_i18n }
    id:          { type: integer, required: true, primaryKey: true, autoincrement: true }
    price:       { type: float }

  my_product_i18n:
    _attributes: { phpName: ProductI18n }
    id:          { type: integer, required: true, primaryKey: true, foreignTable: my_product, foreignReference: id }
    culture:     { isCulture: true, type: varchar, size: 7, required: true, primaryKey: true }
    name:        { type: varchar, size: 50 }

最初のテーブル内のisI18N属性とi18nTable属性と、2番目のテーブル内の特別なcultureカラムに注目してください。これらすべてはsymfony固有のPropelの強化機能です。

symfonyの自動化によってこれを書くことがはるかに速くなります。国際化したデータを含むテーブルがプレフィックスとして_i18nを持つメインのテーブルと同じ名前を持ち、それらが両方のテーブルでidという名前のカラムに関連している場合、メインテーブルに対するi18n属性と同様に、_i18nテーブルにおいてidカラムとcultureカラムを省略できます; symfonyはこれらを推察します。このことはsymfonyはリスト13-7にあるスキーマをリスト13-6のスキーマと同じものとみなすということを意味します。

リスト13-7 - 省略形式の、国際化したデータのためのスキーマのサンプル(config/schema.yml)

my_connection:
  my_product:
    _attributes: { phpName: Product }
    id:
    price:       float
  my_product_i18n:
    _attributes: { phpName: ProductI18n }
    name:        varchar(50)

生成された国際化オブジェクトを使う

いったん対応するオブジェクトモデルがビルドされると(symfonypropel-build-modelを呼び出し、それぞれのschema.ymlを修正したあとで、symfony ccでキャッシュをクリアすることを忘れないでください)、リスト13-8で示されるように、あたかも1つのテーブルしか存在しないかのように、国際化のサポートをするProductクラスを使うことができます。

リスト13-8 - 国際化オブジェクトを処理する

[php]
$product = ProductPeer::retrieveByPk(1);
$product->setCulture('fr');
$product->setName('Nom du produit');
$product->save();

$product->setCulture('en');
$product->setName('Product name');
$product->save();

echo $product->getName();
 => 'Product name'

$product->setCulture('fr');
echo $product->getName();
 => 'Nom du produit'

国際化オブジェクトを使うたびにcultureの変更を忘れる必要がないことが望ましいのであれば、オブジェクトクラスのhydrate()メソッドも変更できます。リスト13-9で例をご覧ください。

リスト13-9 - cultureを設定するためにhydrate()メソッドをオーバーライドする(myproject/lib/model/Product.php)

[php]
public function hydrate(ResultSet $rs, $startcol = 1)
{
  $this->setCulture(sfContext::getInstance()->getUser()->getCulture());

  return parent::hydrate($rs, $startcol);
}

ピアオブジェクトを持つクエリに関しては、リスト13-10で示されるように、通常のdoSelectの代わりに、doSelectWithI18nメソッドを使うことで現在のcultureのための翻訳内容を持つオブジェクトへの結果を制限できます。加えて、このメソッドは同時に通常のオブジェクトに関して関連する国際化オブジェクトを作成します。結果として全内容を取得するクエリの数を減らすことになります(パフォーマンスにおけるこのメソッドのプラスの影響に関する詳細な情報は18章を参照)。

リスト13-10 - 国際化されたCriteriaでオブジェクトを取得する

[php]
$c = new Criteria();
$c->add(ProductPeer::PRICE, 100, Criteria::LESS_THAN);
$products = ProductPeer::doSelectWithI18n($c, $culture);
// $culture引数はオプション
// cultureが与えられていない場合現在のユーザーのcultureが使われる

ですので、基本的には、国際化オブジェクトを直接処理する必要は決してありませんが、代わりに通常のオブジェクトを持つクエリを行うたびにcultureをモデルに渡します(もしくは推測させます)。

インターフェイスの翻訳

ユーザーのインターフェイスは国際化アプリケーションに適用させる必要があります。テンプレートは、同じプレゼンテーションによってですが、いくつかの言語でラベル、メッセージ、とナビゲーションを表示できます。symfonyは、デフォルトの言語でテンプレートを開発し、その上でテンプレート内部で使われるフレーズのための翻訳文を辞書ファイルで提供することを推奨します。そういうわけで、翻訳内容を修正、追加、もしくは削除するたびにテンプレートを変更する必要はありません。

設定の翻訳

テンプレートはデフォルトでは翻訳されません。このことは、リスト13-11で示されるように、ほかのすべてに先だってsettings.ymlファイルでテンプレート翻訳機能を有効にする必要があることを意味します。

リスト13-11 - インターフェイス翻訳を有効にする(myapp/config/settings.yml)

all:
  .settings:
    i18n: on

翻訳ヘルパーを使う

英語がデフォルトの言語で、英語とフランス語のサイトを作りたい場合を例に挙げてみましょう。サイトを翻訳することを考えるまえに、おそらくはリスト13-12の例で示されるようなテンプレートを書くでしょう。

リスト13-12 - 単独の言語テンプレート

[php]
Welcome to our website. Today's date is <?php echo format_date(date()) ?>

テンプレートの語句を翻訳するsymfonyのために、これらの語句は翻訳される文章として認識されなければなりません。これが__()ヘルパー(2つのアンダースコア)、国際化ヘルパーグループのメンバーの目的です。ですので、すべてのテンプレートは翻訳するためにこのような関数呼び出し内部で語句を閉じている必要があります。リスト13-12は、たとえば、リスト13-13のように修正できます(この章の後のほうの"複雑な翻訳ニーズを扱う"のセクションでご覧頂けます。そこではこの例の翻訳ヘルパーを呼び出す方法より優れた方法があります) 。

リスト13-13 - 多言語の準備ができているテンプレート

[php]
<?php use_helper('I18N') ?>

<?php echo __('Welcome to our website.') ?>
<?php echo __("Today's date is ") ?>
<?php echo format_date(date()) ?>

TIP アプリケーションがすべてのページに対して国際化ヘルパーグループを使う場合、それぞれのテンプレートに対してuse_helper('I18N')を繰り返すことを避けるために、ヘルパーをsettings.ymlファイルのstandard_helpers設定に含めることはおそらくよいアイディアです。

辞書ファイルを使う

__()関数が呼び出されるたびに、symfonyは現在のユーザーのcultureの辞書で引数の翻訳を探します。対応する語句が見つかったら、翻訳が送り戻され、レスポンスに表示されます。ですのでユーザーのインターフェイスの翻訳は辞書ファイルに依存します。

辞書ファイルはmessages.[language code].xmlにしたがって名づけられたXLIFF(XML Localization Interchange File Format)で書かれており、アプリケーションのi18n/ディレクトリに保存されます。

XLIFFはXMLに基づいた標準フォーマットです。よく知られているように、Webサイトにおいてテキストを参照し翻訳するためにサードパーティの翻訳ツールを利用できます。翻訳会社はこのようなファイルの扱いかたや、新しいXLIFFフォーマットの翻訳文を追加することでサイト全体を翻訳する方法を知っています。

TIP XLIFF標準規格に加え、symfonyは辞書のためにいくつかのほかの翻訳用のバックエンドツールも提供します: gettext、MySQL、SQLite、とCreoleです。これらのバックエンドツールを設定することに関する詳細な情報はAPIドキュメントを参照してください。

リスト13-14はリスト13-13をフランス語に翻訳するために必要なmessages.fr.xmlファイルのなかでXLIFF構文の例を示しています。

リスト13-14 - XLIFF辞書(myapp/i18n/messages.fr.xml)

[xml]
<?xml version="1.0" ?>
<xliff version="1.0">
  <file orginal="global" source-language="en_US" datatype="plaintext">
    <body>
      <trans-unit id="1">
        <source>Welcome to our website.</source>
        <target>Bienvenue sur notre site web.</target>
      </trans-unit>
      <trans-unit id="2">
        <source>Today's date is </source>
        <target>La date d'aujourd'hui est </target>
      </trans-unit>
    </body>
  </file>
</xliff>

source-language属性はつねにあなたのcultureのすべてのISOコードを含まなければなりません。それぞれの翻訳は一意的なid属性を持つtrans-unitタグで書きます。

デフォルトのユーザーのcultureによって(en_USに設定します)、語句は翻訳されず、__()の生の引数呼び出しが表示されます。リスト13-13の結果はリスト13-12に似ています。しかしながら、cultureがfr_FRもしくはfr_BEに変更された場合、リスト13-15のようなmessages.fr.xmlファイルからの翻訳が代わりに表示されます。

リスト13-15 - 翻訳されたテンプレート

[php]
Bienvenue sur notre site web. La date d'aujourd'hui est
<?php echo format_date(date()) ?>

追加翻訳が必要な場合、同じディレクトリに新しいmessages.XX.xml翻訳ファイルを追加するだけです。

辞書を管理する

messages.XX.xmlファイルが長すぎて読みにくくなった場合、つねに翻訳をテーマによって名づけられたいくつかの辞書ファイルに分割することができます。たとえば、messages.fr.xmlファイルをアプリケーションのi18n/ディレクトリのなかでつぎの3つのファイルに分割できます:

翻訳がデフォルトのmessages.XX.xmlファイルですぐに見つからないのであれば、3番目の引数を使う__()ヘルパーを呼び出すたびにどの辞書を使うのか宣言しなければならないことに注意してください。たとえば、navigation.fr.xmlの辞書で翻訳された文字列を出力するには、つぎのように書きます:

[php]
<?php echo __('Welcome to our website', null, 'navigation') ?>

翻訳辞書を編成する別の方法はモジュールによって分割することです。アプリケーション全体に対して単独のmessages.XX.xmlファイルを書く代わりに、modules/[module_name]/i18n/ディレクトリごとに1つのモジュールを書けます。このことによってアプリケーションからモジュールをより独立したものにします。プラグイン(17章を参照)といったものを再利用したい場合に必要です。

翻訳が必要なそのほかの要素を扱う

つぎの項目は翻訳が必要かもしれないそのほかの要素です:

複雑な翻訳ニーズを扱う

翻訳は__()の引数が完全な文である場合のみ意味をなします。しかしながら、単語と混ざったフォーマットもしくは変数を持つ場合、文をいくつかのチャンク(塊)に分割したくなることがありますが、ヘルパーを無意味な文に呼び出す結果になります。幸いにして、__()ヘルパーはトークン(字句)に基づいた置き換え機能を提供します。この機能は翻訳者がより扱いやすい意味にある辞書を持つための助けになります。HTMLのフォーマッティングと同様に、ヘルパー呼び出しで同じようにトークンをそのままにできます。リスト13-16は例を示しています。

リスト13-16 - コードを含むセンテンスを翻訳する

[php]
// 基本例
Welcome to all the <b>new</b> users.<br />
There are <?php echo count_logged() ?> persons logged.

// テキストの翻訳機能を有効にするためのわるい方法
<?php echo __('Welcome to all the') ?>
<b><?php echo __('new') ?></b>
<?php echo __('users') ?>.<br />
<?php echo __('There are') ?>
<?php echo count_logged() ?>
<?php echo __('persons logged') ?>

// テキストの翻訳機能を有効にするためのよい方法
<?php echo __('Welcome to all the <b>new</b> users') ?> <br />
<?php echo __('There are %1% persons logged', array('%1%' => count_logged())) ?>

この例では、トークンは%1%ですが、決して何でもよいわけではありません。 なぜなら翻訳ヘルパーによって使われる置き換え関数がstrtr()だからです。

翻訳に関する共通問題の1つは複数形の使いかたです。結果の数に応じて、テキストは変わりますが、言語に従った同じ方法では変わりません。たとえば、リスト13-16の最後のセンテンスはcount_logged()0もしくは1を返す場合には正しくはありません。この関数の戻り値をテストし、使うセンテンスがどれなのかを選択できますが、これはたくさんのコードが必要になることを意味します。加えて、異なる言語は異なる文法のルールを持ち、複数形の語形変化のルールはとても複雑になる場合があります。この問題は非常にありふれたものなので、symfonyは、format_number_choice()と呼ばれる、この問題を処理するヘルパーを提供します。リスト13-17はこのヘルパーを使う方法を示しています。

リスト13-17 - パラメーターの値に依存するセンテンスを翻訳する

[php]
<?php echo format_number_choice(
  '[0]Nobody is logged|[1]There is 1 person logged|(1,+Inf]There are %1% persons logged', array('%1%' => count_logged()), count_logged()) ?>

最初の引数はテキストの複数の選択肢です。2番目の引数は(__()ヘルパーに関する)置き換えパターンのオプションです。3番目の引数はどのテキストが取られるのかを決めるテスト上の数字です。

つぎのような構文を使うことで、メッセージ/文字列の選択肢は許可される値の配列の後に続くパイプ(|)文字によって分離されます:

角かっこと丸かっこの区切り子の空ではない組み合わせが許容されます。

翻訳機能を適切に機能させるにはメッセージをXLIFFファイルに明示的に表示しなければなりません。リスト13-18は例を示しています。

リスト13-18 - format_number_choice()の引数に対するXLIFFの辞書

...
<trans-unit id="3">
  <source>[0]Nobody is logged|[1]There is 1 person logged|(1,+Inf]There are %1% persons logged</source>
  <target>[0]Personne n'est connecté|[1]Une personne est connectée|(1,+Inf]Il y a %1% personnes en ligne</target>
</trans-unit>
...

SIDEBAR 文字集合のための少しの説明

テンプレートで国際化された内容を処理することはしばし文字集合の問題につながります。ローカライズされた文字コードを使う場合、ユーザーがcultureを変更するたびに文字コードも変更することが必要になります。加えて、任意の文字コードで書かれたテンプレートはほかの文字コードの文字を正確に表示しません。

複数のcultureを扱うと同時に、すべてのテンプレートがUTF-8で保存され、レイアウトがこの文字コードで内容を宣言しなければならない訳はそういうわけです。つねにUTF-8で扱えば、不愉快な不意打ちに合わなくてすみますし、やっかいな問題を悩まずにすみます。

symfonyのアプリケーションはsettings.ymlファイルのなかの文字コードに関する1つのなか心的な設定に依存します。このパラメーターを変更することはすべてのレスポンスのContent-Typeヘッダーを変更することになります。

all:
  .settings:
    charset: utf-8

テンプレートの外部から翻訳ヘルパーを呼び出す

ページに表示されるテキストのすべてがテンプレートによってもたらされたものではありません。アプリケーションのほかの部分: アクション、フィルター、モデルクラスなどで、__()ヘルパーをしばし呼び出す必要があるのはそういうわけです。リスト13-19はContext Singletonを通してI18Nオブジェクトの現在のインスタンスを読みとることでアクションでヘルパーを呼び出す方法を示しています。

リスト13-19 - __()をアクションで呼び出す

[php]
$this->getContext()->getI18N()->__($text, $args, 'messages');

まとめ

ユーザーのcultureの扱いかたを理解しているのであればWebアプリケーションで国際化とローカライズする作業は苦痛をともなわずに行うことができます。ヘルパーはフォーマットされたデータを正しく出力する方法を自動的に考慮し、データベースから読みとられたローカライズされた内容はあたかもシンプルなテーブルの一部として見なされます。インターフェイスの翻訳に関しては、__()ヘルパーとXLIFFの辞書によって最小限の労力で最大限の結果が保証されます。