Hatena::ブログ(Diary)

24時間CakePHP このページをアンテナに追加 RSSフィード

CakePHP1.2.8以前、1.3.5以前に重大なセキュリティホールが見つかりました。
ただちにコアを最新版にアップデートすることをお勧めします。

参考: CakePHPのSecurityComponentに深刻なセキュリティホールが見つかりました - Shin x blog

2010/05/06

多言語化するときのConfig.languageの値

本題

カレント言語を指定するには、次のようにすると思います。

<?php
// app/locale/ja_jp ディレクトリ以下の情報を用いる
Configure::write('Config.language', 'ja_jp');
?>

以下、この例でいう'ja_jp'を識別子と呼びます。


さて、識別子が、l10nのカタログ(のキー)に存在するとき、l10nはそれに対応する「locale」「localeFallback」といった値を、languagePath(ロケール情報をもつディレクトリへのパス郡)に追加します。

よって、例えば、識別子に「ja」を指定した場合、以下のようになります。

<?php
// 識別子「ja」はカタログにあり、「locale」、「localeFallback」の値は「jpn」
Configure::write('Config.language', 'ja');
// よって、app/locale/jpn/ 以下の情報を参照する
// $l10n->languagePath = array('jpn', 'jpn');
?>

カタログって何?

  • 言語に対する様々な情報を保持しています。
  • リクエストヘッダの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に記述をおけば、多種多様英語圏の国・地域に適応できるといったことが可能になります。

余談

最初、本題のことが理解できておらず、小一時間デバッグして、ようやくたどり着き、どうせなら噛み砕いてみようと思ってこのエントリを書きました。半分備忘録です。

なかなか大多数の地域に渡って多言語化することはないと思いますが、世界中で利用されているCakePHPならではの機能であるなぁと思います。

2010/03/04

TimeHelperを使って多言語対応の日付・時刻を表示する

CakePHP1.3からは、TimeHelperでi18nFormat()というメソッドが使えます。

http://book.cakephp.org/ja/view/1566/View-と-Helper

概要

このメソッドは、strftime()の書式を与えると、アプリケーションロケール情報に基づいて日付・時刻を返します。

  1. setlocale()してからstrftime($this->Time->fromString($time))するのと何が違うの?

この機構の素晴らしいところは、アプリケーションレベルでローカライゼーションの制御ができるということです。

と言ってもよくわからないと思うので、実際に使えるところまでのチュートリアルを示します。

LC_TIMEファイルの設置

まず、このメソッドを使うにあたって、LC_TIMEファイルを設置しなければなりません。

これはubuntudebianの、/usr/share/i18n/locales以下にあるファイル群からコピーできます。

設置場所はapp/locale/言語/LC_TIMEです。(言語 == Configure::read('Config.language'))

<?php
// in config/core.php
Configure::write('Config.language' ,'ja');
?>

とします。

$ cp /usr/share/i18n/locales/ja_JP /var/www/myapp/app/locale/ja/LC_TIME

GUIで操作する場合、フォルダではなく、ファイルをコピーしてきてリネームしてください。

使ってみる

CakePHP1.3からはTimeHelper::format($date,$format)が18nFormat()のエイリアスになっています。

この例ではi18nFormat()として使っていますが、実用的にはformat()で十分でしょう。

デフォルトのフォーマット
<?php
// in View
echo $this->Time->i18nFormat(time());
?>

さて、何が表示されましたか?

2010年03月04日”

のような表示ならば成功です。

  1. ”2010U5E7403U670804U65E5”みたいなよくわからん文字になる
    1. Gitの最新版にアップデートしてください。修正しました。
  2. 2010年03月%d日”となって文字化けする
    1. 修正パッチを送りましたがまだ適用されてません。後にパッチを載せておきますので、使ってみてください。
フォーマットを指定する
<?php
   // ”2010年03月04日”
echo $this->Time->i18nFormat(time(),'%x %X');
echo $this->Time->i18nFormat(time(),'%c');
   // ”午前07時50分59秒”
echo $this->Time->i18nFormat(time(),'%r');
   // ”木”
echo $this->Time->i18nFormat(time(),'%a');
   // ”木曜日”
echo $this->Time->i18nFormat(time(),'%A');
   // ”3月”
echo $this->Time->i18nFormat(time(),'%b');
echo $this->Time->i18nFormat(time(),'%B');
?>

などなど。

ただし、WindowsなどでUTF-8のときに%Z等を使うと文字化けします。これはstrftime()の仕様に基づきます。

なお、表示結果はLC_TIMEの中身に依存します。

つまり、気に入らないならば自分好みのLC_TIMEを作れる・使えるということですね。

※ただし、中身を弄るのはツールを使わない限り難しい

何故使うのか

さて、このようなことをせずとも、setlocale()とstrftime()で実は同じことが出来ます

しかし、レンタルサーバーなど、日本語ロケールがそもそも存在しなかったり、Windowsだと文字化けで使い物にならなかったりします。

これらはそのような問題をCakePHPが吸収してくれます。

最後に

一つ注意ですが、LC_TIMEファイルだけで(自分のコピーしたファイルは)216KBもあります。

シェイプアップしたファイルを置いておきました。これは最新版のcake/tests/test_app/locale/ja_jpのものと同等です。

http://bin.cakephp.org/view/1264346250


パッチ

diffっぽいもの

http://github.com/hiromi2424/cakephp1x/commit/027d675914aade911dc6773e42f7be99d5c67820

ファイルが欲しい

http://github.com/hiromi2424/cakephp1x/blob/1.3/cake/libs/view/helpers/time.php

コアのファイルに上書きしてください。

補足

nice()やniceShort()は未だに多言語対応してませんでした。(tommorow と yesterday だけ)

id:cake67 さんすいません。お詫びして訂正します。

※対応するようにパッチを投げる予定でいます

2010/03/02

(速報)1.3のi18nFormat()はまだ使ってはいけない

CakePHP1.3では、Time Helperを使うことにより、日付/時刻の表示の多国後対応が容易になりました。

http://book.cakephp.org/ja/view/1579/Library-classes


これは少なくとも自分にとって非常に嬉しいニュースでした。

というわけで嬉々として試してみたんですが、ガイド通りにセットアップしても妙な文字列が表示されます。


ソースリーディングしたところ、こういうことでした


  1. app/localeに置かれた言語別の日時/時刻定義ファイル(LC_TIME)を解析する。
  2. 中身の文字列は全て(?)<Unnnn>という形式になっている。これはUTF-8コードの頭文字にUを付けたもの。
  3. 'Unnnn' => "\xnnnn"という変換を行う。(かなり乱暴に言うと)
  4. ただし変換するのは'U00nn'となっているもののみ

というわけで、マルチバイトユニコード(つまり日本語は全滅)のものは、全てUnnnnといった文字列そのまま表示され、使い物になりません。

この件に関しては、完全にバグだと思われますので、修正コードとそのテストケースを含むチケットを後ほど投げる予定です。