CakePHPのl10nとphpのsetlocale()がかみ合わない件
setlocale()
でロケールを設定する必要が出たので、CakePHPのl10nと連動させようとしてます。ただ、残念な事にl10nとsetlocale()
が上手くかみ合わず行き詰まりました。
とりあえずの結論
l10nからは'ja'か'jpn'が取得できます。setlocale()
でロケールを日本語にするには、'ja_jp'か'ja_jp.utf-8'を指定する必要があります。つまり、CakePHPが自動判別したCakePHPの設定をそのまま使って、setlocale()
でphpのロケールを設定する事は出来ないようです。
日本語は基本的に日本でしか使われて無いので、ja_jpは冗長で、CakePHPでは略されてる様です。逆に例えば英語では、CakePHPでも'en_au'や'en_bz'など10個ほど設定が用意されています。なので、日本語と同じように国名コードの方が省略される言語の場合に、setlocale()
に使えないという事になります。
CakePHPの言語設定
CakePHPアプリでの言語は、基本的にConfigure::write('Config.language', 'ja');
の様に設定します。'ja'はもちろん日本語ですが、設定に使える文字列はl10n::__l10nCatalogというメンバ変数に定義されています。
l10n::__l10nCatalogに定義があって、英語など複数の国で使われる場合。
'en-gb' => array('language' => 'English (British)', 'locale' => 'en_gb', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
https://github.com/cakephp/cakephp/blob/1.3.6/cake/libs/l10n.php#L224
Configure::write('Config.language', 'en-gb');
で設定し、この設定が反映されます。'en-gb'は英語ですが、特にイギリス英語を指してます。CakePHPで言語を設定するのは、大抵poファイルでの翻訳だと思いますが、それには'locale'か'localeFallback'が使われます。
$l10n->languagePath = array('en_gb', 'eng');
この様に'locale', 'localeFallback'の順番でlanguagePathに設定されます。この場合i18nで翻訳する際、APP/locale/en_gb/にpoファイルがあるかチェックし、無ければAPP/locale/eng/を見に行く事になります。具体的にはhttps://github.com/cakephp/cakephp/blob/1.3.6/cake/libs/i18n.php#L284このあたりのコードです。
l10n::__l10nCatalogに定義があって、日本語など国別コードが省略される場合。
'ja' => array('language' => 'Japanese', 'locale' => 'jpn', 'localeFallback' => 'jpn', 'charset' => 'utf-8', 'direction' => 'ltr'),
https://github.com/cakephp/cakephp/blob/1.3.6/cake/libs/l10n.php#L276
Configure::write('Config.language', 'ja');
の場合は、この設定がl10n内で使われるようになります。前述の通り日本語の場合、基本的に日本でのみ使われる言語なので、'locale'と'localeFallback'のどちらも'jpn'が指定されています。
$l10n->languagePath = array('jpn', 'jpn');
当然、languagePathには'jpn'だけが入っています。翻訳に関してはAPP/locale/jpn/で、多分CakePHPでの標準的*1な位置だと思います。、ちょうどここがsetlocale()
と合わない部分です。
l10n::__l10nCatalogに定義が無い場合。
$l10n->languagePath = array('ja_jp');
例えばConfigure::write('Config.language', 'ja_jp');
を指定したら、指定した文字がそのままlanguagePathにも設定されます。APP/locale/ja_jp/をチェックする事になります。この例の場合、setlocale()
には使える設定ですが、l10n::__l10nCatalogに設定が無いので'language'や'locale'や'charset'などが設定されません。
l10n::__l10nCatalogのカタログとは
- 言語に対する様々な情報を保持しています。
- リクエストヘッダのHTTP_ACCEPT_LANGUAGEをキーになっています。(振り分けが容易)
<?php // 日本語の例 'ja' => array( 'language' => 'Japanese', // 言語のフルネーム 'locale' => 'jpn', // setlocale()などに適応される地域名 'localeFallback' => 'jpn', // 下記を参照 'charset' => 'utf-8', // この言語を使用する際に推奨される文字エンコーディング(自信なし) 'direction' => 'ltr' // 文字の方向。ltr(left to right)とrtl(right to left)のどちらか ) ?>localeFallbackですが、fallbackとは予備のもの、といった意味合いを持ちます。
例えば英語の場合、様々な地域・国で使われ、方言も多種多様にわたりますが、
- locale => en_ca // カナダ英語
- localeFallback => eng // カナダ英語も英語!
という風に、英語ならどこの地域・国でも「eng」を予備として利用できる、といった風になります。
つまりapp/locale/engに記述をおけば、多種多様な英語圏の国・地域に適応できるといったことが可能になります。
多言語化するときのConfig.languageの値 - 24時間CakePHP
この件を調べてる際の糸口になったエントリですが、カタログについて詳しく書いてあります。今でも気になってる事として「'locale' => 'jpn', // setlocale()などに適応される地域名」てあるけど、やっぱり'jpn'はsetlocale()
に使えるはずなのかな?
setlocale()でロケール設定する際の引数
<?php $locale = setlocale(LC_ALL, '0');var_dump($locale); //string(1) "C" $locale = setlocale(LC_ALL, 'jpn');var_dump($locale); //bool(false) $locale = setlocale(LC_ALL, 'jp');var_dump($locale); //bool(false) $locale = setlocale(LC_ALL, 'ja');var_dump($locale); //bool(false) $locale = setlocale(LC_ALL, 'ja_jp');var_dump($locale); //string(5) "ja_jp" $locale = setlocale(LC_ALL, 'ja_jp.utf-8');var_dump($locale); //string(11) "ja_jp.utf-8" $locale = setlocale(LC_ALL, 'jpn_jp');var_dump($locale); //bool(false) $locale = setlocale(LC_ALL, 'ja-jp');var_dump($locale); //bool(false)
CakePHPでは'locale'=>'jpn'
としているし、最初はもしかしたら'jpn'でもsetlocale()
でロケールの設定が出来るのかもと思い、いろんなパターンを試してみました。結果は上記の通り、よく見る国別コード付きのパターンじゃないと設定できませんでした。
カテゴリやロケール名は、 » RFC 1766 や » ISO 639 にあります。 ロケールの命名方式は、システムによって異なります。
http://jp2.php.net/manual/ja/function.setlocale.php
setlocale()
のマニュアルを見てみると、RFC 1766やISO 639を見るとあります。ISO 639は言語の略称の規格で、3文字か2文字の略称が定義されてます。ja_jpの最初のjaの部分は、RFC 1766で一次言語タグと呼ばれていて、ISO 639で規定される2文字の言語コードになっているようです。そのまま解釈するとsetlocale()
に'jpn'は使えないという事になります。
なお、システムによって異なるとも書いてあるので、最後の環境の通りMacとLinuxで試してみましたが、結果は同じでした。
あと、setlocale(LC_ALL, '0')
の様に引数'0'だと、ロケール設定は適用されず、単に現在の設定が返されます。
C言語だと
よく知りませんが、C言語の方だとsetlocale(jpn) - Google 検索やsetlocale(japanese) - Google 検索の様な指定が出来るみたいです。phpはC言語っぽい関数が多いので、同じ動きをしてくれれば良いんですけど。
CakePHPの問題?setlocale()の問題?
翻訳ファイルの置き場所を、en_gbやen_usとは別にengで統一したい場合というのは確かにありそうなので、jpnと言った3文字の略称も必要なんだとは思います。l10n::__l10nCatalogにRFC 1766準拠な値も必ず持たせるとかしてくれれば良いのに。もしくは逆にphpのsetlocale()
が'jpn'の様な形で指定できないのが変なのかな?個人的にはCakePHPがRFC 1766に準拠したロケールを扱うようになって欲しいです。
環境
Linux
サーバー | coreserver |
CakePHP | 1.3.6 |
php | 5.2.5 |
*1:pluginに含まれる翻訳ファイルもjpnを想定してると思う。