sfI18N.class.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. <?php
  2. /*
  3. * This file is part of the symfony package.
  4. * (c) 2004-2006 Fabien Potencier <fabien.potencier@symfony-project.com>
  5. *
  6. * For the full copyright and license information, please view the LICENSE
  7. * file that was distributed with this source code.
  8. */
  9. /**
  10. * sfI18N wraps the core i18n classes for a symfony context.
  11. *
  12. * @package symfony
  13. * @subpackage i18n
  14. * @author Fabien Potencier <fabien.potencier@symfony-project.com>
  15. * @version SVN: $Id: sfI18N.class.php 13482 2008-11-29 14:35:54Z fabien $
  16. */
  17. class sfI18N
  18. {
  19. protected
  20. $configuration = null,
  21. $dispatcher = null,
  22. $cache = null,
  23. $options = array(),
  24. $culture = 'en',
  25. $messageSource = null,
  26. $messageFormat = null;
  27. /**
  28. * Class constructor.
  29. *
  30. * @see initialize()
  31. */
  32. public function __construct(sfApplicationConfiguration $configuration, sfCache $cache = null, $options = array())
  33. {
  34. $this->initialize($configuration, $cache, $options);
  35. }
  36. /**
  37. * Initializes this class.
  38. *
  39. * Available options:
  40. *
  41. * * culture: The culture
  42. * * source: The i18n source (XLIFF by default)
  43. * * debug: Whether to enable debug or not (false by default)
  44. * * database: The database name (default by default)
  45. * * untranslated_prefix: The prefix to use when a message is not translated
  46. * * untranslated_suffix: The suffix to use when a message is not translated
  47. *
  48. * @param sfApplicationConfiguration $configuration A sfApplicationConfiguration instance
  49. * @param sfCache $cache A sfCache instance
  50. * @param array $options An array of options
  51. */
  52. public function initialize(sfApplicationConfiguration $configuration, sfCache $cache = null, $options = array())
  53. {
  54. $this->configuration = $configuration;
  55. $this->dispatcher = $configuration->getEventDispatcher();
  56. $this->cache = $cache;
  57. if (isset($options['culture']))
  58. {
  59. $this->culture = $options['culture'];
  60. unset($options['culture']);
  61. }
  62. $this->options = array_merge(array(
  63. 'source' => 'XLIFF',
  64. 'debug' => false,
  65. 'database' => 'default',
  66. 'untranslated_prefix' => '[T]',
  67. 'untranslated_suffix' => '[/T]',
  68. ), $options);
  69. $this->dispatcher->connect('user.change_culture', array($this, 'listenToChangeCultureEvent'));
  70. if($this->isMessageSourceFileBased($this->options['source']))
  71. {
  72. $this->dispatcher->connect('controller.change_action', array($this, 'listenToChangeActionEvent'));
  73. }
  74. }
  75. /**
  76. * Returns the initialization options
  77. *
  78. * @return array The options used to initialize sfI18n
  79. */
  80. public function getOptions()
  81. {
  82. return $this->options;
  83. }
  84. /**
  85. * Returns the configuration instance.
  86. *
  87. * @return sfApplicationConfiguration An sfApplicationConfiguration instance
  88. */
  89. public function getConfiguration()
  90. {
  91. return $this->configuration;
  92. }
  93. /**
  94. * Sets the message source.
  95. *
  96. * @param mixed $dirs An array of i18n directories if message source is a sfMessageSource_File subclass, null otherwise
  97. * @param string $culture The culture
  98. */
  99. public function setMessageSource($dirs, $culture = null)
  100. {
  101. if (is_null($dirs))
  102. {
  103. $this->messageSource = $this->createMessageSource();
  104. }
  105. else
  106. {
  107. $this->messageSource = sfMessageSource::factory('Aggregate', array_map(array($this, 'createMessageSource'), $dirs));
  108. }
  109. if (!is_null($this->cache))
  110. {
  111. $this->messageSource->setCache($this->cache);
  112. }
  113. if (!is_null($culture))
  114. {
  115. $this->setCulture($culture);
  116. }
  117. else
  118. {
  119. $this->messageSource->setCulture($this->culture);
  120. }
  121. $this->messageFormat = null;
  122. }
  123. /**
  124. * Returns a new message source.
  125. *
  126. * @param mixed $dir An array of i18n directories to create a XLIFF or gettext message source, null otherwise
  127. *
  128. * @return sfMessageSource A sfMessageSource object
  129. */
  130. public function createMessageSource($dir = null)
  131. {
  132. return sfMessageSource::factory($this->options['source'], self::isMessageSourceFileBased($this->options['source']) ? $dir : $this->options['database']);
  133. }
  134. /**
  135. * Gets the current culture for i18n format objects.
  136. *
  137. * @return string The culture
  138. */
  139. public function getCulture()
  140. {
  141. return $this->culture;
  142. }
  143. /**
  144. * Sets the current culture for i18n format objects.
  145. *
  146. * @param string $culture The culture
  147. */
  148. public function setCulture($culture)
  149. {
  150. $this->culture = $culture;
  151. // change user locale for formatting, collation, and internal error messages
  152. setlocale(LC_ALL, 'en_US.utf8', 'en_US.UTF8', 'en_US.utf-8', 'en_US.UTF-8');
  153. setlocale(LC_COLLATE, $culture.'.utf8', $culture.'.UTF8', $culture.'.utf-8', $culture.'.UTF-8');
  154. setlocale(LC_CTYPE, $culture.'.utf8', $culture.'.UTF8', $culture.'.utf-8', $culture.'.UTF-8');
  155. setlocale(LC_MONETARY, $culture.'.utf8', $culture.'.UTF8', $culture.'.utf-8', $culture.'.UTF-8');
  156. setlocale(LC_TIME, $culture.'.utf8', $culture.'.UTF8', $culture.'.utf-8', $culture.'.UTF-8');
  157. if ($this->messageSource)
  158. {
  159. $this->messageSource->setCulture($culture);
  160. $this->messageFormat = null;
  161. }
  162. }
  163. /**
  164. * Gets the message source.
  165. *
  166. * @return sfMessageSource A sfMessageSource object
  167. */
  168. public function getMessageSource()
  169. {
  170. if (!isset($this->messageSource))
  171. {
  172. $dirs = ($this->isMessageSourceFileBased($this->options['source'])) ? $this->configuration->getI18NGlobalDirs() : null;
  173. $this->setMessageSource($dirs, $this->culture);
  174. }
  175. return $this->messageSource;
  176. }
  177. /**
  178. * Gets the message format.
  179. *
  180. * @return sfMessageFormat A sfMessageFormat object
  181. */
  182. public function getMessageFormat()
  183. {
  184. if (!isset($this->messageFormat))
  185. {
  186. $this->messageFormat = new sfMessageFormat($this->getMessageSource(), sfConfig::get('sf_charset'));
  187. if ($this->options['debug'])
  188. {
  189. $this->messageFormat->setUntranslatedPS(array($this->options['untranslated_prefix'], $this->options['untranslated_suffix']));
  190. }
  191. }
  192. return $this->messageFormat;
  193. }
  194. /**
  195. * Gets the translation for the given string
  196. *
  197. * @param string $string The string to translate
  198. * @param array $args An array of arguments for the translation
  199. * @param string $catalogue The catalogue name
  200. *
  201. * @return string The translated string
  202. */
  203. public function __($string, $args = array(), $catalogue = 'messages')
  204. {
  205. return $this->getMessageFormat()->format($string, $args, $catalogue);
  206. }
  207. /**
  208. * Gets a country name.
  209. *
  210. * @param string $iso The ISO code
  211. * @param string $culture The culture for the translation
  212. *
  213. * @return string The country name
  214. */
  215. public function getCountry($iso, $culture = null)
  216. {
  217. $c = sfCultureInfo::getInstance(is_null($culture) ? $this->culture : $culture);
  218. $countries = $c->getCountries();
  219. return (array_key_exists($iso, $countries)) ? $countries[$iso] : '';
  220. }
  221. /**
  222. * Gets a native culture name.
  223. *
  224. * @param string $culture The culture
  225. *
  226. * @return string The culture name
  227. */
  228. public function getNativeName($culture)
  229. {
  230. return sfCultureInfo::getInstance($culture)->getNativeName();
  231. }
  232. /**
  233. * Returns a timestamp from a date with time formatted with a given culture.
  234. *
  235. * @param string $dateTime The formatted date with time as string
  236. * @param string $culture The culture
  237. *
  238. * @return integer The timestamp
  239. */
  240. public function getTimestampForCulture($dateTime, $culture = null)
  241. {
  242. list($day, $month, $year) = $this->getDateForCulture($dateTime, is_null($culture) ? $this->culture : $culture);
  243. list($hour, $minute) = $this->getTimeForCulture($dateTime, is_null($culture) ? $this->culture : $culture);
  244. return is_null($day) ? null : mktime($hour, $minute, 0, $month, $day, $year);
  245. }
  246. /**
  247. * Returns the day, month and year from a date formatted with a given culture.
  248. *
  249. * @param string $date The formatted date as string
  250. * @param string $culture The culture
  251. *
  252. * @return array An array with the day, month and year
  253. */
  254. public function getDateForCulture($date, $culture = null)
  255. {
  256. if (!$date)
  257. {
  258. return null;
  259. }
  260. $dateFormatInfo = @sfDateTimeFormatInfo::getInstance(is_null($culture) ? $this->culture : $culture);
  261. $dateFormat = $dateFormatInfo->getShortDatePattern();
  262. // We construct the regexp based on date format
  263. $dateRegexp = preg_replace('/[dmy]+/i', '(\d+)', $dateFormat);
  264. // We parse date format to see where things are (m, d, y)
  265. $a = array(
  266. 'd' => strpos($dateFormat, 'd'),
  267. 'm' => strpos($dateFormat, 'M'),
  268. 'y' => strpos($dateFormat, 'y'),
  269. );
  270. $tmp = array_flip($a);
  271. ksort($tmp);
  272. $i = 0;
  273. $c = array();
  274. foreach ($tmp as $value) $c[++$i] = $value;
  275. $datePositions = array_flip($c);
  276. // We find all elements
  277. if (preg_match("~$dateRegexp~", $date, $matches))
  278. {
  279. // We get matching timestamp
  280. return array($matches[$datePositions['d']], $matches[$datePositions['m']], $matches[$datePositions['y']]);
  281. }
  282. else
  283. {
  284. return null;
  285. }
  286. }
  287. /**
  288. * Returns the hour, minute from a date formatted with a given culture.
  289. *
  290. * @param string $date The formatted date as string
  291. * @param string $culture The culture
  292. *
  293. * @return array An array with the hour and minute
  294. */
  295. public function getTimeForCulture($time, $culture)
  296. {
  297. if (!$time) return 0;
  298. $culture = is_null($culture) ? $this->culture : $culture;
  299. $timeFormatInfo = @sfDateTimeFormatInfo::getInstance($culture);
  300. $timeFormat = $timeFormatInfo->getShortTimePattern();
  301. // We construct the regexp based on time format
  302. $timeRegexp = preg_replace(array('/[^hm:]+/i', '/[hm]+/i'), array('', '(\d+)'), $timeFormat);
  303. // We parse time format to see where things are (h, m)
  304. $a = array(
  305. 'h' => strpos($timeFormat, 'H') !== false ? strpos($timeFormat, 'H') : strpos($timeFormat, 'h'),
  306. 'm' => strpos($timeFormat, 'm')
  307. );
  308. $tmp = array_flip($a);
  309. ksort($tmp);
  310. $i = 0;
  311. $c = array();
  312. foreach ($tmp as $value) $c[++$i] = $value;
  313. $timePositions = array_flip($c);
  314. // We find all elements
  315. if (preg_match("~$timeRegexp~", $time, $matches))
  316. {
  317. // We get matching timestamp
  318. return array($matches[$timePositions['h']], $matches[$timePositions['m']]);
  319. }
  320. else
  321. {
  322. return null;
  323. }
  324. }
  325. /**
  326. * Returns true if messages are stored in a file.
  327. *
  328. * @param string $source The source name
  329. *
  330. * @return Boolean true if messages are stored in a file, false otherwise
  331. */
  332. static public function isMessageSourceFileBased($source)
  333. {
  334. $class = 'sfMessageSource_'.$source;
  335. return class_exists($class) && is_subclass_of($class, 'sfMessageSource_File');
  336. }
  337. /**
  338. * Listens to the user.change_culture event.
  339. *
  340. * @param sfEvent $event An sfEvent instance
  341. *
  342. */
  343. public function listenToChangeCultureEvent(sfEvent $event)
  344. {
  345. // change the message format object with the new culture
  346. $this->setCulture($event['culture']);
  347. }
  348. /**
  349. * Listens to the controller.change_action event.
  350. *
  351. * @param sfEvent $event An sfEvent instance
  352. *
  353. */
  354. public function listenToChangeActionEvent(sfEvent $event)
  355. {
  356. // change message source directory to our module
  357. $this->setMessageSource($this->configuration->getI18NDirs($event['module']));
  358. }
  359. }