sfMessageSource.class.php 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. <?php
  2. /**
  3. * sfMessageSource class file.
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the BSD License.
  7. *
  8. * Copyright(c) 2004 by Qiang Xue. All rights reserved.
  9. *
  10. * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue}
  11. * The latest version of PRADO can be obtained from:
  12. * {@link http://prado.sourceforge.net/}
  13. *
  14. * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
  15. * @version $Id: sfMessageSource.class.php 11836 2008-09-29 08:07:31Z fabien $
  16. * @package symfony
  17. * @subpackage i18n
  18. */
  19. /**
  20. * Abstract sfMessageSource class.
  21. *
  22. * The base class for all sfMessageSources. Message sources must be instantiated
  23. * using the factory method. The default valid sources are
  24. *
  25. * # XLIFF -- using XML XLIFF format to store the translation messages.
  26. * # SQLite -- Store the translation messages in a SQLite database.
  27. * # MySQL -- Using a MySQL database to store the messages.
  28. * # gettext -- Translated messages are stored in the gettext format.
  29. *
  30. * A custom message source can be instantiated by specifying the filename
  31. * parameter to point to the custom class file. E.g.
  32. * <code>
  33. * $resource = '...'; //custom message source resource
  34. * $classfile = '../sfMessageSource_MySource.php'; //custom message source
  35. * $source = sfMessageSource::factory('MySource', $resource, $classfile);
  36. * </code>
  37. *
  38. * If you are writting your own message sources, pay attention to the
  39. * loadCatalogue method. It details how the resources are loaded and cached.
  40. * See also the existing message source types as examples.
  41. *
  42. * The following example instantiates a MySQL message source, set the culture,
  43. * set the cache handler, and use the source in a message formatter.
  44. * The messages are store in a database named "messages". The source parameter
  45. * for the actory method is a PEAR DB style DSN.
  46. * <code>
  47. * $dsn = 'mysql://username:password@localhost/messages';
  48. * $source = sfMessageSource::factory('MySQL', $dsn);
  49. *
  50. * //set the culture and cache, store the cache in the /tmp directory.
  51. * $source->setCulture('en_AU')l
  52. * $source->setCache(new sfMessageCache(new sfFileCache(array('/tmp'))));
  53. *
  54. * $formatter = new sfMessageFormat($source);
  55. * </code>
  56. *
  57. * @author Xiang Wei Zhuo <weizhuo[at]gmail[dot]com>
  58. * @version v1.0, last update on Fri Dec 24 19:55:49 EST 2004
  59. * @package symfony
  60. * @subpackage i18n
  61. */
  62. abstract class sfMessageSource implements sfIMessageSource
  63. {
  64. /**
  65. * The culture name for this message source.
  66. * @var string
  67. */
  68. protected $culture;
  69. /**
  70. * Array of translation messages.
  71. * @var array
  72. */
  73. protected $messages = array();
  74. /**
  75. * The source of message translations.
  76. * @var string
  77. */
  78. protected $source;
  79. /**
  80. * The translation cache.
  81. * @var sfMessageCache
  82. */
  83. protected $cache;
  84. protected $untranslated = array();
  85. /**
  86. * Private constructor. sfMessageSource must be initialized using
  87. * the factory method.
  88. */
  89. private function __construct()
  90. {
  91. //throw new sfException('Please use the factory method to instantiate.');
  92. }
  93. /**
  94. * Factory method to instantiate a new sfMessageSource depending on the
  95. * source type. The built-in source types are 'XLIFF', 'SQLite',
  96. * 'MySQL', 'gettext', and 'Aggregate'.
  97. * The source parameter is dependent on the source type.
  98. * For 'gettext' and 'XLIFF', it should point to the directory
  99. * where the messages are stored. For database types, e.g. 'SQLite' and
  100. * 'MySQL', it should be a PEAR DB style DSN string.
  101. *
  102. * Custom message source are possible by supplying the a filename parameter
  103. * in the factory method.
  104. *
  105. * @param string $type the message source type.
  106. * @param string $source the location of the resource.
  107. * @param string $filename the filename of the custom message source.
  108. * @return sfMessageSource a new message source of the specified type.
  109. * @throws sfException
  110. */
  111. static function factory($type, $source = '.', $filename = '')
  112. {
  113. if ($filename)
  114. {
  115. if (!is_file($filename))
  116. {
  117. throw new sfException(sprintf("File %s not found.", $filename));
  118. }
  119. include_once($filename);
  120. }
  121. $class = 'sfMessageSource_'.$type;
  122. if (!class_exists($class))
  123. {
  124. throw new sfException(sprintf('Unable to find type "%s".', $type));
  125. }
  126. return new $class($source);
  127. }
  128. /**
  129. * Loads a particular message catalogue. Use read() to
  130. * to get the array of messages. The catalogue loading sequence
  131. * is as follows:
  132. *
  133. * # [1] Call getCatalogueList($catalogue) to get a list of variants for for the specified $catalogue.
  134. * # [2] For each of the variants, call getSource($variant) to get the resource, could be a file or catalogue ID.
  135. * # [3] Verify that this resource is valid by calling isValidSource($source)
  136. * # [4] Try to get the messages from the cache
  137. * # [5] If a cache miss, call load($source) to load the message array
  138. * # [6] Store the messages to cache.
  139. * # [7] Continue with the foreach loop, e.g. goto [2].
  140. *
  141. * @param string $catalogue a catalogue to load
  142. * @return boolean always true
  143. * @see read()
  144. */
  145. function load($catalogue = 'messages')
  146. {
  147. $variants = $this->getCatalogueList($catalogue);
  148. $this->messages = array();
  149. foreach ($variants as $variant)
  150. {
  151. $source = $this->getSource($variant);
  152. if ($this->isValidSource($source) == false)
  153. {
  154. continue;
  155. }
  156. $loadData = true;
  157. if ($this->cache)
  158. {
  159. $lastModified = $this->getLastModified($source);
  160. if ($lastModified >= 0 && $lastModified < $this->cache->getLastModified($variant.':'.$this->culture))
  161. {
  162. $data = unserialize($this->cache->get($variant.':'.$this->culture));
  163. if (is_array($data))
  164. {
  165. $this->messages[$variant] = $data;
  166. $loadData = false;
  167. }
  168. unset($data);
  169. }
  170. }
  171. if ($loadData)
  172. {
  173. $data = &$this->loadData($source);
  174. if (is_array($data))
  175. {
  176. $this->messages[$variant] = $data;
  177. if ($this->cache)
  178. {
  179. $this->cache->set($variant.':'.$this->culture, serialize($data));
  180. }
  181. }
  182. unset($data);
  183. }
  184. }
  185. return true;
  186. }
  187. /**
  188. * Gets the array of messages.
  189. *
  190. * @return array translation messages.
  191. */
  192. public function read()
  193. {
  194. return $this->messages;
  195. }
  196. /**
  197. * Gets the cache handler for this source.
  198. *
  199. * @return sfMessageCache cache handler
  200. */
  201. public function getCache()
  202. {
  203. return $this->cache;
  204. }
  205. /**
  206. * Sets the cache handler for caching the messages.
  207. *
  208. * @param sfCache $cache the cache handler.
  209. */
  210. public function setCache(sfCache $cache)
  211. {
  212. $this->cache = $cache;
  213. }
  214. /**
  215. * Adds a untranslated message to the source. Need to call save()
  216. * to save the messages to source.
  217. *
  218. * @param string $message message to add
  219. */
  220. public function append($message)
  221. {
  222. if (!in_array($message, $this->untranslated))
  223. {
  224. $this->untranslated[] = $message;
  225. }
  226. }
  227. /**
  228. * Sets the culture for this message source.
  229. *
  230. * @param string $culture culture name
  231. */
  232. public function setCulture($culture)
  233. {
  234. $this->culture = $culture;
  235. }
  236. /**
  237. * Gets the culture identifier for the source.
  238. *
  239. * @return string culture identifier.
  240. */
  241. public function getCulture()
  242. {
  243. return $this->culture;
  244. }
  245. /**
  246. * Gets the last modified unix-time for this particular catalogue+variant.
  247. *
  248. * @param string $source catalogue+variant
  249. * @return int last modified in unix-time format.
  250. */
  251. protected function getLastModified($source)
  252. {
  253. return 0;
  254. }
  255. /**
  256. * Loads the message for a particular catalogue+variant.
  257. * This methods needs to implemented by subclasses.
  258. *
  259. * @param string $variant catalogue+variant.
  260. * @return array of translation messages.
  261. */
  262. public function &loadData($variant)
  263. {
  264. return array();
  265. }
  266. /**
  267. * Gets the source, this could be a filename or database ID.
  268. *
  269. * @param string $variant catalogue+variant
  270. * @return string the resource key
  271. */
  272. public function getSource($variant)
  273. {
  274. return $variant;
  275. }
  276. /**
  277. * Determines if the source is valid.
  278. *
  279. * @param string $source catalogue+variant
  280. * @return boolean true if valid, false otherwise.
  281. */
  282. public function isValidSource($source)
  283. {
  284. return false;
  285. }
  286. /**
  287. * Gets all the variants of a particular catalogue.
  288. * This method must be implemented by subclasses.
  289. *
  290. * @param string $catalogue catalogue name
  291. * @return array list of all variants for this catalogue.
  292. */
  293. public function getCatalogueList($catalogue)
  294. {
  295. return array();
  296. }
  297. }