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

Debug toolbar