Index: /branches/version-2_12-dev/html/install/sql/insert_data.sql
===================================================================
--- /branches/version-2_12-dev/html/install/sql/insert_data.sql	(revision 21812)
+++ /branches/version-2_12-dev/html/install/sql/insert_data.sql	(revision 21813)
@@ -1027,4 +1027,5 @@
 INSERT INTO mtb_constants (id, name, rank, remarks) VALUES ('MASTER_DATA_REALDIR', 'DATA_REALDIR . "cache/"', 27, 'マスターデータキャッシュディレクトリ');
 INSERT INTO mtb_constants (id, name, rank, remarks) VALUES ('UPDATE_HTTP', '"http://sv01.ec-cube.net/info/index.php"', 28, 'アップデート管理用ファイル格納場所');
+INSERT INTO mtb_constants (id, name, rank, remarks) VALUES ('LANG_CODE', '"ja"', 38, '言語コード');
 INSERT INTO mtb_constants (id, name, rank, remarks) VALUES ('CHAR_CODE', '"UTF-8"', 39, '文字コード');
 INSERT INTO mtb_constants (id, name, rank, remarks) VALUES ('LOCALE', '"ja_JP.UTF-8"', 40, 'ロケール設定');
Index: /branches/version-2_12-dev/data/class/helper/SC_Helper_Locale.php
===================================================================
--- /branches/version-2_12-dev/data/class/helper/SC_Helper_Locale.php	(revision 21813)
+++ /branches/version-2_12-dev/data/class/helper/SC_Helper_Locale.php	(revision 21813)
@@ -0,0 +1,172 @@
+<?php
+/*
+ * This file is part of EC-CUBE
+ *
+ * Copyright(c) 2000-2011 LOCKON CO.,LTD. All Rights Reserved.
+ *
+ * http://www.lockon.co.jp/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+require DATA_REALDIR . 'module/Locale/streams.php';
+require DATA_REALDIR . 'module/Locale/gettext.php';
+
+/**
+ * ローカリゼーション関係のヘルパークラス.
+ * 主に static 参照する関数群.
+ *
+ * @package Helper
+ * @author LOCKON CO.,LTD.
+ * @version $Id$
+ */
+class SC_Helper_Locale {
+
+    // }}}
+    // {{{ functions
+
+    /**
+     * メッセージの一覧から、エイリアスに対応する文字列を返す.
+     *
+     * @param string $string メッセージエイリアス
+     * @param string $lang_code 言語コード
+     * @param integer $device_type_id 端末種別ID
+     * @return string エイリアスに対応する文字列
+     */
+    function get_locale($string, $lang_code = LANG_CODE, $device_type_id = DEVICE_TYPE_PC) {
+        // 指定言語の文字列一覧を取得する
+        $translations = SC_Helper_Locale_Ex::get_translations($lang_code, $device_type_id);
+        // エイリアスに対応する文字列が存在するかどうか
+        if (isset($translations[$string])) {
+            return $translations[$string];
+        }
+        else {
+            return $string;
+        }
+    }
+
+    /**
+     * 指定言語の文字列一覧を取得する
+     *
+     * @param string $lang_code 言語コード
+     * @param integer $device_type_id 端末種別ID
+     * @return array 文字列一覧
+     */
+    function get_translations($lang_code = LANG_CODE, $device_type_id = DEVICE_TYPE_PC) {
+        static $translations;
+
+        // 指定言語の文字列がまだ読み込まれていない場合
+        if (empty($translations[$lang_code][$device_type_id])) {
+            $translations[$lang_code][$device_type_id] = array();
+
+            // 読み込むロケールファイルの一覧を取得
+            $file_list = SC_Helper_Locale_Ex::get_locale_file_list($lang_code, $device_type_id);
+
+            // ロケールファイル毎に、php_gettextを利用して文字列を取得する
+            foreach ($file_list as $locale_file) {
+                $stream = new FileReader($locale_file);
+                $gettext = new gettext_reader($stream);
+
+                $gettext->load_tables();
+                $translations[$lang_code][$device_type_id] = array_merge($translations[$lang_code][$device_type_id], $gettext->cache_translations);
+            }
+        }
+
+        return $translations[$lang_code][$device_type_id];
+    }
+
+    /**
+     * ロケールファイルの一覧を取得する
+     *
+     * @param string $lang_code 言語コード
+     * @param integer $device_type_id 端末種別ID
+     * @return array ファイル一覧
+     */
+    function get_locale_file_list($lang_code = LANG_CODE, $device_type_id = DEVICE_TYPE_PC) {
+        $file_list = array();
+
+        // コアのロケールファイルのパスを作成
+        $core_locale_path = DATA_REALDIR . "locales/$lang_code.mo";
+        // 指定言語のロケールファイルが存在すればファイル一覧に追加
+        if (file_exists($core_locale_path)) {
+            $file_list[] = $core_locale_path;
+        }
+
+        // 有効なプラグインを取得
+        $arrPluginDataList = SC_Plugin_Util_Ex::getEnablePlugin();
+        // pluginディレクトリを取得
+        $arrPluginDirectory = SC_Plugin_Util_Ex::getPluginDirectory();
+        foreach ($arrPluginDataList as $arrPluginData) {
+            // プラグイン本体ファイル名が取得したプラグインディレクトリ一覧にある事を確認
+            if (array_search($arrPluginData['plugin_code'], $arrPluginDirectory) !== false) {
+                // プラグインのロケールファイルのパスを作成
+                $plugin_locale_path = PLUGIN_UPLOAD_REALDIR . $arrPluginData['plugin_code'] . "/locales/$lang_code.mo";
+                // 指定言語のロケールファイルが存在すればファイル一覧に追加
+                if (file_exists($plugin_locale_path)) {
+                    $file_list[] = $plugin_locale_path;
+                }
+            }
+        }
+
+        // テンプレートのロケールファイルのパスを作成
+        $template_locale_path = HTML_REALDIR . SC_Helper_PageLayout_Ex::getUserDir($device_type_id, true) . "locales/$lang_code.mo";
+        // 指定言語のロケールファイルが存在すればファイル一覧に追加
+        if (file_exists($template_locale_path)) {
+            $file_list[] = $template_locale_path;
+        }
+
+        return $file_list;
+    }
+
+    /**
+     * 適切な複数形の書式を判定する
+     *
+     * @param integer $count 数値
+     * @param string $lang_code 言語コード
+     * @return integer インデックス
+     */
+    function get_plural_index($count, $lang_code = LANG_CODE) {
+        // 計算式を取得
+        $string = SC_Helper_Locale_Ex::get_plural_forms($lang_code);
+        $string = str_replace('nplurals', "\$total", $string);
+        $string = str_replace("n", $count, $string);
+        $string = str_replace('plural', "\$plural", $string);
+
+        $total = 0;
+        $plural = 0;
+
+        eval("$string");
+        if ($plural >= $total) $plural = $total - 1;
+
+        return $plural;
+    }
+
+    /**
+     * 適切な複数形の書式判定に使用する計算式を取得する
+     *
+     * @param string $lang_code 言語コード
+     * @return string 計算式
+     */
+    function get_plural_forms($lang_code = LANG_CODE) {
+        static $plural_forms;
+
+        // 書式が読みこまれていない場合はファイルから一覧を読み込む
+        if(empty($plural_forms)){
+            $plural_forms = @include_once DATA_REALDIR . "include/plural_forms.inc";
+        }
+
+        return $plural_forms[$lang_code];
+    }
+}
Index: /branches/version-2_12-dev/data/class/util/SC_Utils.php
===================================================================
--- /branches/version-2_12-dev/data/class/util/SC_Utils.php	(revision 21758)
+++ /branches/version-2_12-dev/data/class/util/SC_Utils.php	(revision 21813)
@@ -1736,4 +1736,67 @@
     }
 
-
+    /**
+     * メッセージエイリアスを翻訳する.
+     *
+     * @param string $string メッセージエイリアス
+     * @param array $args 置換用のパラメーター
+     * @param array $options オプション
+     * @return string 表示するメッセージ
+     */
+    function t($string, $args = array(), $options = array()) {
+        // 言語コードが指定されていなければ、サイトの言語コードを使用する
+        if (empty($options['lang_code'])) {
+            $options['lang_code'] = LANG_CODE;
+        }
+        // 機種IDが指定されていなければ、機種を判別する
+        if (empty($options['device_type_id'])) {
+            $options['device_type_id'] = SC_Display_Ex::detectDevice();
+        }
+
+        // メッセージエイリアスに対応する、指定言語の文字列を取得
+        $translated = SC_Helper_Locale_Ex::get_locale($string, $options['lang_code'], $options['device_type_id']);
+
+        // パラメーターが指定されていれば、メッセージを置換する
+        if (empty($args)) {
+          return $translated;
+        }
+        else {
+          return strtr($translated, $args);
+        }
+    }
+
+    /**
+     * メッセージエイリアス翻訳の複数形対応.
+     *
+     * @param integer $count 表示する数値
+     * @param string $single メッセージエイリアス（単数）
+     * @param string $plural メッセージエイリアス（複数）
+     * @param array $args 置換用のパラメーター
+     * @param array $options オプション
+     * @return string 表示するメッセージ
+     */
+    function t_plural($count, $single, $plural, $args = array(), $options = array()) {
+        $args[':count'] = $count;
+
+        // 言語コードが指定されていなければ、サイトの言語コードを使用する
+        if (empty($options['lang_code'])) {
+            $options['lang_code'] = LANG_CODE;
+        }
+        // 機種IDが指定されていなければ、機種を判別する
+        if (empty($options['device_type_id'])) {
+            $options['device_type_id'] = SC_Display_Ex::detectDevice();
+        }
+
+        // 適切な複数形の書式を判定
+        $index = SC_Helper_Locale_Ex::get_plural_index($count, $options['lang_code']);
+
+        // pluralの文字列はnull文字で結合されている
+        $key = $single . chr(0) . $plural;
+        // メッセージエイリアスに対応する、指定言語の文字列を取得
+        $translated = SC_Helper_Locale_Ex::get_locale($key, $options['lang_code'], $options['device_type_id']);
+        // null文字で分割
+        $list = explode(chr(0), $translated);
+
+        return strtr($list[$index], $args);
+    }
 }
Index: /branches/version-2_12-dev/data/locales/ja.po
===================================================================
--- /branches/version-2_12-dev/data/locales/ja.po	(revision 21813)
+++ /branches/version-2_12-dev/data/locales/ja.po	(revision 21813)
@@ -0,0 +1,20 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: EC-CUBE Core\n"
+"POT-Creation-Date: 2012-05-07 13:39+0900\n"
+"PO-Revision-Date: 2012-05-07 19:10+0900\n"
+"Last-Translator: PineRay <matsuda@cyber-will.co.jp>\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n!=1);\n"
+
+msgid "TEST"
+msgstr "テスト"
+
+msgid "SINGLE"
+msgid_plural "PLURAL"
+msgstr[0] "ひとつ"
+msgstr[1] ":count個"
+
Index: /branches/version-2_12-dev/data/include/plural_forms.inc
===================================================================
--- /branches/version-2_12-dev/data/include/plural_forms.inc	(revision 21813)
+++ /branches/version-2_12-dev/data/include/plural_forms.inc	(revision 21813)
@@ -0,0 +1,8 @@
+<?php
+/**
+ * 複数形書式判定用の計算式
+ */
+
+return array(
+    'ja' => "nplurals=2; plural=(n!=1);"
+);
Index: /branches/version-2_12-dev/data/module/Locale/streams.php
===================================================================
--- /branches/version-2_12-dev/data/module/Locale/streams.php	(revision 21813)
+++ /branches/version-2_12-dev/data/module/Locale/streams.php	(revision 21813)
@@ -0,0 +1,167 @@
+<?php
+/*
+   Copyright (c) 2003, 2005, 2006, 2009 Danilo Segan <danilo@kvota.net>.
+
+   This file is part of PHP-gettext.
+
+   PHP-gettext is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   PHP-gettext is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with PHP-gettext; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+*/
+
+
+  // Simple class to wrap file streams, string streams, etc.
+  // seek is essential, and it should be byte stream
+class StreamReader {
+  // should return a string [FIXME: perhaps return array of bytes?]
+  function read($bytes) {
+    return false;
+  }
+
+  // should return new position
+  function seekto($position) {
+    return false;
+  }
+
+  // returns current position
+  function currentpos() {
+    return false;
+  }
+
+  // returns length of entire stream (limit for seekto()s)
+  function length() {
+    return false;
+  }
+};
+
+class StringReader {
+  var $_pos;
+  var $_str;
+
+  function StringReader($str='') {
+    $this->_str = $str;
+    $this->_pos = 0;
+  }
+
+  function read($bytes) {
+    $data = substr($this->_str, $this->_pos, $bytes);
+    $this->_pos += $bytes;
+    if (strlen($this->_str)<$this->_pos)
+      $this->_pos = strlen($this->_str);
+
+    return $data;
+  }
+
+  function seekto($pos) {
+    $this->_pos = $pos;
+    if (strlen($this->_str)<$this->_pos)
+      $this->_pos = strlen($this->_str);
+    return $this->_pos;
+  }
+
+  function currentpos() {
+    return $this->_pos;
+  }
+
+  function length() {
+    return strlen($this->_str);
+  }
+
+};
+
+
+class FileReader {
+  var $_pos;
+  var $_fd;
+  var $_length;
+
+  function FileReader($filename) {
+    if (file_exists($filename)) {
+
+      $this->_length=filesize($filename);
+      $this->_pos = 0;
+      $this->_fd = fopen($filename,'rb');
+      if (!$this->_fd) {
+        $this->error = 3; // Cannot read file, probably permissions
+        return false;
+      }
+    } else {
+      $this->error = 2; // File doesn't exist
+      return false;
+    }
+  }
+
+  function read($bytes) {
+    if ($bytes) {
+      fseek($this->_fd, $this->_pos);
+
+      // PHP 5.1.1 does not read more than 8192 bytes in one fread()
+      // the discussions at PHP Bugs suggest it's the intended behaviour
+      $data = '';
+      while ($bytes > 0) {
+        $chunk  = fread($this->_fd, $bytes);
+        $data  .= $chunk;
+        $bytes -= strlen($chunk);
+      }
+      $this->_pos = ftell($this->_fd);
+
+      return $data;
+    } else return '';
+  }
+
+  function seekto($pos) {
+    fseek($this->_fd, $pos);
+    $this->_pos = ftell($this->_fd);
+    return $this->_pos;
+  }
+
+  function currentpos() {
+    return $this->_pos;
+  }
+
+  function length() {
+    return $this->_length;
+  }
+
+  function close() {
+    fclose($this->_fd);
+  }
+
+};
+
+// Preloads entire file in memory first, then creates a StringReader
+// over it (it assumes knowledge of StringReader internals)
+class CachedFileReader extends StringReader {
+  function CachedFileReader($filename) {
+    if (file_exists($filename)) {
+
+      $length=filesize($filename);
+      $fd = fopen($filename,'rb');
+
+      if (!$fd) {
+        $this->error = 3; // Cannot read file, probably permissions
+        return false;
+      }
+      $this->_str = fread($fd, $length);
+      fclose($fd);
+
+    } else {
+      $this->error = 2; // File doesn't exist
+      return false;
+    }
+  }
+};
+
+
+?>
Index: /branches/version-2_12-dev/data/module/Locale/gettext.php
===================================================================
--- /branches/version-2_12-dev/data/module/Locale/gettext.php	(revision 21813)
+++ /branches/version-2_12-dev/data/module/Locale/gettext.php	(revision 21813)
@@ -0,0 +1,432 @@
+<?php
+/*
+   Copyright (c) 2003, 2009 Danilo Segan <danilo@kvota.net>.
+   Copyright (c) 2005 Nico Kaiser <nico@siriux.net>
+
+   This file is part of PHP-gettext.
+
+   PHP-gettext is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   PHP-gettext is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with PHP-gettext; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+*/
+
+/**
+ * Provides a simple gettext replacement that works independently from
+ * the system's gettext abilities.
+ * It can read MO files and use them for translating strings.
+ * The files are passed to gettext_reader as a Stream (see streams.php)
+ *
+ * This version has the ability to cache all strings and translations to
+ * speed up the string lookup.
+ * While the cache is enabled by default, it can be switched off with the
+ * second parameter in the constructor (e.g. whenusing very large MO files
+ * that you don't want to keep in memory)
+ */
+class gettext_reader {
+  //public:
+   var $error = 0; // public variable that holds error code (0 if no error)
+
+   //private:
+  var $BYTEORDER = 0;        // 0: low endian, 1: big endian
+  var $STREAM = NULL;
+  var $short_circuit = false;
+  var $enable_cache = false;
+  var $originals = NULL;      // offset of original table
+  var $translations = NULL;    // offset of translation table
+  var $pluralheader = NULL;    // cache header field for plural forms
+  var $total = 0;          // total string count
+  var $table_originals = NULL;  // table for original strings (offsets)
+  var $table_translations = NULL;  // table for translated strings (offsets)
+  var $cache_translations = NULL;  // original -> translation mapping
+
+
+  /* Methods */
+
+
+  /**
+   * Reads a 32bit Integer from the Stream
+   *
+   * @access private
+   * @return Integer from the Stream
+   */
+  function readint() {
+      if ($this->BYTEORDER == 0) {
+        // low endian
+        $input=unpack('V', $this->STREAM->read(4));
+        return array_shift($input);
+      } else {
+        // big endian
+        $input=unpack('N', $this->STREAM->read(4));
+        return array_shift($input);
+      }
+    }
+
+  function read($bytes) {
+    return $this->STREAM->read($bytes);
+  }
+
+  /**
+   * Reads an array of Integers from the Stream
+   *
+   * @param int count How many elements should be read
+   * @return Array of Integers
+   */
+  function readintarray($count) {
+    if ($this->BYTEORDER == 0) {
+        // low endian
+        return unpack('V'.$count, $this->STREAM->read(4 * $count));
+      } else {
+        // big endian
+        return unpack('N'.$count, $this->STREAM->read(4 * $count));
+      }
+  }
+
+  /**
+   * Constructor
+   *
+   * @param object Reader the StreamReader object
+   * @param boolean enable_cache Enable or disable caching of strings (default on)
+   */
+  function gettext_reader($Reader, $enable_cache = true) {
+    // If there isn't a StreamReader, turn on short circuit mode.
+    if (! $Reader || isset($Reader->error) ) {
+      $this->short_circuit = true;
+      return;
+    }
+
+    // Caching can be turned off
+    $this->enable_cache = $enable_cache;
+
+    $MAGIC1 = "\x95\x04\x12\xde";
+    $MAGIC2 = "\xde\x12\x04\x95";
+
+    $this->STREAM = $Reader;
+    $magic = $this->read(4);
+    if ($magic == $MAGIC1) {
+      $this->BYTEORDER = 1;
+    } elseif ($magic == $MAGIC2) {
+      $this->BYTEORDER = 0;
+    } else {
+      $this->error = 1; // not MO file
+      return false;
+    }
+
+    // FIXME: Do we care about revision? We should.
+    $revision = $this->readint();
+
+    $this->total = $this->readint();
+    $this->originals = $this->readint();
+    $this->translations = $this->readint();
+  }
+
+  /**
+   * Loads the translation tables from the MO file into the cache
+   * If caching is enabled, also loads all strings into a cache
+   * to speed up translation lookups
+   *
+   * @access private
+   */
+  function load_tables() {
+    if (is_array($this->cache_translations) &&
+      is_array($this->table_originals) &&
+      is_array($this->table_translations))
+      return;
+
+    /* get original and translations tables */
+    if (!is_array($this->table_originals)) {
+      $this->STREAM->seekto($this->originals);
+      $this->table_originals = $this->readintarray($this->total * 2);
+    }
+    if (!is_array($this->table_translations)) {
+      $this->STREAM->seekto($this->translations);
+      $this->table_translations = $this->readintarray($this->total * 2);
+    }
+
+    if ($this->enable_cache) {
+      $this->cache_translations = array ();
+      /* read all strings in the cache */
+      for ($i = 0; $i < $this->total; $i++) {
+        $this->STREAM->seekto($this->table_originals[$i * 2 + 2]);
+        $original = $this->STREAM->read($this->table_originals[$i * 2 + 1]);
+        $this->STREAM->seekto($this->table_translations[$i * 2 + 2]);
+        $translation = $this->STREAM->read($this->table_translations[$i * 2 + 1]);
+        $this->cache_translations[$original] = $translation;
+      }
+    }
+  }
+
+  /**
+   * Returns a string from the "originals" table
+   *
+   * @access private
+   * @param int num Offset number of original string
+   * @return string Requested string if found, otherwise ''
+   */
+  function get_original_string($num) {
+    $length = $this->table_originals[$num * 2 + 1];
+    $offset = $this->table_originals[$num * 2 + 2];
+    if (! $length)
+      return '';
+    $this->STREAM->seekto($offset);
+    $data = $this->STREAM->read($length);
+    return (string)$data;
+  }
+
+  /**
+   * Returns a string from the "translations" table
+   *
+   * @access private
+   * @param int num Offset number of original string
+   * @return string Requested string if found, otherwise ''
+   */
+  function get_translation_string($num) {
+    $length = $this->table_translations[$num * 2 + 1];
+    $offset = $this->table_translations[$num * 2 + 2];
+    if (! $length)
+      return '';
+    $this->STREAM->seekto($offset);
+    $data = $this->STREAM->read($length);
+    return (string)$data;
+  }
+
+  /**
+   * Binary search for string
+   *
+   * @access private
+   * @param string string
+   * @param int start (internally used in recursive function)
+   * @param int end (internally used in recursive function)
+   * @return int string number (offset in originals table)
+   */
+  function find_string($string, $start = -1, $end = -1) {
+    if (($start == -1) or ($end == -1)) {
+      // find_string is called with only one parameter, set start end end
+      $start = 0;
+      $end = $this->total;
+    }
+    if (abs($start - $end) <= 1) {
+      // We're done, now we either found the string, or it doesn't exist
+      $txt = $this->get_original_string($start);
+      if ($string == $txt)
+        return $start;
+      else
+        return -1;
+    } else if ($start > $end) {
+      // start > end -> turn around and start over
+      return $this->find_string($string, $end, $start);
+    } else {
+      // Divide table in two parts
+      $half = (int)(($start + $end) / 2);
+      $cmp = strcmp($string, $this->get_original_string($half));
+      if ($cmp == 0)
+        // string is exactly in the middle => return it
+        return $half;
+      else if ($cmp < 0)
+        // The string is in the upper half
+        return $this->find_string($string, $start, $half);
+      else
+        // The string is in the lower half
+        return $this->find_string($string, $half, $end);
+    }
+  }
+
+  /**
+   * Translates a string
+   *
+   * @access public
+   * @param string string to be translated
+   * @return string translated string (or original, if not found)
+   */
+  function translate($string) {
+    if ($this->short_circuit)
+      return $string;
+    $this->load_tables();
+
+    if ($this->enable_cache) {
+      // Caching enabled, get translated string from cache
+      if (array_key_exists($string, $this->cache_translations))
+        return $this->cache_translations[$string];
+      else
+        return $string;
+    } else {
+      // Caching not enabled, try to find string
+      $num = $this->find_string($string);
+      if ($num == -1)
+        return $string;
+      else
+        return $this->get_translation_string($num);
+    }
+  }
+
+  /**
+   * Sanitize plural form expression for use in PHP eval call.
+   *
+   * @access private
+   * @return string sanitized plural form expression
+   */
+  function sanitize_plural_expression($expr) {
+    // Get rid of disallowed characters.
+    $expr = preg_replace('@[^a-zA-Z0-9_:;\(\)\?\|\&=!<>+*/\%-]@', '', $expr);
+
+    // Add parenthesis for tertiary '?' operator.
+    $expr .= ';';
+    $res = '';
+    $p = 0;
+    for ($i = 0; $i < strlen($expr); $i++) {
+      $ch = $expr[$i];
+      switch ($ch) {
+      case '?':
+        $res .= ' ? (';
+        $p++;
+        break;
+      case ':':
+        $res .= ') : (';
+        break;
+      case ';':
+        $res .= str_repeat( ')', $p) . ';';
+        $p = 0;
+        break;
+      default:
+        $res .= $ch;
+      }
+    }
+    return $res;
+  }
+
+  /**
+   * Parse full PO header and extract only plural forms line.
+   *
+   * @access private
+   * @return string verbatim plural form header field
+   */
+  function extract_plural_forms_header_from_po_header($header) {
+    if (preg_match("/(^|\n)plural-forms: ([^\n]*)\n/i", $header, $regs))
+      $expr = $regs[2];
+    else
+      $expr = "nplurals=2; plural=n == 1 ? 0 : 1;";
+    return $expr;
+  }
+
+  /**
+   * Get possible plural forms from MO header
+   *
+   * @access private
+   * @return string plural form header
+   */
+  function get_plural_forms() {
+    // lets assume message number 0 is header
+    // this is true, right?
+    $this->load_tables();
+
+    // cache header field for plural forms
+    if (! is_string($this->pluralheader)) {
+      if ($this->enable_cache) {
+        $header = $this->cache_translations[""];
+      } else {
+        $header = $this->get_translation_string(0);
+      }
+      $expr = $this->extract_plural_forms_header_from_po_header($header);
+      $this->pluralheader = $this->sanitize_plural_expression($expr);
+    }
+    return $this->pluralheader;
+  }
+
+  /**
+   * Detects which plural form to take
+   *
+   * @access private
+   * @param n count
+   * @return int array index of the right plural form
+   */
+  function select_string($n) {
+    $string = $this->get_plural_forms();
+    $string = str_replace('nplurals',"\$total",$string);
+    $string = str_replace("n",$n,$string);
+    $string = str_replace('plural',"\$plural",$string);
+
+    $total = 0;
+    $plural = 0;
+
+    eval("$string");
+    if ($plural >= $total) $plural = $total - 1;
+    return $plural;
+  }
+
+  /**
+   * Plural version of gettext
+   *
+   * @access public
+   * @param string single
+   * @param string plural
+   * @param string number
+   * @return translated plural form
+   */
+  function ngettext($single, $plural, $number) {
+    if ($this->short_circuit) {
+      if ($number != 1)
+        return $plural;
+      else
+        return $single;
+    }
+
+    // find out the appropriate form
+    $select = $this->select_string($number);
+
+    // this should contains all strings separated by NULLs
+    $key = $single . chr(0) . $plural;
+
+
+    if ($this->enable_cache) {
+      if (! array_key_exists($key, $this->cache_translations)) {
+        return ($number != 1) ? $plural : $single;
+      } else {
+        $result = $this->cache_translations[$key];
+        $list = explode(chr(0), $result);
+        return $list[$select];
+      }
+    } else {
+      $num = $this->find_string($key);
+      if ($num == -1) {
+        return ($number != 1) ? $plural : $single;
+      } else {
+        $result = $this->get_translation_string($num);
+        $list = explode(chr(0), $result);
+        return $list[$select];
+      }
+    }
+  }
+
+  function pgettext($context, $msgid) {
+    $key = $context . chr(4) . $msgid;
+    $ret = $this->translate($key);
+    if (strpos($ret, "\004") !== FALSE) {
+      return $msgid;
+    } else {
+      return $ret;
+    }
+  }
+
+  function npgettext($context, $singular, $plural, $number) {
+    $key = $context . chr(4) . $singular;
+    $ret = $this->ngettext($key, $plural, $number);
+    if (strpos($ret, "\004") !== FALSE) {
+      return $singular;
+    } else {
+      return $ret;
+    }
+
+  }
+}
+
+?>
Index: /branches/version-2_12-dev/data/mtb_constants_init.php
===================================================================
--- /branches/version-2_12-dev/data/mtb_constants_init.php	(revision 21812)
+++ /branches/version-2_12-dev/data/mtb_constants_init.php	(revision 21813)
@@ -32,4 +32,6 @@
 /** アップデート管理用ファイル格納場所 */
 define('UPDATE_HTTP', "http://sv01.ec-cube.net/info/index.php");
+/** 言語コード */
+define('LANG_CODE', "ja");
 /** 文字コード */
 define('CHAR_CODE', "UTF-8");
Index: /branches/version-2_12-dev/data/class_extends/helper_extends/SC_Helper_Locale_Ex.php
===================================================================
--- /branches/version-2_12-dev/data/class_extends/helper_extends/SC_Helper_Locale_Ex.php	(revision 21813)
+++ /branches/version-2_12-dev/data/class_extends/helper_extends/SC_Helper_Locale_Ex.php	(revision 21813)
@@ -0,0 +1,37 @@
+<?php
+/*
+ * This file is part of EC-CUBE
+ *
+ * Copyright(c) 2000-2011 LOCKON CO.,LTD. All Rights Reserved.
+ *
+ * http://www.lockon.co.jp/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+// {{{ requires
+require_once CLASS_REALDIR . 'helper/SC_Helper_Locale.php';
+
+/**
+ * ローカリゼーション関係のヘルパークラス(拡張).
+ *
+ * SC_Helper_Locale をカスタマイズする場合はこのクラスを編集する.
+ *
+ * @package Helper
+ * @author LOCKON CO.,LTD.
+ * @version $Id$
+ */
+class SC_Helper_Locale_Ex extends SC_Helper_Locale {
+}
