Subversion-Projekte lars-tiefland.prado

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
 
3
/**
4
 * CultureInfo class file.
5
 *
6
 * This program is free software; you can redistribute it and/or modify
7
 * it under the terms of the BSD License.
8
 *
9
 * Copyright(c) 2004 by Qiang Xue. All rights reserved.
10
 *
11
 * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue}
12
 * The latest version of PRADO can be obtained from:
13
 * {@link http://prado.sourceforge.net/}
14
 *
15
 * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
16
 * @version $Id: CultureInfo.php 2541 2008-10-21 15:05:13Z qiang.xue $
17
 * @package System.I18N.core
18
 */
19
 
20
/**
21
 * CultureInfo class.
22
 *
23
 * Represents information about a specific culture including the
24
 * names of the culture, the calendar used, as well as access to
25
 * culture-specific objects that provide methods for common operations,
26
 * such as formatting dates, numbers, and currency.
27
 *
28
 * The CultureInfo class holds culture-specific information, such as the
29
 * associated language, sublanguage, country/region, calendar, and cultural
30
 * conventions. This class also provides access to culture-specific
31
 * instances of DateTimeFormatInfo and NumberFormatInfo. These objects
32
 * contain the information required for culture-specific operations,
33
 * such as formatting dates, numbers and currency.
34
 *
35
 * The culture names follow the format "<languagecode>_<country/regioncode>",
36
 * where <languagecode> is a lowercase two-letter code derived from ISO 639
37
 * codes. You can find a full list of the ISO-639 codes at
38
 * http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt
39
 *
40
 * The <country/regioncode2> is an uppercase two-letter code derived from
41
 * ISO 3166. A copy of ISO-3166 can be found at
42
 * http://www.chemie.fu-berlin.de/diverse/doc/ISO_3166.html
43
 *
44
 * For example, Australian English is "en_AU".
45
 *
46
 * @author Xiang Wei Zhuo <weizhuo[at]gmail[dot]com>
47
 * @version $Id: CultureInfo.php 2541 2008-10-21 15:05:13Z qiang.xue $
48
 * @package System.I18N.core
49
 */
50
class CultureInfo
51
{
52
	/**
53
	 * ICU data filename extension.
54
	 * @var string
55
	 */
56
	private $dataFileExt = '.dat';
57
 
58
	/**
59
	 * The ICU data array.
60
	 * @var array
61
	 */
62
	private $data = array();
63
 
64
	/**
65
	 * The current culture.
66
	 * @var string
67
	 */
68
	private $culture;
69
 
70
	/**
71
	 * Directory where the ICU data is stored.
72
	 * @var string
73
	 */
74
	private $dataDir;
75
 
76
	/**
77
	 * A list of ICU date files loaded.
78
	 * @var array
79
	 */
80
	private $dataFiles = array();
81
 
82
	/**
83
	 * The current date time format info.
84
	 * @var DateTimeFormatInfo
85
	 */
86
	private $dateTimeFormat;
87
 
88
	/**
89
	 * The current number format info.
90
	 * @var NumberFormatInfo
91
	 */
92
	private $numberFormat;
93
 
94
	/**
95
	 * A list of properties that are accessable/writable.
96
	 * @var array
97
	 */
98
	protected $properties = array();
99
 
100
	/**
101
	 * Culture type, all.
102
	 * @see getCultures()
103
	 * @var int
104
	 */
105
	const ALL = 0;
106
 
107
	/**
108
	 * Culture type, neutral.
109
	 * @see getCultures()
110
	 * @var int
111
	 */
112
	const NEUTRAL = 1;
113
 
114
	/**
115
	 * Culture type, specific.
116
	 * @see getCultures()
117
	 * @var int
118
	 */
119
	const SPECIFIC = 2;
120
 
121
	/**
122
	 * Display the culture name.
123
	 * @return string the culture name.
124
	 * @see getName()
125
	 */
126
	function __toString()
127
	{
128
		return $this->getName();
129
	}
130
 
131
 
132
	/**
133
	 * Allow functions that begins with 'set' to be called directly
134
	 * as an attribute/property to retrieve the value.
135
	 * @return mixed
136
	 */
137
	function __get($name)
138
	{
139
		$getProperty = 'get'.$name;
140
		if(in_array($getProperty, $this->properties))
141
			return $this->$getProperty();
142
		else
143
			throw new Exception('Property '.$name.' does not exists.');
144
	}
145
 
146
	/**
147
	 * Allow functions that begins with 'set' to be called directly
148
	 * as an attribute/property to set the value.
149
	 */
150
	function __set($name, $value)
151
	{
152
		$setProperty = 'set'.$name;
153
		if(in_array($setProperty, $this->properties))
154
			$this->$setProperty($value);
155
		else
156
			throw new Exception('Property '.$name.' can not be set.');
157
	}
158
 
159
 
160
	/**
161
	 * Initializes a new instance of the CultureInfo class based on the
162
	 * culture specified by name. E.g. <code>new CultureInfo('en_AU');</cdoe>
163
	 * The culture indentifier must be of the form
164
	 * "language_(country/region/variant)".
165
	 * @param string a culture name, e.g. "en_AU".
166
	 * @return return new CultureInfo.
167
	 */
168
	function __construct($culture='en')
169
	{
170
		$this->properties = get_class_methods($this);
171
 
172
		if(empty($culture))
173
			$culture = 'en';
174
 
175
		$this->dataDir = $this->dataDir();
176
		$this->dataFileExt = $this->fileExt();
177
 
178
		$this->setCulture($culture);
179
 
180
		$this->loadCultureData('root');
181
		$this->loadCultureData($culture);
182
	}
183
 
184
	/**
185
	 * Get the default directory for the ICU data.
186
	 * The default is the "data" directory for this class.
187
	 * @return string directory containing the ICU data.
188
	 */
189
	protected static function dataDir()
190
	{
191
		return dirname(__FILE__).'/data/';
192
	}
193
 
194
	/**
195
	 * Get the filename extension for ICU data. Default is ".dat".
196
	 * @return string filename extension for ICU data.
197
	 */
198
	protected static function fileExt()
199
	{
200
		return '.dat';
201
	}
202
 
203
	/**
204
	* Gets the CultureInfo that for this culture string
205
	* @return CultureInfo invariant culture info is "en".
206
	*/
207
	public static function getInstance($culture)
208
	{
209
		static $instances = array();
210
		if(!isset($instances[$culture]))
211
			$instances[$culture] = new CultureInfo($culture);
212
		return $instances[$culture];
213
	}
214
 
215
	/**
216
	 * Determine if a given culture is valid. Simply checks that the
217
	 * culture data exists.
218
	 * @param string a culture
219
	 * @return boolean true if valid, false otherwise.
220
	 */
221
	public function validCulture($culture)
222
	{
223
		if(preg_match('/^[_\\w]+$/', $culture))
224
			return is_file(self::dataDir().$culture.self::fileExt());
225
 
226
		return false;
227
	}
228
 
229
	/**
230
	 * Set the culture for the current instance. The culture indentifier
231
	 * must be of the form "<language>_(country/region)".
232
	 * @param string culture identifier, e.g. "fr_FR_EURO".
233
	 */
234
	protected function setCulture($culture)
235
	{
236
		if(!empty($culture))
237
		{
238
			if (!preg_match('/^[_\\w]+$/', $culture))
239
				throw new Exception('Invalid culture supplied: ' . $culture);
240
		}
241
 
242
		$this->culture = $culture;
243
	}
244
 
245
	/**
246
	 * Load the ICU culture data for the specific culture identifier.
247
	 * @param string the culture identifier.
248
	 */
249
	protected function loadCultureData($culture)
250
	{
251
		$file_parts = explode('_',$culture);
252
		$current_part = $file_parts[0];
253
 
254
		$files = array($current_part);
255
 
256
		for($i = 1, $k = count($file_parts); $i < $k; ++$i)
257
		{
258
			$current_part .= '_'.$file_parts[$i];
259
			$files[] = $current_part;
260
		}
261
 
262
		foreach($files as $file)
263
		{
264
			$filename = $this->dataDir.$file.$this->dataFileExt;
265
 
266
			if(is_file($filename) == false)
267
				throw new Exception('Data file for "'.$file.'" was not found.');
268
 
269
			if(in_array($filename, $this->dataFiles) === false)
270
			{
271
				array_unshift($this->dataFiles, $file);
272
 
273
				$data = &$this->getData($filename);
274
				$this->data[$file] = &$data;
275
 
276
				if(isset($data['__ALIAS']))
277
					$this->loadCultureData($data['__ALIAS'][0]);
278
				unset($data);
279
			}
280
		}
281
	}
282
 
283
	/**
284
	 * Get the data by unserializing the ICU data from disk.
285
	 * The data files are cached in a static variable inside
286
	 * this function.
287
	 * @param string the ICU data filename
288
	 * @return array ICU data
289
	 */
290
	protected function &getData($filename)
291
	{
292
		static $data = array();
293
		static $files = array();
294
 
295
		if(!in_array($filename, $files))
296
		{
297
			$data[$filename] = unserialize(file_get_contents($filename));
298
			$files[] = $filename;
299
		}
300
 
301
		return $data[$filename];
302
	}
303
 
304
	/**
305
	 * Find the specific ICU data information from the data.
306
	 * The path to the specific ICU data is separated with a slash "/".
307
	 * E.g. To find the default calendar used by the culture, the path
308
	 * "calendar/default" will return the corresponding default calendar.
309
	 * Use merge=true to return the ICU including the parent culture.
310
	 * E.g. The currency data for a variant, say "en_AU" contains one
311
	 * entry, the currency for AUD, the other currency data are stored
312
	 * in the "en" data file. Thus to retrieve all the data regarding
313
	 * currency for "en_AU", you need to use findInfo("Currencies,true);.
314
	 * @param string the data you want to find.
315
	 * @param boolean merge the data from its parents.
316
	 * @return mixed the specific ICU data.
317
	 */
318
	protected function findInfo($path='/', $merge=false)
319
	{
320
		$result = array();
321
		foreach($this->dataFiles as $section)
322
		{
323
			$info = $this->searchArray($this->data[$section], $path);
324
 
325
			if($info)
326
			{
327
				if($merge)
328
					$result = array_merge($info,$result);
329
				else
330
					return $info;
331
			}
332
		}
333
 
334
		return $result;
335
	}
336
 
337
	/**
338
	 * Search the array for a specific value using a path separated using
339
	 * slash "/" separated path. e.g to find $info['hello']['world'],
340
	 * the path "hello/world" will return the corresponding value.
341
	 * @param array the array for search
342
	 * @param string slash "/" separated array path.
343
	 * @return mixed the value array using the path
344
	 */
345
	private function searchArray($info, $path='/')
346
	{
347
		$index = explode('/',$path);
348
 
349
		$array = $info;
350
 
351
		for($i = 0, $k = count($index); $i < $k; ++$i)
352
		{
353
			$value = $index[$i];
354
			if($i < $k-1 && isset($array[$value]))
355
				$array = $array[$value];
356
			else if ($i == $k-1 && isset($array[$value]))
357
				return $array[$value];
358
		}
359
	}
360
 
361
	/**
362
	 * Gets the culture name in the format
363
	 * "<languagecode2>_(country/regioncode2)".
364
	 * @return string culture name.
365
	 */
366
	function getName()
367
	{
368
		return $this->culture;
369
	}
370
 
371
	/**
372
	 * Gets the DateTimeFormatInfo that defines the culturally appropriate
373
	 * format of displaying dates and times.
374
	 * @return DateTimeFormatInfo date time format information for the culture.
375
	 */
376
	function getDateTimeFormat()
377
	{
378
		if($this->dateTimeFormat === null)
379
		{
380
			$calendar = $this->getCalendar();
381
			$info = $this->findInfo("calendar/{$calendar}", true);
382
			$this->setDateTimeFormat(new DateTimeFormatInfo($info));
383
		}
384
 
385
		return $this->dateTimeFormat;
386
	}
387
 
388
	/**
389
	 * Set the date time format information.
390
	 * @param DateTimeFormatInfo the new date time format info.
391
	 */
392
	function setDateTimeFormat($dateTimeFormat)
393
	{
394
		$this->dateTimeFormat = $dateTimeFormat;
395
	}
396
 
397
	/**
398
	 * Gets the default calendar used by the culture, e.g. "gregorian".
399
	 * @return string the default calendar.
400
	 */
401
	function getCalendar()
402
	{
403
		$info = $this->findInfo('calendar/default');
404
		return $info[0];
405
	}
406
 
407
	/**
408
	 * Gets the culture name in the language that the culture is set
409
	 * to display. Returns <code>array('Language','Country');</code>
410
	 * 'Country' is omitted if the culture is neutral.
411
	 * @return array array with language and country as elements, localized.
412
	 */
413
	function getNativeName()
414
	{
415
		$lang = substr($this->culture,0,2);
416
		$reg = substr($this->culture,3,2);
417
		$language = $this->findInfo("Languages/{$lang}");
418
		$region = $this->findInfo("Countries/{$reg}");
419
		if($region)
420
			return $language[0].' ('.$region[0].')';
421
		else
422
			return $language[0];
423
	}
424
 
425
	/**
426
	 * Gets the culture name in English.
427
	 * Returns <code>array('Language','Country');</code>
428
	 * 'Country' is omitted if the culture is neutral.
429
	 * @return string language (country), it may locale code string if english name does not exist.
430
	 */
431
	function getEnglishName()
432
	{
433
		$lang = substr($this->culture,0,2);
434
		$reg = substr($this->culture,3,2);
435
		$culture = $this->getInvariantCulture();
436
 
437
		$language = $culture->findInfo("Languages/{$lang}");
438
		if(count($language) == 0)
439
			return $this->culture;
440
 
441
		$region = $culture->findInfo("Countries/{$reg}");
442
		if($region)
443
			return $language[0].' ('.$region[0].')';
444
		else
445
			return $language[0];
446
	}
447
 
448
	/**
449
	 * Gets the CultureInfo that is culture-independent (invariant).
450
	 * Any changes to the invariant culture affects all other
451
	 * instances of the invariant culture.
452
	 * The invariant culture is assumed to be "en";
453
	 * @return CultureInfo invariant culture info is "en".
454
	 */
455
	static function getInvariantCulture()
456
	{
457
		static $invariant;
458
		if($invariant === null)
459
			$invariant = new CultureInfo();
460
		return $invariant;
461
	}
462
 
463
	/**
464
	 * Gets a value indicating whether the current CultureInfo
465
	 * represents a neutral culture. Returns true if the culture
466
	 * only contains two characters.
467
	 * @return boolean true if culture is neutral, false otherwise.
468
	 */
469
	function getIsNeutralCulture()
470
	{
471
		return strlen($this->culture) == 2;
472
	}
473
 
474
	/**
475
	 * Gets the NumberFormatInfo that defines the culturally appropriate
476
	 * format of displaying numbers, currency, and percentage.
477
	 * @return NumberFormatInfo the number format info for current culture.
478
	 */
479
	function getNumberFormat()
480
	{
481
		if($this->numberFormat === null)
482
		{
483
			$elements = $this->findInfo('NumberElements');
484
			$patterns = $this->findInfo('NumberPatterns');
485
			$currencies = $this->getCurrencies();
486
			$data = array(	'NumberElements'=>$elements,
487
							'NumberPatterns'=>$patterns,
488
							'Currencies' => $currencies);
489
 
490
			$this->setNumberFormat(new NumberFormatInfo($data));
491
		}
492
		return $this->numberFormat;
493
	}
494
 
495
	/**
496
	 * Set the number format information.
497
	 * @param NumberFormatInfo the new number format info.
498
	 */
499
	function setNumberFormat($numberFormat)
500
	{
501
		$this->numberFormat = $numberFormat;
502
	}
503
 
504
	/**
505
	 * Gets the CultureInfo that represents the parent culture of the
506
	 * current CultureInfo
507
	 * @return CultureInfo parent culture information.
508
	 */
509
	function getParent()
510
	{
511
		if(strlen($this->culture) == 2)
512
			return $this->getInvariantCulture();
513
 
514
		$lang = substr($this->culture,0,2);
515
			return new CultureInfo($lang);
516
	}
517
 
518
	/**
519
	 * Gets the list of supported cultures filtered by the specified
520
	 * culture type. This is an EXPENSIVE function, it needs to traverse
521
	 * a list of ICU files in the data directory.
522
	 * This function can be called statically.
523
	 * @param int culture type, CultureInfo::ALL, CultureInfo::NEUTRAL
524
	 * or CultureInfo::SPECIFIC.
525
	 * @return array list of culture information available.
526
	 */
527
	static function getCultures($type=CultureInfo::ALL)
528
	{
529
		$dataDir = CultureInfo::dataDir();
530
		$dataExt = CultureInfo::fileExt();
531
		$dir = dir($dataDir);
532
 
533
		$neutral = array();
534
		$specific = array();
535
 
536
		while (false !== ($entry = $dir->read()))
537
		{
538
			if(is_file($dataDir.$entry)
539
				&& substr($entry,-4) == $dataExt
540
				&& $entry != 'root'.$dataExt)
541
			{
542
				$culture = substr($entry,0,-4);
543
				if(strlen($culture) == 2)
544
					$neutral[] = $culture;
545
				else
546
					$specific[] = $culture;
547
			}
548
		}
549
		$dir->close();
550
 
551
		switch($type)
552
		{
553
			case CultureInfo::ALL :
554
				$all = 	array_merge($neutral, $specific);
555
				sort($all);
556
				return $all;
557
				break;
558
			case CultureInfo::NEUTRAL :
559
				return $neutral;
560
				break;
561
			case CultureInfo::SPECIFIC :
562
				return $specific;
563
				break;
564
		}
565
	}
566
 
567
	/**
568
	 * Simplify a single element array into its own value.
569
	 * E.g. <code>array(0 => array('hello'), 1 => 'world');</code>
570
	 * becomes <code>array(0 => 'hello', 1 => 'world');</code>
571
	 * @param array with single elements arrays
572
	 * @return array simplified array.
573
	 */
574
	private function simplify($array)
575
	{
576
		for($i = 0, $k = count($array); $i<$k; ++$i)
577
		{
578
			$key = key($array);
579
			if(is_array($array[$key])
580
				&& count($array[$key]) == 1)
581
				$array[$key] = $array[$key][0];
582
			next($array);
583
		}
584
		return $array;
585
	}
586
 
587
	/**
588
	 * Get a list of countries in the language of the localized version.
589
	 * @return array a list of localized country names.
590
	 */
591
	function getCountries()
592
	{
593
		return $this->simplify($this->findInfo('Countries',true));
594
	}
595
 
596
	/**
597
	 * Get a list of currencies in the language of the localized version.
598
	 * @return array a list of localized currencies.
599
	 */
600
	function getCurrencies()
601
	{
602
		return $this->findInfo('Currencies',true);
603
	}
604
 
605
	/**
606
	 * Get a list of languages in the language of the localized version.
607
	 * @return array list of localized language names.
608
	 */
609
	function getLanguages()
610
	{
611
		return $this->simplify($this->findInfo('Languages',true));
612
	}
613
 
614
	/**
615
	 * Get a list of scripts in the language of the localized version.
616
	 * @return array list of localized script names.
617
	 */
618
	function getScripts()
619
	{
620
		return $this->simplify($this->findInfo('Scripts',true));
621
	}
622
 
623
	/**
624
	 * Get a list of timezones in the language of the localized version.
625
	 * @return array list of localized timezones.
626
	 */
627
	function getTimeZones()
628
	{
629
		return $this->simplify($this->findInfo('zoneStrings',true));
630
	}
631
}
632