* @access private
*/
+ /** @var Ethna_Controller コントローラーオブジェクト */
+ var $ctl;
+
/** @var bool gettextフラグ */
var $use_gettext;
$this->locale_dir = $locale_dir;
$this->appid = strtoupper($appid);
- $ctl =& Ethna_Controller::getInstance();
- $config =& $ctl->getConfig();
- $this->logger =& $ctl->getLogger();
+ $this->ctl =& Ethna_Controller::getInstance();
+ $config =& $this->ctl->getConfig();
+ $this->logger =& $this->ctl->getLogger();
$this->use_gettext = $config->get('use_gettext') ? true : false;
// gettext load check.
$this->locale = $locale;
$this->systemencoding = $systemencoding;
$this->clientencoding = $clientencoding;
+
+ // 強制的にメッセージカタログ再生成
+ if (!$this->use_gettext) {
+ $this->messages = $this->_makeEthnaMsgCatalog();
+ }
}
/**
} else {
//
+ // 初期化されてない場合は、
// Ethna独自のメッセージカタログを初期化
- // この処理ははじめに使用するときまで遅延させる
//
if ($this->messages === false) {
- $this->messages = $this->_getEthnaMsgCatalog();
+ $this->messages = $this->_makeEthnaMsgCatalog();
}
//
* Ethna独自のメッセージカタログを読み込んで生成する
*
* 1. [appid]/locale/[locale_name]/LC_MESSAGES/*.ini
- * ã\81\8bã\82\89ã\83¡ã\83\83ã\82»ã\83¼ã\82¸ã\82\92èªã\81¿è¾¼ã\81¿ã\80\81$this->messages ã\82\92å\88\9dæ\9c\9få\8c\96ã\81\99ã\82\8b
+ * ã\81\8bã\82\89ã\83¡ã\83\83ã\82»ã\83¼ã\82¸ã\82\92èªã\81¿è¾¼ã\82\80ã\80\82
* 2. Ethnaが吐くメッセージカタログファイル名は ethna_sysmsg.ini とし、
* skel化して ETHNA_HOME/skel/locale/[locale_name]/ethna_sysmsg.ini に置く
* 3. "ethna i18n" コマンドでは、1. のファイルとプロジェクトファイル
* 内の _et('xxxx') を全て走査し、メッセージカタログを作る。gettext を利用
* するのであれば、potファイルを生成する。
* 4. ethna_sysmsg.ini は単純な ini ファイル形式とし、
- * msgid = translation の形式とする。エンコーディングは一律 UTF-8
+ * "msgid" = "translation" の形式とする。エンコーディングは一律 UTF-8
*
* @access private
* @return array 読み込んだメッセージカタログ。失敗した場合は空の配列
*/
- function _getEthnaMsgCatalog()
+ function _makeEthnaMsgCatalog()
{
+ if ($this->messages !== false) {
+ return;
+ }
+
$ret_messages = array();
+ // Ethna_I18N#setLanguage を呼び出さず
+ // このメソッドを呼び出すと、ロケール名が空になる
+ // その場合は Ethna_Controller の設定を補う
+ if (empty($this->locale)) {
+ list($this->locale, $sys_enc, $cli_enc) = $this->ctl->getLanguage();
+ }
+
+ // ロケールディレクトリが存在しない場合は、E_NOTICEを出し、
+ // デフォルトの skelton ファイルを使う
$msg_dir = sprintf("%s/%s/LC_MESSAGES", $this->locale_dir, $this->locale);
if (!file_exists($msg_dir)) {
- $this->logger->log(LOG_WARNING, "Message directory was not found!! : $msg_dir");
- return $ret_messages;
+ // use skelton.
+ $this->logger->log(LOG_NOTICE,
+ "Message directory was not found!! : $msg_dir,"
+ . " Use skelton file Instead");
+ $msg_dir = sprintf("%s/skel/locale/%s", ETHNA_BASE, $this->locale);
}
$msg_dh = opendir($msg_dir);
- // ini ファイル形式だが、parse_ini_file 関数は使わない
- // 特殊な文字が多すぎる上、parseの仕方もバージョンによって
- // 微妙に違ったりするため。
+ // ファイルフォーマットは ini ファイルライクだが、
+ // parse_ini_file 関数は使わない。
+ //
+ // キーに含められないキーワードや文字があるため。
+ // e.x yes, no {}|&~![() 等
+ // @see http://www.php.net/manual/en/function.parse-ini-file.php
while (($file = readdir($msg_dh)) !== false) {
if (is_dir($file) || !preg_match("/[A-Za-z0-9\-_]+\.ini$/", $file)) {
continue;
$msg_file = sprintf("%s/%s", $msg_dir, $file);
$contents = file($msg_file);
foreach ($contents as $idx => $line) {
- if (strpos($line, ';') === 0 || strpos($line, '=') === false) {
+
+ // コメント、もしくは不正な行は無視する
+ trim($line);
+ if (strpos($line, ';') === 0 || strpos($line, '=') === false || $line[0] != '"') {
continue;
}
- $catalog = array();
- if (preg_match('/^"(.+?)"\s*=\s*"(.*?)".*$/', $line, $catalog)) {
- $msgid = $catalog[1];
- $msgstr = $catalog[2];
- $ret_messages[$msgid] = $msgstr;
- } else {
+
+ $quote = 0; // ダブルクオートの数
+ $before_is_quote = false; // 直前の文字がダブルクォートか否か
+ $equal_op = 0; // 等値演算子の数
+ $is_end = false; // 終了フラグ
+ $length = strlen($line);
+ $msgid = $msgstr = '';
+
+ // 1文字ずつ、ダブルクォートの数
+ // を基準にしてパースする
+ for ($pos = 0; $pos < $length; $pos++) {
+
+ // 特別な文字で分岐
+ switch ($line[$pos]) {
+ case '"':
+ if (!$before_is_quote) {
+ $quote++;
+ continue 2; // switch 文を抜けるのではなく、
+ // for文に戻る。
+ }
+ $before_is_quote = false;
+
+ // ダブルクォートが4つに達した時点で終了
+ if ($quote == 4) {
+ $is_end = true;
+ }
+ break;
+ case '=':
+ // 等値演算子は文法的にvalidかどうかを確
+ // 認する手段でしかない
+ if ($quote == 2) {
+ $equal_op++;
+ }
+ case '\\': // backslash
+ if ($quote == 1 || $quote == 3) {
+ $before_is_quote = true;
+ }
+ break;
+ default:
+ if ($before_is_quote) {
+ $before_is_quote = false;
+ }
+ }
+
+ if ($is_end == true) {
+ break;
+ }
+
+ // パース済みの文字列を追加
+ if ($quote == 1) {
+ $msgid .= $line[$pos];
+ }
+ if ($quote == 3) {
+ $msgstr .= $line[$pos];
+ }
+ }
+
+ // valid な行かチェック
+ if ($equal_op != 1 || $quote != 4) {
$this->logger->log(LOG_WARNING,
"invalid message catalog in {$file}, line " . ($idx + 1)
- );
- }
+ );
+ continue;
+ }
+
+ $ret_messages[$msgid] = $msgstr;
}
}
-
+
return $ret_messages;
}
}
--- /dev/null
+<?php
+// vim: foldmethod=marker
+/**
+ * Ethna_I18N_Test.php
+ *
+ * @author Yoshinari Takaoka <takaoka@beatcraft.com>
+ * @version $Id$
+ */
+
+//{{{ Ethna_I18N_Test
+/**
+ * Test Case For Ethna_I18N class
+ *
+ * @access public
+ */
+class Ethna_I18N_Test extends Ethna_UnitTestBase
+{
+ var $i18n;
+
+ function setUp()
+ {
+ $ctl =& Ethna_Controller::getInstance();
+ $this->i18n = $ctl->getI18N();
+ }
+
+ // {{{ test_get_ja_JP
+ function test_get_ja_JP()
+ {
+ // デフォルトは日本語のメッセージが返ってくる
+ $this->assertEqual($this->i18n->get('Backend'), 'バックエンド');
+ $this->assertEqual($this->i18n->get('Could not write uploaded file to disk.'), 'ディスクへの書き込みに失敗しました。');
+ $this->assertEqual($this->i18n->get('Filter(%d)'), 'フィルタ(%d)');
+ $this->assertEqual($this->i18n->get('Heisei'), '平成');
+ $this->assertEqual($this->i18n->get('%Y/%m/%d %H:%M:%S'), '%Y年%m月%d日 %H時%M分%S秒');
+
+ // カタログにないメッセージはそのまま返ってくる
+ $this->assertEqual($this->i18n->get('foo'), 'foo');
+ $this->assertEqual($this->i18n->get('www.example.com'), 'www.example.com');
+ }
+ // }}}
+
+ // {{{ test_get_fallback_locale
+ function test_get_fallback_locale()
+ {
+ // ロケール切り替え
+ $this->i18n->setLanguage('en_US', 'ASCII', 'ASCII');
+
+ // メッセージカタログファイルがないロケールの場合は、
+ // skel/locale/ethna_sysmsg.ini にあるメッセージが返ってくる
+ $this->assertEqual($this->i18n->get('Backend'), 'Backend');
+ $this->assertEqual($this->i18n->get('Could not write uploaded file to disk.'),
+ 'Could not write uploaded file to disk.'
+ );
+ $this->assertEqual($this->i18n->get('Filter(%d)'), 'Filter(%d)');
+ $this->assertEqual($this->i18n->get('Heisei'), 'Heisei');
+ $this->assertEqual($this->i18n->get('%Y/%m/%d %H:%M:%S'),
+ '%Y/%m/%d %H:%M:%S'
+ );
+
+ // カタログにないメッセージはそのまま返ってくる
+ $this->assertEqual($this->i18n->get('foo'), 'foo');
+ $this->assertEqual($this->i18n->get('www.example.com'), 'www.example.com');
+ }
+ // }}}
+}
+// }}}
+
+?>