I18n.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628
  1. <?php
  2. /**
  3. * Internationalization
  4. *
  5. * PHP 5
  6. *
  7. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  8. * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
  9. *
  10. * Licensed under The MIT License
  11. * Redistributions of files must retain the above copyright notice.
  12. *
  13. * @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
  14. * @link http://cakephp.org CakePHP(tm) Project
  15. * @package Cake.I18n
  16. * @since CakePHP(tm) v 1.2.0.4116
  17. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  18. */
  19. App::uses('CakePlugin', 'Core');
  20. App::uses('L10n', 'I18n');
  21. App::uses('Multibyte', 'I18n');
  22. App::uses('CakeSession', 'Model/Datasource');
  23. /**
  24. * I18n handles translation of Text and time format strings.
  25. *
  26. * @package Cake.I18n
  27. */
  28. class I18n {
  29. /**
  30. * Instance of the L10n class for localization
  31. *
  32. * @var L10n
  33. */
  34. public $l10n = null;
  35. /**
  36. * Default domain of translation
  37. *
  38. * @var string
  39. */
  40. public static $defaultDomain = 'default';
  41. /**
  42. * Current domain of translation
  43. *
  44. * @var string
  45. */
  46. public $domain = null;
  47. /**
  48. * Current category of translation
  49. *
  50. * @var string
  51. */
  52. public $category = 'LC_MESSAGES';
  53. /**
  54. * Current language used for translations
  55. *
  56. * @var string
  57. */
  58. protected $_lang = null;
  59. /**
  60. * Translation strings for a specific domain read from the .mo or .po files
  61. *
  62. * @var array
  63. */
  64. protected $_domains = array();
  65. /**
  66. * Set to true when I18N::_bindTextDomain() is called for the first time.
  67. * If a translation file is found it is set to false again
  68. *
  69. * @var boolean
  70. */
  71. protected $_noLocale = false;
  72. /**
  73. * Translation categories
  74. *
  75. * @var array
  76. */
  77. protected $_categories = array(
  78. 'LC_ALL', 'LC_COLLATE', 'LC_CTYPE', 'LC_MONETARY', 'LC_NUMERIC', 'LC_TIME', 'LC_MESSAGES'
  79. );
  80. /**
  81. * Escape string
  82. *
  83. * @var string
  84. */
  85. protected $_escape = null;
  86. /**
  87. * Constructor, use I18n::getInstance() to get the i18n translation object.
  88. *
  89. * @return void
  90. */
  91. public function __construct() {
  92. $this->l10n = new L10n();
  93. }
  94. /**
  95. * Return a static instance of the I18n class
  96. *
  97. * @return I18n
  98. */
  99. public static function &getInstance() {
  100. static $instance = array();
  101. if (!$instance) {
  102. $instance[0] = new I18n();
  103. }
  104. return $instance[0];
  105. }
  106. /**
  107. * Used by the translation functions in basics.php
  108. * Returns a translated string based on current language and translation files stored in locale folder
  109. *
  110. * @param string $singular String to translate
  111. * @param string $plural Plural string (if any)
  112. * @param string $domain Domain The domain of the translation. Domains are often used by plugin translations
  113. * @param string $category Category The integer value of the category to use.
  114. * @param integer $count Count Count is used with $plural to choose the correct plural form.
  115. * @param string $language Language to translate string to.
  116. * If null it checks for language in session followed by Config.language configuration variable.
  117. * @return string translated string.
  118. */
  119. public static function translate($singular, $plural = null, $domain = null, $category = 6, $count = null, $language = null) {
  120. $_this = I18n::getInstance();
  121. if (strpos($singular, "\r\n") !== false) {
  122. $singular = str_replace("\r\n", "\n", $singular);
  123. }
  124. if ($plural !== null && strpos($plural, "\r\n") !== false) {
  125. $plural = str_replace("\r\n", "\n", $plural);
  126. }
  127. if (is_numeric($category)) {
  128. $_this->category = $_this->_categories[$category];
  129. }
  130. if (empty($language)) {
  131. if (CakeSession::started()) {
  132. $language = CakeSession::read('Config.language');
  133. }
  134. if (empty($language)) {
  135. $language = Configure::read('Config.language');
  136. }
  137. }
  138. if (($_this->_lang && $_this->_lang !== $language) || !$_this->_lang) {
  139. $lang = $_this->l10n->get($language);
  140. $_this->_lang = $lang;
  141. }
  142. if (is_null($domain)) {
  143. $domain = self::$defaultDomain;
  144. }
  145. $_this->domain = $domain . '_' . $_this->l10n->lang;
  146. if (!isset($_this->_domains[$domain][$_this->_lang])) {
  147. $_this->_domains[$domain][$_this->_lang] = Cache::read($_this->domain, '_cake_core_');
  148. }
  149. if (!isset($_this->_domains[$domain][$_this->_lang][$_this->category])) {
  150. $_this->_bindTextDomain($domain);
  151. Cache::write($_this->domain, $_this->_domains[$domain][$_this->_lang], '_cake_core_');
  152. }
  153. if ($_this->category == 'LC_TIME') {
  154. return $_this->_translateTime($singular, $domain);
  155. }
  156. if (!isset($count)) {
  157. $plurals = 0;
  158. } elseif (!empty($_this->_domains[$domain][$_this->_lang][$_this->category]["%plural-c"]) && $_this->_noLocale === false) {
  159. $header = $_this->_domains[$domain][$_this->_lang][$_this->category]["%plural-c"];
  160. $plurals = $_this->_pluralGuess($header, $count);
  161. } else {
  162. if ($count != 1) {
  163. $plurals = 1;
  164. } else {
  165. $plurals = 0;
  166. }
  167. }
  168. if (!empty($_this->_domains[$domain][$_this->_lang][$_this->category][$singular])) {
  169. if (($trans = $_this->_domains[$domain][$_this->_lang][$_this->category][$singular]) || ($plurals) && ($trans = $_this->_domains[$domain][$_this->_lang][$_this->category][$plural])) {
  170. if (is_array($trans)) {
  171. if (isset($trans[$plurals])) {
  172. $trans = $trans[$plurals];
  173. } else {
  174. trigger_error(
  175. __d('cake_dev',
  176. 'Missing plural form translation for "%s" in "%s" domain, "%s" locale. ' .
  177. ' Check your po file for correct plurals and valid Plural-Forms header.',
  178. $singular,
  179. $domain,
  180. $_this->_lang
  181. ),
  182. E_USER_WARNING
  183. );
  184. $trans = $trans[0];
  185. }
  186. }
  187. if (strlen($trans)) {
  188. return $trans;
  189. }
  190. }
  191. }
  192. if (!empty($plurals)) {
  193. return $plural;
  194. }
  195. return $singular;
  196. }
  197. /**
  198. * Clears the domains internal data array. Useful for testing i18n.
  199. *
  200. * @return void
  201. */
  202. public static function clear() {
  203. $self = I18n::getInstance();
  204. $self->_domains = array();
  205. }
  206. /**
  207. * Get the loaded domains cache.
  208. *
  209. * @return array
  210. */
  211. public static function domains() {
  212. $self = I18n::getInstance();
  213. return $self->_domains;
  214. }
  215. /**
  216. * Attempts to find the plural form of a string.
  217. *
  218. * @param string $header Type
  219. * @param integer $n Number
  220. * @return integer plural match
  221. */
  222. protected function _pluralGuess($header, $n) {
  223. if (!is_string($header) || $header === "nplurals=1;plural=0;" || !isset($header[0])) {
  224. return 0;
  225. }
  226. if ($header === "nplurals=2;plural=n!=1;") {
  227. return $n != 1 ? 1 : 0;
  228. } elseif ($header === "nplurals=2;plural=n>1;") {
  229. return $n > 1 ? 1 : 0;
  230. }
  231. if (strpos($header, "plurals=3")) {
  232. if (strpos($header, "100!=11")) {
  233. if (strpos($header, "10<=4")) {
  234. return $n % 10 == 1 && $n % 100 != 11 ? 0 : ($n % 10 >= 2 && $n % 10 <= 4 && ($n % 100 < 10 || $n % 100 >= 20) ? 1 : 2);
  235. } elseif (strpos($header, "100<10")) {
  236. return $n % 10 == 1 && $n % 100 != 11 ? 0 : ($n % 10 >= 2 && ($n % 100 < 10 || $n % 100 >= 20) ? 1 : 2);
  237. }
  238. return $n % 10 == 1 && $n % 100 != 11 ? 0 : ($n != 0 ? 1 : 2);
  239. } elseif (strpos($header, "n==2")) {
  240. return $n == 1 ? 0 : ($n == 2 ? 1 : 2);
  241. } elseif (strpos($header, "n==0")) {
  242. return $n == 1 ? 0 : ($n == 0 || ($n % 100 > 0 && $n % 100 < 20) ? 1 : 2);
  243. } elseif (strpos($header, "n>=2")) {
  244. return $n == 1 ? 0 : ($n >= 2 && $n <= 4 ? 1 : 2);
  245. } elseif (strpos($header, "10>=2")) {
  246. return $n == 1 ? 0 : ($n % 10 >= 2 && $n % 10 <= 4 && ($n % 100 < 10 || $n % 100 >= 20) ? 1 : 2);
  247. }
  248. return $n % 10 == 1 ? 0 : ($n % 10 == 2 ? 1 : 2);
  249. } elseif (strpos($header, "plurals=4")) {
  250. if (strpos($header, "100==2")) {
  251. return $n % 100 == 1 ? 0 : ($n % 100 == 2 ? 1 : ($n % 100 == 3 || $n % 100 == 4 ? 2 : 3));
  252. } elseif (strpos($header, "n>=3")) {
  253. return $n == 1 ? 0 : ($n == 2 ? 1 : ($n == 0 || ($n >= 3 && $n <= 10) ? 2 : 3));
  254. } elseif (strpos($header, "100>=1")) {
  255. return $n == 1 ? 0 : ($n == 0 || ($n % 100 >= 1 && $n % 100 <= 10) ? 1 : ($n % 100 >= 11 && $n % 100 <= 20 ? 2 : 3));
  256. }
  257. } elseif (strpos($header, "plurals=5")) {
  258. return $n == 1 ? 0 : ($n == 2 ? 1 : ($n >= 3 && $n <= 6 ? 2 : ($n >= 7 && $n <= 10 ? 3 : 4)));
  259. }
  260. }
  261. /**
  262. * Binds the given domain to a file in the specified directory.
  263. *
  264. * @param string $domain Domain to bind
  265. * @return string Domain binded
  266. */
  267. protected function _bindTextDomain($domain) {
  268. $this->_noLocale = true;
  269. $core = true;
  270. $merge = array();
  271. $searchPaths = App::path('locales');
  272. $plugins = CakePlugin::loaded();
  273. if (!empty($plugins)) {
  274. foreach ($plugins as $plugin) {
  275. $pluginDomain = Inflector::underscore($plugin);
  276. if ($pluginDomain === $domain) {
  277. $searchPaths[] = CakePlugin::path($plugin) . 'Locale' . DS;
  278. $searchPaths = array_reverse($searchPaths);
  279. break;
  280. }
  281. }
  282. }
  283. foreach ($searchPaths as $directory) {
  284. foreach ($this->l10n->languagePath as $lang) {
  285. $localeDef = $directory . $lang . DS . $this->category;
  286. if (is_file($localeDef)) {
  287. $definitions = self::loadLocaleDefinition($localeDef);
  288. if ($definitions !== false) {
  289. $this->_domains[$domain][$this->_lang][$this->category] = self::loadLocaleDefinition($localeDef);
  290. $this->_noLocale = false;
  291. return $domain;
  292. }
  293. }
  294. if ($core) {
  295. $app = $directory . $lang . DS . $this->category . DS . 'core';
  296. $translations = false;
  297. if (is_file($app . '.mo')) {
  298. $translations = self::loadMo($app . '.mo');
  299. }
  300. if ($translations === false && is_file($app . '.po')) {
  301. $translations = self::loadPo($app . '.po');
  302. }
  303. if ($translations !== false) {
  304. $this->_domains[$domain][$this->_lang][$this->category] = $translations;
  305. $merge[$domain][$this->_lang][$this->category] = $this->_domains[$domain][$this->_lang][$this->category];
  306. $this->_noLocale = false;
  307. $core = null;
  308. }
  309. }
  310. $file = $directory . $lang . DS . $this->category . DS . $domain;
  311. $translations = false;
  312. if (is_file($file . '.mo')) {
  313. $translations = self::loadMo($file . '.mo');
  314. }
  315. if ($translations === false && is_file($file . '.po')) {
  316. $translations = self::loadPo($file . '.po');
  317. }
  318. if ($translations !== false) {
  319. $this->_domains[$domain][$this->_lang][$this->category] = $translations;
  320. $this->_noLocale = false;
  321. break 2;
  322. }
  323. }
  324. }
  325. if (empty($this->_domains[$domain][$this->_lang][$this->category])) {
  326. $this->_domains[$domain][$this->_lang][$this->category] = array();
  327. return $domain;
  328. }
  329. if (isset($this->_domains[$domain][$this->_lang][$this->category][""])) {
  330. $head = $this->_domains[$domain][$this->_lang][$this->category][""];
  331. foreach (explode("\n", $head) as $line) {
  332. $header = strtok($line,":");
  333. $line = trim(strtok("\n"));
  334. $this->_domains[$domain][$this->_lang][$this->category]["%po-header"][strtolower($header)] = $line;
  335. }
  336. if (isset($this->_domains[$domain][$this->_lang][$this->category]["%po-header"]["plural-forms"])) {
  337. $switch = preg_replace("/(?:[() {}\\[\\]^\\s*\\]]+)/", "", $this->_domains[$domain][$this->_lang][$this->category]["%po-header"]["plural-forms"]);
  338. $this->_domains[$domain][$this->_lang][$this->category]["%plural-c"] = $switch;
  339. unset($this->_domains[$domain][$this->_lang][$this->category]["%po-header"]);
  340. }
  341. $this->_domains = Hash::mergeDiff($this->_domains, $merge);
  342. if (isset($this->_domains[$domain][$this->_lang][$this->category][null])) {
  343. unset($this->_domains[$domain][$this->_lang][$this->category][null]);
  344. }
  345. }
  346. return $domain;
  347. }
  348. /**
  349. * Loads the binary .mo file and returns array of translations
  350. *
  351. * @param string $filename Binary .mo file to load
  352. * @return mixed Array of translations on success or false on failure
  353. */
  354. public static function loadMo($filename) {
  355. $translations = false;
  356. // @codingStandardsIgnoreStart
  357. // Binary files extracted makes non-standard local variables
  358. if ($data = file_get_contents($filename)) {
  359. $translations = array();
  360. $header = substr($data, 0, 20);
  361. $header = unpack("L1magic/L1version/L1count/L1o_msg/L1o_trn", $header);
  362. extract($header);
  363. if ((dechex($magic) == '950412de' || dechex($magic) == 'ffffffff950412de') && !$version) {
  364. for ($n = 0; $n < $count; $n++) {
  365. $r = unpack("L1len/L1offs", substr($data, $o_msg + $n * 8, 8));
  366. $msgid = substr($data, $r["offs"], $r["len"]);
  367. unset($msgid_plural);
  368. if (strpos($msgid, "\000")) {
  369. list($msgid, $msgid_plural) = explode("\000", $msgid);
  370. }
  371. $r = unpack("L1len/L1offs", substr($data, $o_trn + $n * 8, 8));
  372. $msgstr = substr($data, $r["offs"], $r["len"]);
  373. if (strpos($msgstr, "\000")) {
  374. $msgstr = explode("\000", $msgstr);
  375. }
  376. $translations[$msgid] = $msgstr;
  377. if (isset($msgid_plural)) {
  378. $translations[$msgid_plural] =& $translations[$msgid];
  379. }
  380. }
  381. }
  382. }
  383. // @codingStandardsIgnoreEnd
  384. return $translations;
  385. }
  386. /**
  387. * Loads the text .po file and returns array of translations
  388. *
  389. * @param string $filename Text .po file to load
  390. * @return mixed Array of translations on success or false on failure
  391. */
  392. public static function loadPo($filename) {
  393. if (!$file = fopen($filename, "r")) {
  394. return false;
  395. }
  396. $type = 0;
  397. $translations = array();
  398. $translationKey = "";
  399. $plural = 0;
  400. $header = "";
  401. do {
  402. $line = trim(fgets($file));
  403. if ($line === "" || $line[0] === "#") {
  404. continue;
  405. }
  406. if (preg_match("/msgid[[:space:]]+\"(.+)\"$/i", $line, $regs)) {
  407. $type = 1;
  408. $translationKey = stripcslashes($regs[1]);
  409. } elseif (preg_match("/msgid[[:space:]]+\"\"$/i", $line, $regs)) {
  410. $type = 2;
  411. $translationKey = "";
  412. } elseif (preg_match("/^\"(.*)\"$/i", $line, $regs) && ($type == 1 || $type == 2 || $type == 3)) {
  413. $type = 3;
  414. $translationKey .= stripcslashes($regs[1]);
  415. } elseif (preg_match("/msgstr[[:space:]]+\"(.+)\"$/i", $line, $regs) && ($type == 1 || $type == 3) && $translationKey) {
  416. $translations[$translationKey] = stripcslashes($regs[1]);
  417. $type = 4;
  418. } elseif (preg_match("/msgstr[[:space:]]+\"\"$/i", $line, $regs) && ($type == 1 || $type == 3) && $translationKey) {
  419. $type = 4;
  420. $translations[$translationKey] = "";
  421. } elseif (preg_match("/^\"(.*)\"$/i", $line, $regs) && $type == 4 && $translationKey) {
  422. $translations[$translationKey] .= stripcslashes($regs[1]);
  423. } elseif (preg_match("/msgid_plural[[:space:]]+\".*\"$/i", $line, $regs)) {
  424. $type = 6;
  425. } elseif (preg_match("/^\"(.*)\"$/i", $line, $regs) && $type == 6 && $translationKey) {
  426. $type = 6;
  427. } elseif (preg_match("/msgstr\[(\d+)\][[:space:]]+\"(.+)\"$/i", $line, $regs) && ($type == 6 || $type == 7) && $translationKey) {
  428. $plural = $regs[1];
  429. $translations[$translationKey][$plural] = stripcslashes($regs[2]);
  430. $type = 7;
  431. } elseif (preg_match("/msgstr\[(\d+)\][[:space:]]+\"\"$/i", $line, $regs) && ($type == 6 || $type == 7) && $translationKey) {
  432. $plural = $regs[1];
  433. $translations[$translationKey][$plural] = "";
  434. $type = 7;
  435. } elseif (preg_match("/^\"(.*)\"$/i", $line, $regs) && $type == 7 && $translationKey) {
  436. $translations[$translationKey][$plural] .= stripcslashes($regs[1]);
  437. } elseif (preg_match("/msgstr[[:space:]]+\"(.+)\"$/i", $line, $regs) && $type == 2 && !$translationKey) {
  438. $header .= stripcslashes($regs[1]);
  439. $type = 5;
  440. } elseif (preg_match("/msgstr[[:space:]]+\"\"$/i", $line, $regs) && !$translationKey) {
  441. $header = "";
  442. $type = 5;
  443. } elseif (preg_match("/^\"(.*)\"$/i", $line, $regs) && $type == 5) {
  444. $header .= stripcslashes($regs[1]);
  445. } else {
  446. unset($translations[$translationKey]);
  447. $type = 0;
  448. $translationKey = "";
  449. $plural = 0;
  450. }
  451. } while (!feof($file));
  452. fclose($file);
  453. $merge[""] = $header;
  454. return array_merge($merge, $translations);
  455. }
  456. /**
  457. * Parses a locale definition file following the POSIX standard
  458. *
  459. * @param string $filename Locale definition filename
  460. * @return mixed Array of definitions on success or false on failure
  461. */
  462. public static function loadLocaleDefinition($filename) {
  463. if (!$file = fopen($filename, "r")) {
  464. return false;
  465. }
  466. $definitions = array();
  467. $comment = '#';
  468. $escape = '\\';
  469. $currentToken = false;
  470. $value = '';
  471. $_this = I18n::getInstance();
  472. while ($line = fgets($file)) {
  473. $line = trim($line);
  474. if (empty($line) || $line[0] === $comment) {
  475. continue;
  476. }
  477. $parts = preg_split("/[[:space:]]+/", $line);
  478. if ($parts[0] === 'comment_char') {
  479. $comment = $parts[1];
  480. continue;
  481. }
  482. if ($parts[0] === 'escape_char') {
  483. $escape = $parts[1];
  484. continue;
  485. }
  486. $count = count($parts);
  487. if ($count == 2) {
  488. $currentToken = $parts[0];
  489. $value = $parts[1];
  490. } elseif ($count == 1) {
  491. $value .= $parts[0];
  492. } else {
  493. continue;
  494. }
  495. $len = strlen($value) - 1;
  496. if ($value[$len] === $escape) {
  497. $value = substr($value, 0, $len);
  498. continue;
  499. }
  500. $mustEscape = array($escape . ',', $escape . ';', $escape . '<', $escape . '>', $escape . $escape);
  501. $replacements = array_map('crc32', $mustEscape);
  502. $value = str_replace($mustEscape, $replacements, $value);
  503. $value = explode(';', $value);
  504. $_this->_escape = $escape;
  505. foreach ($value as $i => $val) {
  506. $val = trim($val, '"');
  507. $val = preg_replace_callback('/(?:<)?(.[^>]*)(?:>)?/', array(&$_this, '_parseLiteralValue'), $val);
  508. $val = str_replace($replacements, $mustEscape, $val);
  509. $value[$i] = $val;
  510. }
  511. if (count($value) == 1) {
  512. $definitions[$currentToken] = array_pop($value);
  513. } else {
  514. $definitions[$currentToken] = $value;
  515. }
  516. }
  517. return $definitions;
  518. }
  519. /**
  520. * Auxiliary function to parse a symbol from a locale definition file
  521. *
  522. * @param string $string Symbol to be parsed
  523. * @return string parsed symbol
  524. */
  525. protected function _parseLiteralValue($string) {
  526. $string = $string[1];
  527. if (substr($string, 0, 2) === $this->_escape . 'x') {
  528. $delimiter = $this->_escape . 'x';
  529. return implode('', array_map('chr', array_map('hexdec',array_filter(explode($delimiter, $string)))));
  530. }
  531. if (substr($string, 0, 2) === $this->_escape . 'd') {
  532. $delimiter = $this->_escape . 'd';
  533. return implode('', array_map('chr', array_filter(explode($delimiter, $string))));
  534. }
  535. if ($string[0] === $this->_escape && isset($string[1]) && is_numeric($string[1])) {
  536. $delimiter = $this->_escape;
  537. return implode('', array_map('chr', array_filter(explode($delimiter, $string))));
  538. }
  539. if (substr($string, 0, 3) === 'U00') {
  540. $delimiter = 'U00';
  541. return implode('', array_map('chr', array_map('hexdec', array_filter(explode($delimiter, $string)))));
  542. }
  543. if (preg_match('/U([0-9a-fA-F]{4})/', $string, $match)) {
  544. return Multibyte::ascii(array(hexdec($match[1])));
  545. }
  546. return $string;
  547. }
  548. /**
  549. * Returns a Time format definition from corresponding domain
  550. *
  551. * @param string $format Format to be translated
  552. * @param string $domain Domain where format is stored
  553. * @return mixed translated format string if only value or array of translated strings for corresponding format.
  554. */
  555. protected function _translateTime($format, $domain) {
  556. if (!empty($this->_domains[$domain][$this->_lang]['LC_TIME'][$format])) {
  557. if (($trans = $this->_domains[$domain][$this->_lang][$this->category][$format])) {
  558. return $trans;
  559. }
  560. }
  561. return $format;
  562. }
  563. }