CakePHP 2.xアプリケーションの国際化(I18N - Internationalization)と現地化(L10N - Localization)の実例を公開します。
ダウンロード: CakePHP 2.xアプリケーションのソースファイル → cake-i18n.zip
このCakePHP 2.xサンプルのみどころです。
以下では、サンプルの概要、アプリの構成ファイルに続き、上記の各項目について解説していきます。
Wikipediaを引用します。
CakePHP(ケイクピーエイチピー)とは、PHPで書かれたオープンソースのWebアプリケーションフレームワークである。先行するRuby on Railsの概念の多くを取り入れており、Rails流の高速開発とPHPの機動性を兼ね備えたフレームワークと言われている。MITライセンスの元でフリーで配布されている。
同じくWikipediaによると、CakePHPは2005年に誕生、2008年にPHP v4またはv5で動作するv1.2.0がリリースされ、2011年にPHP v5のみをサポートするv2.0.0がリリースされています。
2012/12現在の最新安定版はv2.2.4です。
近年のWebアプリ作成の流れであるコンベンションが取り入れられており、データベースやPHPコードの命名規則でURLパスやリクエスト変数名が決まるという手法がとられています。 このコンベンションに従ったアプリ作成を支援するため、Cakeという便利なユーティリティが用意されており、データベースのテーブル定義をもとに、そのテーブルを操作するWebアプリを自動生成することもできます。
CakePHPのローカライズのしくみはC言語の世界で一般的なGNUのgettextライブラリを使用するものです。
このアプリは簡易メッセージ交換アプリです。 メッセージの作成・送信、一覧表示、削除ができます。
CakePHPの公式サイトの次のチュートリアルを組み合わせて、ちょっと色づけしてみた、という感じにしあがっています。
以下、画像をクリックするとその画像だけを表示できます。
このアプリは英語と日本語をサポートします。 Webブラウザで最優先の言語を日本語にすると日本語表示に、それ以外(たとえば英語)にすると英語表示になります。
言語設定がどうあれ、メッセージの本文はそれが入力された言語で表示されます。
このアプリはあらかじめユーザ登録が必要です。 登録の際、管理者権限と利用者権限との選択をします。 管理者権限を持つユーザは全メッセージの参照、削除が行えます。 利用者権限のユーザは、自分が出したか受け取ったメッセージについて、参照と削除が行えます。 そもそも他人のメッセージは一覧に現れません。 管理者はユーザの一覧表示、ユーザ情報の追加、削除が行えます。
CakePHPのアプリは、ふつうのPHPアプリケーションとはセットアップ方法が異なります。 Apacheをインストールして、PHPをインストールして、CakePHPをどこかにインストールして、それからアプリを... という方法ではありません。 CakePHPのパッケージをApacheのドキュメントルート配下にコピーして、その場所にアプリ専用のファイルを順に足していく、という方法を取ります。 JavaのStrutsアプリ制作で、ブランクテンプレートをコピーして、そこにアプリのファイルを足していくようなイメージです。
先にこのアプリを構成する主なファイルをみてみます。
<apache-document-root> cake-i18n/ app/ Config/ core.php database.php routes.php Controller/ AppController.php MessagesController.php UsersController.php Locale/ jpn/ LC_MESSAGES/ default.po Model Message.php User.php View Layouts default.ctp Messages add.ctp index.ctp view.ctp Users add.ctp index.ctp login.ctp view.ctp webroot css cake.generic.css
Apacheのドキュメントルート直下のcake-i18nがこのアプリのルートです。 CakePHPのパッケージを展開したフォルダーと一致します。 その下のappより下にアプリのファイルを一定の規則のもとに追加していきます。
おおまかにみると、
Config | アプリの設定ファイルの置き場所。使用するデータベース、デバッグモードの指定、アプリのトップページなどを決定する。 |
Controller | 各URLパスに対応する処理関数(アクション)を含むコントローラクラスを置く場所 |
Model | データベースの各テーブルを操作するためのモデルクラスを置く場所 |
View | コントローラの各関数に対応する結果ページの中核部分を置く場所。Layoutsには全体のレイアウトをデザインしたファイルを置く。デフォルトではdefault.ctpが使用される。 |
Locale | ローカライズ用翻訳ファイルを置く場所 |
webroot | アプリの静的ファイルを置く場所。たとえば、デフォルトのスタイルシートはcss/cake.generic.css。 |
このApacheのサーバ名が<server>だとすると次のURLがこのアプリの入り口です。
http://<server>/cake-i18n/
以下、アプリの制作手順を辿りながら、構成ファイルの作成方法について説明していきます。
CakePHPアプリ作成はデータの定義から始まります。 はじめにアプリで使用するデータベーステーブルを設計、作成しておきます。
このサンプルアプリはふたつのテーブルを使用します。 テキストメッセージを収めるmessagesテーブル、アプリのユーザーを登録するusersテーブルのふたつです。
次のSQL文でmessagesテーブルを作成します。
CREATE TABLE messages ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, sender_id INT UNSIGNED, receipient_id INT UNSIGNED, title VARCHAR(50), body TEXT, created DATETIME DEFAULT NULL, modified DATETIME DEFAULT NULL );
次はもうひとつのusersテーブル作成のSQL文です。
CREATE TABLE users ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50), password VARCHAR(50), role VARCHAR(20), created DATETIME DEFAULT NULL, modified DATETIME DEFAULT NULL );
データベースの接続情報はConfig/database.phpで指定します。
public $default = array( 'datasource' => 'Database/Mysql', 'persistent' => false, 'host' => 'localhost', 'login' => 'root', 'password' => 'test', 'database' => 'test', 'encoding' => 'utf8' );
CakePHPはこの情報を使ってデータベースにどんなテーブルがあるか、どんな列があるかを認識します。
ごらんのとおり、このアプリで使用する文字コードはデータベース、Webアプリ全体を通してUTF8を使っています。
続いてテーブルごとにモデルクラスを作成します。 messagesテーブルのモデルクラスはapp/Model/Message.php、usersテーブルのモデルクラスはapp/Model/User.phpです。 CakePHPにテーブルの名前を教えるだけで、プログラマがSQL文や列名の指定などを行わなくとも、CakePHPがテーブル定義を調べてかなりのことをやってくれるので驚きます。
モデルクラスを使うと、プログラマが特に関数を用意しなくても、次のような処理が可能です。
$this->Message->findAll(); $this->Message->findAllBySenderId(3); $this->User->findById(3); $this->User->findAllByRole('Admin');
それぞれ、messagesテーブルから全行、同じくsender_idが3の全行、usersテーブルからidが3の行、同じくroleがAdminの全行のデータを取り出します。
データベースの更新をモデルにまかせておけば、createdやmodifiedなどタイムスタンプも自動的に更新してくれます。
続いてコントローラを作成します。 コントローラクラスには、このアプリのURLパスに対応する関数を定義します。 たとえば、messages/index(あるいは単にmessages/)に対しては、app/Controller/MessagesController.phpというコントローラクラス内に定義されたindex()関数が呼び出されます。 同じく、messages/addに対してはMessagesController.php内のadd()関数が対応します。
最後にビューページを作成します。 messages/indexに対応するMessagesController.phpのindex()の結果を表示するビューがapp/View/Messages/index.ctpです。 messages/addの場合はapp/View/Messages/add.ctpが対応します。
http://<server>/cake-i18n/messages/index -> MessagesController::index() -> View/Messages/index.ctp (uses Messages.php -> messages table) http://<server>/cake-i18n/messages/add -> MessagesController::add() -> View/Messages/add.ctp (uses Messages.php -> messages table)
このようにCakePHPではアプリの各要素間の連結がデータベースのテーブルやコラムの名前、PHPコードのフォルダ、ファイル、関数の名前に従って行われます。 特別な設定ファイルなどは一切不要です。
CakePHPには、CakeというPHPで書かれたコンソールコマンドが用意されています。 Cakeの二つのサブコマンドを使って、
Cakeユーティリティに引数「bake」を指定して実行するとBakeサブコマンドが起動できます(Cake i18nについては後述)。 このアプリのmessagesテーブルに対して生成されたファイルは次のものです。
app/Model/Message.php app/Controller/MessagesController.php app/View/Messages/index.ctp app/View/Messages/add.ctp app/View/Messages/delete.ctp app/View/Messages/view.ctp app/View/Messages/edit.ctp
これだけで、テーブル単体ではあるものの、そのテーブルの行を操作するWebページとその処理部分がすべて揃います。 特に一覧ページには最初からページ繰り(pagination)の機能が織り込まれています。 ページ移動のためのボタンが付き、表の見出しをクリックすると表示順をかえることができます。 さらに最初の表示順を指定することも可能です。
単独のテーブルの操作だけで成立するアプリは少ないと思いますが、アプリを構成する個別データの基本操作を行うためのPHPコードをいちいち手書きする必要がなくなるのは助かります。 あとは、他のデータとの関連付け、HTMLフォームの初期値の設定、など例外的な処理を追加していくことでアプリが完成します。
Cake bakeコマンドを使用するにはパスの設定などやや面倒なところがあります。 次のサイトが参考になると思います。
Bakeコマンドで生成したPHPコードにいくつかの変更を加え、所望のアプリをしあげていきます。 私が加えた主な変更箇所は次のとおりです。
Config/routes.php | アプリの入り口(ログインページ)を指定 |
Controller/AppController.php | セッションおよび認証コンポーネントの使用宣言 |
Controller/MessageController.php | 通常利用者に対して表示するメッセージの制限。差出人をログイン中のユーザーに設定。 |
View/Messages/add.ctp | 受信者候補一覧の作成。送信者はログイン中のユーザーに固定。 |
View/Messages/index.ctp | 送信者・受信者を名前で表示(IDではなく) |
View/Messages/view.ctp | 同上 |
View/User/add.ctp | 入力可能な権限を限定(adminとuser) |
View/User/index.ctp | パスワードを表示しない(ハッシュ値なので見えてもかまわないが) |
変更箇所には「mods」ではじまるコメントが付けてあります。 サンプルのコードをごらんになれば、手を入れた箇所がいかに少なくて済んでいるか、おわかりいただけると思います。
このうち、一点重要な変更があります。モデル(つまりテーブル)間の関係付けです。 メッセージテーブルには送信者と受信者のIDが格納されています。 これらIDはユーザテーブルの行を指しています。 最初は、メッセージの一覧や追加画面には最初、IDのほうが表示されていました。 名前が表示されれば便利です。
messagesテーブルとusersテーブルとの関連付けをCakePHPに通知しておき、messagesテーブルの行を読み込むと、あわせてusersテーブルの関連の行も読み込んでくれます。 この場合は特定のメッセージの送信者と受信者の名前が得られます。
この設定のもとでモデルを使ってメッセージデータを読み込んでみると、
$message = $this->Message->findById(10); // read a message whose id is ten
読み込んだデータ、つまり変数$messageの内容は次のようになります。
array 'Message' id => 10 sender_id = 3 receipient_id = 4 title => 'Hello' body => 'Hello, World' 'Sender' id => 3 username => 'obama' 'Receipient' id => 4 username => 'abe'
CakePHPのモデルどうしの関係付けの機能はデフォルトで一段ですが、複数段でいもづる式にも行える強力なものです。
Bakeユーティリティにはモデル間の関係を設定する機能もあるようですが、使い方がよくわからず、試していません。 これができればすばらしいと思いました。
Cakeユーティリティのもうひとつの機能は、アプリを構成する全ソースファイルを解析して、翻訳の必要のある文字列を抽出することです。
Bakeコマンドが生成したPHPコードの中身を見てみると、次のような箇所があります。
[View/Messages/index.ctp] <?php echo $this->Form->postLink(__('Delete'), array('action' => 'delete', $message['Message']['id']), null, __('Are you sure you want to delete # %s?', $message['Message']['id'])); ?> [app/Controller/MessagesController.php] $this->Session->setFlash(__('Message deleted'));
下線が二つ並んだ関数があります。
これがローカライズされた文字列を出力するための関数です。
たとえば、__('Delete')
であれば、何もしないとボタン名は英語の「Delete」になります。
日本語用の翻訳ファイルを用意して「Delete」に対応する日本語が「削除」であることを書いておけば、ブラウザの言語が日本語に設定されていると、ボタン名は「削除」と表示されます。
Cakeユーティリティに引数「i18n」を指定して実行すると、アプリ内のすべてのPHPコードを巡回して、上記のような文字列を抽出して、拡張子がpotのファイルをいくつか生成してくれます。
app Locale cake.pot cake_console.pot cake_dev.pot default.pot
このうち、アプリの文字列が収められたファイルはdefault.potです。 これは複数言語の翻訳の元にするテンプレートファイルです。 中身を見ると次のような項目があります。
#: View/Messages/edit.ctp:19 #: View/Messages/index.ctp:32 #: View/Users/edit.ctp:18 #: View/Users/index.ctp:25 #: View/Scaffolds/form.ctp:31 #: View/Scaffolds/index.ctp:52 #: View/Scaffolds/view.ctp:133 msgid "Delete" msgstr "" #: Controller/MessagesController.php:140 msgid "Message deleted" msgstr ""
これを、日本語の場合であれば、次のように翻訳します。
msgid "Delete" msgstr "削除" msgid "Message deleted" msgstr "メッセージを削除しました"
こうして作成した翻訳ファイルを次の位置に配置すればローカライズは完了です。 拡張子はテンプレートを表すPOTではなく、ただのPOにします。
app/ Locale/ jpn/ LC_MESSAGES/ default.po
ところで、自動生成された翻訳テンプレートにはdefault.pot以外にもファイルがあります。 これらのファイルにはCakePHP側の文字列が含まれています。 メッセージの利用範囲を分ける「ドメイン」ごとに翻訳ファイルを用意できるようになっているのです。
デフォルトで使用されるレイアウトファイルには、次のような行があります。
[View/Layouts/default.ctp] $cakeDescription = __d('cake_dev', 'CakePHP: the rapid development php framework');
ふつうの__()
関数はdefault.poからメッセージを取得します。
__d()
関数は、この場合、cake_devドメインからメッセージを取得しようとします。
つまり、cake_dev.poを用意し、ここに該当の翻訳文字列を入れておく必要があります。
さて、このサンプルアプリは使用言語の指定は何も行っていません。 CakePHP側でブラウザから送られてくる優先言語指定(Accept-Languagesヘッダ)に従い、自動的に切り換えてくれます。
しかしながら、明示的に特定の言語を指定して固定することも可能です。
Configure::write('Config.language', 'en'); // or 'ja' for japanese
ログイン時などセッション開始時に言語を決定し、以降のその言語を使う、という方法も取れます。
[At the beginning of a session] $this->Session->write('Config.language', 'en'); [Rest of the session] class AppController extends Controller { public function beforeFilter() { Configure::write('Config.language', $this->Session->read('Config.language')); } }
以上、CakePHP 2.xサイトの次のドキュメントを参考にしました。
Internationalization & Localization
Cake i18nコマンドについては、同じく、次のサイトが参考になると思います。
通常Webアプリで認証機能を実現するには、ファイルやデータベースから取得したログイン情報をセッションに保存しておき、必要に応じ参照する、というものです。 CakePHPでは、これを簡単にしかも統一的に行えるようなコンポーネントが用意されています。
認証コンポーネント(AuthComponent)の特徴は、一定の規則に従ってユーザ情報を用意しておけば、プログラマはほとんど何もしなくてもよい、というものです。
public $components = array('Auth', parameters);
認証コンポーネントは、データベース内のパスワードが平文ではなくハッシュ値が書き込まれていることを前提にしています。 このため、User.php中に、書き込みの際ハッシュ変換を行うためのしかけを入れる必要があります。
public function beforeSave($options = array()) { if (isset($this->data[$this->alias]['password'])) { $this->data[$this->alias]['password'] = AuthComponent::password($this->data[$this->alias]['password']); } return true; }
また、最初のユーザ(管理者)を登録しないとアプリが使えません。 ログインしないとユーザの追加が行えないからです。 このため、開発モードで追加だけは許しておく、などの工夫がいります。
[Controller/UsersController.php] public function beforeFilter() { parent::beforeFilter(); if (Configure::read('debug') > 0) $this->Auth->allow('add'); }
ユーザがログインすると、そのユーザの情報を収めた行が読み込まれ、セッションに保存されます。 保護ページへのチェックはCakePHPにより自動的に行われます。 権限(role)による認可機能を加えたければ、次の例のように、セッション内のユーザ情報を参照して行えます。
$user = $this->Auth->user(); if ($user['role'] === 'admin') { ...
以上、CakePHP 2.xサイトの次のドキュメントを参考にしました。
このサンプルアプリをテスト実行する方法をまとめます。
以上、http://book.cakephp.org/2.0/ja/installation.htmlを参照。
Apacheの場合mod_writeを有効にするとよい。
またindex.phpがインデックスページとして認識されるようにしておく。
PHP、MySQL、CakePHPともにUTF-8を使用するように設定しておく。
筆者は下記の環境を利用しました。
Apache 2.2
PHP 5.3
MySQL 5.1
CakePHP 2.2
このサイトで提供しているほかのローカライズサンプルです。
横浜工文社では、世界で使えるソフトウェアやドキュメントの制作で実績があります。
何なりとご相談ください。
Copyright © 2012 Kobu.Com. All rights reserved.
提供: 横浜工文社
制作: 荒井文吉
執筆: 2012/12/29
公開するサンプルは試作品で、完全なものではありません。
この解説書とサンプルの転載はご遠慮ください。
リンク張りやお問合せやコメントを歓迎いたします。