summaryrefslogtreecommitdiff
path: root/framework/I18N/core/MessageSource.php
blob: 6563f8c91e517444faace93029f4a6f6e132ef2e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
<?php

/**
 * MessageSource class file.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the BSD License.
 *
 * Copyright(c) 2004 by Qiang Xue. All rights reserved.
 *
 * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue}
 * The latest version of PRADO can be obtained from:
 * {@link http://prado.sourceforge.net/}
 *
 * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
 * @version $Revision: 1.4 $  $Date: 2005/12/17 06:11:28 $
 * @package System.I18N.core
 */

 /**
  * Get the IMessageSource interface.
  */
require_once(dirname(__FILE__).'/IMessageSource.php');

/**
 * Get the MessageCache class file.
 */
require_once(dirname(__FILE__).'/MessageCache.php');

/**
 * Abstract MessageSource class.
 * 
 * The base class for all MessageSources. Message sources must be instantiated
 * using the factory method. The default valid sources are
 *
 *  # XLIFF -- using XML XLIFF format to store the translation messages.
 *  # SQLite -- Store the translation messages in a SQLite database.
 *  # MySQL -- Using a MySQL database to store the messages.
 *  # gettext -- Translated messages are stored in the gettext format.
 *
 * A custom message source can be instantiated by specifying the filename
 * parameter to point to the custom class file. E.g.
 * <code>
 *   $resource = '...'; //custom message source resource
 *   $classfile = '../MessageSource_MySource.php'; //custom message source
 *   $source = MessageSource::factory('MySource', $resource, $classfile);
 * </code>
 * 
 * If you are writting your own message sources, pay attention to the 
 * loadCatalogue method. It details how the resources are loaded and cached.
 * See also the existing message source types as examples.
 * 
 * The following example instantiates a MySQL message source, set the culture,
 * set the cache handler, and use the source in a message formatter. 
 * The messages are store in a database named "messages". The source parameter
 * for the actory method is a PEAR DB style DSN.
 * <code>
 *   $dsn = 'mysql://username:password@localhost/messages';
 *   $source = MessageSource::factory('MySQL', $dsn);
 *   
 *   //set the culture and cache, store the cache in the /tmp directory.
 *   $source->setCulture('en_AU')l
 *   $source->setCache(new MessageCache('/tmp'));
 *   
 *   $formatter = new MessageFormat($source);
 * </code>
 *
 * @author Xiang Wei Zhuo <weizhuo[at]gmail[dot]com>
 * @version v1.0, last update on Fri Dec 24 19:55:49 EST 2004
 * @package System.I18N.core
 */
abstract class MessageSource implements IMessageSource
{	
	/**
	 * The culture name for this message source.
	 * @var string 
	 */
	protected $culture;
	
	/**
	 * Array of translation messages.
	 * @var array 
	 */
	protected $messages = array();

	/**
	 * The source of message translations.
	 * @var string 
	 */
	protected $source;
	
	/**
	 * The translation cache.
	 * @var MessageCache 
	 */
	protected $cache;
	
	protected $untranslated = array();

	/**
	 * Private constructor. MessageSource must be initialized using
	 * the factory method.
	 */
	private function __construct()
	{	
		//throw new Exception('Please use the factory method to instantiate.');
	}
	
	/**
	 * Factory method to instantiate a new MessageSource depending on the
	 * source type. The allowed source types are 'XLIFF', 'SQLite', 
	 * 'MySQL', and 'gettext'. The source parameter is dependent on the 
	 * source type. For 'gettext' and 'XLIFF', it should point to the directory
	 * where the messages are stored. For database types, e.g. 'SQLite' and 
	 * 'MySQL', it should be a PEAR DB style DSN string. 
	 *
 	 * Custom message source are possible by supplying the a filename parameter
 	 * in the factory method.
	 * 
	 * @param string the message source type.
	 * @param string the location of the resource.
	 * @param string the filename of the custom message source.
	 * @return MessageSource a new message source of the specified type. 
	 * @throws InvalidMessageSourceTypeException
	 */
	static function &factory($type, $source='.', $filename='')
	{
		$types = array('XLIFF', 'SQLite', 'MySQL', 'gettext');
		
		if(empty($filename) && in_array($type, $types) == false)
			throw new Exception('Invalid type "'.$type.'", valid types are '.
				implode(', ', $types));
		
		$class = 'MessageSource_'.$type;
		
		if(empty($filename))
			$filename = dirname(__FILE__).'/'.$class.'.php';
						
		if(is_file($filename) == false)
			throw new Exception("File $filename not found");
						
		include_once $filename;
        
		$obj =  new $class($source);
		
		return $obj;
	}
	
	/**
	 * Load a particular message catalogue. Use read() to 
	 * to get the array of messages. The catalogue loading sequence
	 * is as follows
	 *
	 *  # [1] call getCatalogeList($catalogue) to get a list of 
	 *    variants for for the specified $catalogue.
	 *  # [2] for each of the variants, call getSource($variant)
	 *    to get the resource, could be a file or catalogue ID.
	 *  # [3] verify that this resource is valid by calling isValidSource($source)
	 *  # [4] try to get the messages from the cache
	 *  # [5] if a cache miss, call load($source) to load the message array
	 *  # [6] store the messages to cache.
	 *  # [7] continue with the foreach loop, e.g. goto [2].
	 * 
	 * @param string a catalogue to load
	 * @return boolean true if loaded, false otherwise.	 
	 * @see read()
	 */
	function load($catalogue='messages')
	{
		$variants = $this->getCatalogueList($catalogue);
		
		$this->messages = array();
		
		foreach($variants as $variant)
		{
			$source = $this->getSource($variant);
			
			if($this->isValidSource($source) == false) continue;

			$loadData = true;
			
			if($this->cache)
			{
				$data = $this->cache->get($variant, 
					$this->culture, $this->getLastModified($source));
				
				if(is_array($data))
				{
					$this->messages[$variant] = $data;
					$loadData = false;
				}
				unset($data);
			}
			if($loadData)
			{
				$data = &$this->loadData($source);
				if(is_array($data))
				{
					$this->messages[$variant] = $data;
					if($this->cache)
						$this->cache->save($data, $variant, $this->culture);
				}	
				unset($data);
			}
		}
		
		return true;
	}	
	
	/**
	 * Get the array of messages.
	 * @param parameter
	 * @return array translation messages. 
	 */
	public function read()
	{
		return $this->messages;
	}
		
	/**
	 * Get the cache handler for this source.
	 * @return MessageCache cache handler
	 */
	public function getCache()
	{
		return $this->cache;
	}
	
	/**
	 * Set the cache handler for caching the messages.
	 * @param MessageCache the cache handler.
	 */
	public function setCache(MessageCache $cache)
	{
		$this->cache = $cache;
	}
	
	/**
	 * Add a untranslated message to the source. Need to call save()
	 * to save the messages to source.
	 * @param string message to add
	 */	
	public function append($message)
	{
		if(!in_array($message, $this->untranslated))
			$this->untranslated[] = $message;
	}
	
	/**
	 * Set the culture for this message source.
	 * @param string culture name
	 */
	public function setCulture($culture)
	{
		$this->culture = $culture;
	}	
	
	/**
	 * Get the culture identifier for the source.
	 * @return string culture identifier. 
	 */
	public function getCulture()
	{
		return $this->culture;
	}	

	/**
	 * Get the last modified unix-time for this particular catalogue+variant.
	 * @param string catalogue+variant
	 * @return int last modified in unix-time format.
	 */
	protected function getLastModified($source)
	{
		return 0;
	}
	
	/**
	 * Load the message for a particular catalogue+variant.
	 * This methods needs to implemented by subclasses.
	 * @param string catalogue+variant.
	 * @return array of translation messages. 
	 */
	protected function &loadData($variant)
	{
		return array();
	}
	
	/**
	 * Get the source, this could be a filename or database ID.
	 * @param string catalogue+variant
	 * @return string the resource key 
	 */
	protected function getSource($variant)
	{
		return $variant;
	}
	
	/**
	 * Determine if the source is valid.
	 * @param string catalogue+variant
	 * @return boolean true if valid, false otherwise. 
	 */
	protected function isValidSource($source)
	{
		return false;
	}
	
	/**
	 * Get all the variants of a particular catalogue.
	 * This method must be implemented by subclasses.
	 * @param string catalogue name
	 * @return array list of all variants for this catalogue. 
	 */	
	protected function getCatalogueList($catalogue)
	{
		return array();
	}		
}


/**
 * TMessageSourceIOException thrown when unable to modify message source
 * data.
 *
 * @author Wei Zhuo<weizhuo[at]gmail[dot]com>
 * @version $Revision: 1.4 $  $Date: 2005/12/17 06:11:28 ${DATE} ${TIME} $
 * @package System.I18N.core
 */
class TMessageSourceIOException extends TException
{

}
?>