Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

458 lines (412 sloc) 14.33 kb
<?php
/**
* Short description for file.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2009, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2009, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs
* @since CakePHP(tm) v 1.2.0.4116
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Included libraries.
*/
App::import('Core', 'l10n');
/**
* Short description for file.
*
* @package cake
* @subpackage cake.cake.libs
*/
class I18n extends Object {
/**
* Instance of the I10n class for localization
*
* @var I10n
* @access public
*/
var $l10n = null;
/**
* Current domain of translation
*
* @var string
* @access public
*/
var $domain = null;
/**
* Current category of translation
*
* @var string
* @access public
*/
var $category = 'LC_MESSAGES';
/**
* Current language used for translations
*
* @var string
* @access private
*/
var $__lang = null;
/**
* Translation strings for a specific domain read from the .mo or .po files
*
* @var array
* @access private
*/
var $__domains = array();
/**
* Set to true when I18N::__bindTextDomain() is called for the first time.
* If a translation file is found it is set to false again
*
* @var boolean
* @access private
*/
var $__noLocale = false;
/**
* Determine if $__domains cache should be wrote
*
* @var boolean
* @access private
*/
var $__cache = false;
/**
* Set to true when I18N::__bindTextDomain() is called for the first time.
* If a translation file is found it is set to false again
*
* @var array
* @access private
*/
var $__categories = array(
'LC_ALL', 'LC_COLLATE', 'LC_CTYPE', 'LC_MONETARY', 'LC_NUMERIC', 'LC_TIME', 'LC_MESSAGES'
);
/**
* Return a static instance of the I18n class
*
* @return object I18n
* @access public
*/
function &getInstance() {
static $instance = array();
if (!$instance) {
$instance[0] =& new I18n();
$instance[0]->l10n =& new L10n();
}
return $instance[0];
}
/**
* Used by the translation functions in basics.php
* Can also be used like I18n::translate(); but only if the App::import('I18n'); has been used to load the class.
*
* @param string $singular String to translate
* @param string $plural Plural string (if any)
* @param string $domain Domain
* @param string $category Category
* @param integer $count Count
* @return string translated strings.
* @access public
*/
function translate($singular, $plural = null, $domain = null, $category = 6, $count = null) {
$_this =& I18n::getInstance();
if (strpos($singular, "\r\n") !== false) {
$singular = str_replace("\r\n", "\n", $singular);
}
if ($plural !== null && strpos($plural, "\r\n") !== false) {
$plural = str_replace("\r\n", "\n", $plural);
}
if (is_numeric($category)) {
$_this->category = $_this->__categories[$category];
}
$language = Configure::read('Config.language');
if (!empty($_SESSION['Config']['language'])) {
$language = $_SESSION['Config']['language'];
}
if (($_this->__lang && $_this->__lang !== $language) || !$_this->__lang) {
$lang = $_this->l10n->get($language);
$_this->__lang = $lang;
}
if (is_null($domain)) {
$domain = 'default';
}
$_this->domain = $domain . '_' . $_this->l10n->locale;
if (empty($_this->__domains)) {
$_this->__domains = Cache::read($_this->domain, '_cake_core_');
}
if (!isset($_this->__domains[$_this->category][$_this->__lang][$domain])) {
$_this->__bindTextDomain($domain);
$_this->__cache = true;
}
if (!isset($count)) {
$plurals = 0;
} elseif (!empty($_this->__domains[$_this->category][$_this->__lang][$domain]["%plural-c"]) && $_this->__noLocale === false) {
$header = $_this->__domains[$_this->category][$_this->__lang][$domain]["%plural-c"];
$plurals = $_this->__pluralGuess($header, $count);
} else {
if ($count != 1) {
$plurals = 1;
} else {
$plurals = 0;
}
}
if (!empty($_this->__domains[$_this->category][$_this->__lang][$domain][$singular])) {
if (($trans = $_this->__domains[$_this->category][$_this->__lang][$domain][$singular]) || ($plurals) && ($trans = $_this->__domains[$_this->category][$_this->__lang][$domain][$plural])) {
if (is_array($trans)) {
if (isset($trans[$plurals])) {
$trans = $trans[$plurals];
}
}
if (strlen($trans)) {
$singular = $trans;
return $singular;
}
}
}
if (!empty($plurals)) {
return($plural);
}
return($singular);
}
/**
* Attempts to find the plural form of a string.
*
* @param string $header Type
* @param integrer $n Number
* @return integer plural match
* @access private
*/
function __pluralGuess($header, $n) {
if (!is_string($header) || $header === "nplurals=1;plural=0;" || !isset($header[0])) {
return 0;
}
if ($header === "nplurals=2;plural=n!=1;") {
return $n != 1 ? 1 : 0;
} elseif ($header === "nplurals=2;plural=n>1;") {
return $n > 1 ? 1 : 0;
}
if (strpos($header, "plurals=3")) {
if (strpos($header, "100!=11")) {
if (strpos($header, "10<=4")) {
return $n % 10 == 1 && $n % 100 != 11 ? 0 : ($n % 10 >= 2 && $n % 10 <= 4 && ($n % 100 < 10 || $n % 100 >= 20) ? 1 : 2);
} elseif (strpos($header, "100<10")) {
return $n % 10 == 1 && $n % 100 != 11 ? 0 : ($n % 10 >= 2 && ($n % 100 < 10 || $n % 100 >= 20) ? 1 : 2);
}
return $n % 10 == 1 && $n % 100 != 11 ? 0 : ($n != 0 ? 1 : 2);
} elseif (strpos($header, "n==2")) {
return $n == 1 ? 0 : ($n == 2 ? 1 : 2);
} elseif (strpos($header, "n==0")) {
return $n == 1 ? 0 : ($n == 0 || ($n % 100 > 0 && $n % 100 < 20) ? 1 : 2);
} elseif (strpos($header, "n>=2")) {
return $n == 1 ? 0 : ($n >= 2 && $n <= 4 ? 1 : 2);
} elseif (strpos($header, "10>=2")) {
return $n == 1 ? 0 : ($n % 10 >= 2 && $n % 10 <= 4 && ($n % 100 < 10 || $n % 100 >= 20) ? 1 : 2);
}
return $n % 10 == 1 ? 0 : ($n % 10 == 2 ? 1 : 2);
} elseif (strpos($header, "plurals=4")) {
if (strpos($header, "100==2")) {
return $n % 100 == 1 ? 0 : ($n % 100 == 2 ? 1 : ($n % 100 == 3 || $n % 100 == 4 ? 2 : 3));
} elseif (strpos($header, "n>=3")) {
return $n == 1 ? 0 : ($n == 2 ? 1 : ($n == 0 || ($n >= 3 && $n <= 10) ? 2 : 3));
} elseif (strpos($header, "100>=1")) {
return $n == 1 ? 0 : ($n == 0 || ($n % 100 >= 1 && $n % 100 <= 10) ? 1 : ($n % 100 >= 11 && $n % 100 <= 20 ? 2 : 3));
}
} elseif (strpos($header, "plurals=5")) {
return $n == 1 ? 0 : ($n == 2 ? 1 : ($n >= 3 && $n <= 6 ? 2 : ($n >= 7 && $n <= 10 ? 3 : 4)));
}
}
/**
* Binds the given domain to a file in the specified directory.
*
* @param string $domain Domain to bind
* @return string Domain binded
* @access private
*/
function __bindTextDomain($domain) {
$this->__noLocale = true;
$core = true;
$merge = array();
$searchPaths = App::path('locales');
$plugins = App::objects('plugin');
if (!empty($plugins)) {
foreach ($plugins as $plugin) {
$plugin = Inflector::underscore($plugin);
if ($plugin === $domain) {
$searchPaths[] = App::pluginPath($plugin) . DS . 'locale' . DS;
$searchPaths = array_reverse($searchPaths);
break;
}
}
}
foreach ($searchPaths as $directory) {
foreach ($this->l10n->languagePath as $lang) {
$file = $directory . $lang . DS . $this->category . DS . $domain;
if ($core) {
$app = $directory . $lang . DS . $this->category . DS . 'core';
if (file_exists($fn = "$app.mo")) {
$this->__loadMo($fn, $domain);
$this->__noLocale = false;
$merge[$this->category][$this->__lang][$domain] = $this->__domains[$this->category][$this->__lang][$domain];
$core = null;
} elseif (file_exists($fn = "$app.po") && ($f = fopen($fn, "r"))) {
$this->__loadPo($f, $domain);
$this->__noLocale = false;
$merge[$this->category][$this->__lang][$domain] = $this->__domains[$this->category][$this->__lang][$domain];
$core = null;
}
}
if (file_exists($fn = "$file.mo")) {
$this->__loadMo($fn, $domain);
$this->__noLocale = false;
break 2;
} elseif (file_exists($fn = "$file.po") && ($f = fopen($fn, "r"))) {
$this->__loadPo($f, $domain);
$this->__noLocale = false;
break 2;
}
}
}
if (empty($this->__domains[$this->category][$this->__lang][$domain])) {
$this->__domains[$this->category][$this->__lang][$domain] = array();
return($domain);
}
if ($head = $this->__domains[$this->category][$this->__lang][$domain][""]) {
foreach (explode("\n", $head) as $line) {
$header = strtok($line,":");
$line = trim(strtok("\n"));
$this->__domains[$this->category][$this->__lang][$domain]["%po-header"][strtolower($header)] = $line;
}
if (isset($this->__domains[$this->category][$this->__lang][$domain]["%po-header"]["plural-forms"])) {
$switch = preg_replace("/(?:[() {}\\[\\]^\\s*\\]]+)/", "", $this->__domains[$this->category][$this->__lang][$domain]["%po-header"]["plural-forms"]);
$this->__domains[$this->category][$this->__lang][$domain]["%plural-c"] = $switch;
unset($this->__domains[$this->category][$this->__lang][$domain]["%po-header"]);
}
$this->__domains = Set::pushDiff($this->__domains, $merge);
if (isset($this->__domains[$this->category][$this->__lang][$domain][null])) {
unset($this->__domains[$this->category][$this->__lang][$domain][null]);
}
}
return($domain);
}
/**
* Loads the binary .mo file for translation and sets the values for this translation in the var I18n::__domains
*
* @param resource $file Binary .mo file to load
* @param string $domain Domain where to load file in
* @access private
*/
function __loadMo($file, $domain) {
$data = file_get_contents($file);
if ($data) {
$header = substr($data, 0, 20);
$header = unpack("L1magic/L1version/L1count/L1o_msg/L1o_trn", $header);
extract($header);
if ((dechex($magic) == '950412de' || dechex($magic) == 'ffffffff950412de') && $version == 0) {
for ($n = 0; $n < $count; $n++) {
$r = unpack("L1len/L1offs", substr($data, $o_msg + $n * 8, 8));
$msgid = substr($data, $r["offs"], $r["len"]);
unset($msgid_plural);
if (strpos($msgid, "\000")) {
list($msgid, $msgid_plural) = explode("\000", $msgid);
}
$r = unpack("L1len/L1offs", substr($data, $o_trn + $n * 8, 8));
$msgstr = substr($data, $r["offs"], $r["len"]);
if (strpos($msgstr, "\000")) {
$msgstr = explode("\000", $msgstr);
}
$this->__domains[$this->category][$this->__lang][$domain][$msgid] = $msgstr;
if (isset($msgid_plural)) {
$this->__domains[$this->category][$this->__lang][$domain][$msgid_plural] =& $this->__domains[$this->category][$this->__lang][$domain][$msgid];
}
}
}
}
}
/**
* Loads the text .po file for translation and sets the values for this translation in the var I18n::__domains
*
* @param resource $file Text .po file to load
* @param string $domain Domain to load file in
* @return array Binded domain elements
* @access private
*/
function __loadPo($file, $domain) {
$type = 0;
$translations = array();
$translationKey = "";
$plural = 0;
$header = "";
do {
$line = trim(fgets($file));
if ($line == "" || $line[0] == "#") {
continue;
}
if (preg_match("/msgid[[:space:]]+\"(.+)\"$/i", $line, $regs)) {
$type = 1;
$translationKey = stripcslashes($regs[1]);
} elseif (preg_match("/msgid[[:space:]]+\"\"$/i", $line, $regs)) {
$type = 2;
$translationKey = "";
} elseif (preg_match("/^\"(.*)\"$/i", $line, $regs) && ($type == 1 || $type == 2 || $type == 3)) {
$type = 3;
$translationKey .= stripcslashes($regs[1]);
} elseif (preg_match("/msgstr[[:space:]]+\"(.+)\"$/i", $line, $regs) && ($type == 1 || $type == 3) && $translationKey) {
$translations[$translationKey] = stripcslashes($regs[1]);
$type = 4;
} elseif (preg_match("/msgstr[[:space:]]+\"\"$/i", $line, $regs) && ($type == 1 || $type == 3) && $translationKey) {
$type = 4;
$translations[$translationKey] = "";
} elseif (preg_match("/^\"(.*)\"$/i", $line, $regs) && $type == 4 && $translationKey) {
$translations[$translationKey] .= stripcslashes($regs[1]);
} elseif (preg_match("/msgid_plural[[:space:]]+\".*\"$/i", $line, $regs)) {
$type = 6;
} elseif (preg_match("/^\"(.*)\"$/i", $line, $regs) && $type == 6 && $translationKey) {
$type = 6;
} elseif (preg_match("/msgstr\[(\d+)\][[:space:]]+\"(.+)\"$/i", $line, $regs) && ($type == 6 || $type == 7) && $translationKey) {
$plural = $regs[1];
$translations[$translationKey][$plural] = stripcslashes($regs[2]);
$type = 7;
} elseif (preg_match("/msgstr\[(\d+)\][[:space:]]+\"\"$/i", $line, $regs) && ($type == 6 || $type == 7) && $translationKey) {
$plural = $regs[1];
$translations[$translationKey][$plural] = "";
$type = 7;
} elseif (preg_match("/^\"(.*)\"$/i", $line, $regs) && $type == 7 && $translationKey) {
$translations[$translationKey][$plural] .= stripcslashes($regs[1]);
} elseif (preg_match("/msgstr[[:space:]]+\"(.+)\"$/i", $line, $regs) && $type == 2 && !$translationKey) {
$header .= stripcslashes($regs[1]);
$type = 5;
} elseif (preg_match("/msgstr[[:space:]]+\"\"$/i", $line, $regs) && !$translationKey) {
$header = "";
$type = 5;
} elseif (preg_match("/^\"(.*)\"$/i", $line, $regs) && $type == 5) {
$header .= stripcslashes($regs[1]);
} else {
unset($translations[$translationKey]);
$type = 0;
$translationKey = "";
$plural = 0;
}
} while (!feof($file));
fclose($file);
$merge[""] = $header;
return $this->__domains[$this->category][$this->__lang][$domain] = array_merge($merge ,$translations);
}
/**
* Object destructor
*
* Write cache file if changes have been made to the $__map or $__paths
* @access private
*/
function __destruct() {
if ($this->__cache) {
Cache::write($this->domain, array_filter($this->__domains), '_cake_core_');
}
}
}
?>
Jump to Line
Something went wrong with that request. Please try again.