Test Failed
Push — intl ( 8a5819...c6271f )
by Fabio
08:36 queued 02:17
created

CultureInfo::fileExt()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
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:[email protected] 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
 * @author Fabio Bas <ctrlaltca[at]gmail[dot]com>
17
 * @package Prado\I18N\core
18
 */
19
20
namespace Prado\I18N\core;
21
22
use Exception;
23
24
/**
25
 * CultureInfo class.
26
 *
27
 * Represents information about a specific culture including the
28
 * names of the culture, the calendar used, as well as access to
29
 * culture-specific objects that provide methods for common operations,
30
 * such as formatting dates, numbers, and currency.
31
 *
32
 * The CultureInfo class holds culture-specific information, such as the
33
 * associated language, sublanguage, country/region, calendar, and cultural
34
 * conventions. This class also provides access to culture-specific
35
 * instances of DateTimeFormatInfo and NumberFormatInfo. These objects
36
 * contain the information required for culture-specific operations,
37
 * such as formatting dates, numbers and currency.
38
 *
39
 * The culture names follow the format "<languagecode>_<country/regioncode>",
40
 * where <languagecode> is a lowercase two-letter code derived from ISO 639
41
 * codes. You can find a full list of the ISO-639 codes at
42
 * http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt
43
 *
44
 * The <country/regioncode2> is an uppercase two-letter code derived from
45
 * ISO 3166. A copy of ISO-3166 can be found at
46
 * http://www.chemie.fu-berlin.de/diverse/doc/ISO_3166.html
47
 *
48
 * For example, Australian English is "en_AU".
49
 *
50
 * @author Xiang Wei Zhuo <weizhuo[at]gmail[dot]com>
51
 * @author Fabio Bas <ctrlaltca[at]gmail[dot]com>
52
 * @package Prado\I18N\core
53
 */
54
class CultureInfo
55
{
56
	/**
57
	 * The ICU data array.
58
	 * @var array
59
	 */
60
	private $data = [];
61
62
	/**
63
	 * The current culture.
64
	 * @var string
65
	 */
66
	private $culture;
67
68
	/**
69
	 * A list of CLDR resource bundles loaded
70
	 * @var array
71
	 */
72
	private $resourceBundles = array();
73
74
	/**
75
	 * A list of resource bundles keys
76
	 * @var array
77
	 */
78
	protected static $bundleNames = [
79
		'Core' => null,
80
		'Currencies' => 'ICUDATA-curr',
81
		'Languages' => 'ICUDATA-lang',
82
		'Countries' => 'ICUDATA-region',
83
		'zoneStrings' => 'ICUDATA-zone',
84
	];
85
86
	/**
87
	 * The current date time format info.
88
	 * @var DateTimeFormatInfo
89
	 */
90
	private $dateTimeFormat;
91
92
	/**
93
	 * The current number format info.
94
	 * @var NumberFormatInfo
95
	 */
96
	private $numberFormat;
97
98
	/**
99
	 * A list of properties that are accessable/writable.
100
	 * @var array
101
	 */
102
	protected $properties = [];
103
104
	/**
105
	 * Culture type, all.
106
	 * @see getCultures()
107
	 * @var int
108
	 */
109
	const ALL = 0;
110
111
	/**
112
	 * Culture type, neutral.
113
	 * @see getCultures()
114
	 * @var int
115
	 */
116
	const NEUTRAL = 1;
117
118
	/**
119
	 * Culture type, specific.
120
	 * @see getCultures()
121
	 * @var int
122
	 */
123
	const SPECIFIC = 2;
124
125
	/**
126
	 * Display the culture name.
127
	 * @return string the culture name.
128
	 * @see getName()
129
	 */
130
	public function __toString()
131
	{
132
		return $this->getName();
133
	}
134
135
136
	/**
137
	 * Allow functions that begins with 'set' to be called directly
138
	 * as an attribute/property to retrieve the value.
139
	 * @param mixed $name
140
	 * @return mixed
141
	 */
142 View Code Duplication
	public function __get($name)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
143
	{
144
		$getProperty = 'get' . $name;
145
		if (in_array($getProperty, $this->properties)) {
146
			return $this->$getProperty();
147
		} else {
148
			throw new Exception('Property ' . $name . ' does not exists.');
149
		}
150
	}
151
152
	/**
153
	 * Allow functions that begins with 'set' to be called directly
154
	 * as an attribute/property to set the value.
155
	 * @param mixed $name
156
	 * @param mixed $value
157
	 */
158 View Code Duplication
	public function __set($name, $value)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
159
	{
160
		$setProperty = 'set' . $name;
161
		if (in_array($setProperty, $this->properties)) {
162
			$this->$setProperty($value);
163
		} else {
164
			throw new Exception('Property ' . $name . ' can not be set.');
165
		}
166
	}
167
168
169
	/**
170
	 * Initializes a new instance of the CultureInfo class based on the
171
	 * culture specified by name. E.g. <code>new CultureInfo('en_AU');</cdoe>
172
	 * The culture indentifier must be of the form
173
	 * "language_(country/region/variant)".
174
	 * @param string $culture a culture name, e.g. "en_AU".
175
	 * @return return new CultureInfo.
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
176
	 */
177
	public function __construct($culture = 'en')
178
	{
179
		$this->properties = get_class_methods($this);
180
181
		if (empty($culture)) {
182
			$culture = 'en';
183
		}
184
185
		$this->setCulture($culture);
186
187
		$this->loadCultureData('root');
188
		$this->loadCultureData($culture);
189
	}
190
191
	/**
192
<<<<<<< HEAD
193
	 * Get the default directory for the ICU data.
194
	 * The default is the "data" directory for this class.
195
	 * @return string directory containing the ICU data.
196
	 */
197
	protected static function dataDir()
198
	{
199
		return __DIR__ . '/data/';
200
	}
201
202
	/**
203
	 * Get the filename extension for ICU data. Default is ".dat".
204
	 * @return string filename extension for ICU data.
205
	 */
206
	protected static function fileExt()
207
	{
208
		return '.dat';
209
	}
210
211
	/**
212
	 * Gets the CultureInfo that for this culture string
213
	 * @param mixed $culture
214
	 * @return CultureInfo invariant culture info is "en".
215
	 */
216
	public static function getInstance($culture)
217
	{
218
		static $instances = [];
219
		if (!isset($instances[$culture])) {
220
			$instances[$culture] = new CultureInfo($culture);
221
		}
222
		return $instances[$culture];
223
	}
224
225
	/**
226
	 * Determine if a given culture is valid. Simply checks that the
227
	 * culture data exists.
228
	 * @param string $culture a culture
229
	 * @return bool true if valid, false otherwise.
230
	 */
231
	public static function validCulture($culture)
232
	{
233
		return in_array($culture, self::getCultures());
234
	}
235
236
	/**
237
	 * Set the culture for the current instance. The culture indentifier
238
	 * must be of the form "<language>_(country/region)".
239
	 * @param string $culture culture identifier, e.g. "fr_FR_EURO".
240
	 */
241
	protected function setCulture($culture)
242
	{
243
		if (!empty($culture)) {
244
			if (!preg_match('/^[_\\w]+$/', $culture)) {
245
				throw new Exception('Invalid culture supplied: ' . $culture);
246
			}
247
		}
248
249
		$this->culture = $culture;
250
	}
251
252
	/**
253
	 * Load the ICU culture data for the specific culture identifier.
254
	 * @param string $culture the culture identifier.
0 ignored issues
show
Documentation introduced by
There is no parameter named $culture. Did you maybe mean $cultureName?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
255
	 */
256
	protected function loadCultureData($cultureName)
257
	{
258
		$culture_parts = explode('_', $cultureName);
259
		$current_part = $culture_parts[0];
260
261
		$culturesToLoad = [$current_part];
262
		for($i = 1, $k = count($culture_parts); $i < $k; ++$i)
263
		{
264
			$current_part .= '_'.$culture_parts[$i];
265
			$culturesToLoad[] = $current_part;
266
		}
267
268
		foreach(self::$bundleNames as $key => $bundleName)
269
		{
270
			if(!array_key_exists($key, $this->data))
271
				$this->data[$key] = [];
272
		}
273
		foreach($culturesToLoad as $culture)
274
		{
275
			if(in_array($culture, $this->resourceBundles))
276
				continue;
277
278
			array_unshift($this->resourceBundles, $culture);
279
			foreach(self::$bundleNames as $key => $bundleName)
280
			{
281
				$this->data[$key][$culture] = \ResourceBundle::create($culture, $bundleName, false);
282
			}
283
		}
284
	}
285
286
	/**
287
	 * Find the specific ICU data information from the data.
288
	 * The path to the specific ICU data is separated with a slash "/".
289
	 * E.g. To find the default calendar used by the culture, the path
290
	 * "calendar/default" will return the corresponding default calendar.
291
	 * Use merge=true to return the ICU including the parent culture.
292
	 * E.g. The currency data for a variant, say "en_AU" contains one
293
	 * entry, the currency for AUD, the other currency data are stored
294
	 * in the "en" data file. Thus to retrieve all the data regarding
295
	 * currency for "en_AU", you need to use findInfo("Currencies,true);.
296
	 * @param string $path the data you want to find.
297
	 * @param bool $merge merge the data from its parents.
298
	 * @return mixed the specific ICU data.
299
	 */
300
	public function findInfo($path='/', $merge=false, $key = null)
301
	{
302
		$result = [];
303
304
		if($key === null)
305
		{
306
			// try to guess the bundle from the path. Always defaults to "Core".
307
			$key = 'Core';
308
			foreach(self::$bundleNames as $bundleName => $icuBundleName)
309
			{
310
				if(strpos($path, $bundleName) === 0)
311
				{
312
					$key = $bundleName;
313
					break;
314
				}
315
			}
316
		}
317
318
		if(!array_key_exists($key, $this->data))
319
			return $result;
320
		foreach($this->resourceBundles as $culture)
321
		{
322
			$res = $this->data[$key][$culture];
323
			if($res === null)
324
				continue;
325
			$info = $this->searchResources($res, $path);
326
			if($info)
327
			{
328
				if($merge)
329
					$result = array_merge($result, $info);
330
				else
331
					return $info;
332
			}
333
		}
334
335
		return $result;
336
	}
337
338
	/**
339
	 * Search the array for a specific value using a path separated using
340
	 * slash "/" separated path. e.g to find $info['hello']['world'],
341
	 * the path "hello/world" will return the corresponding value.
342
	 * @param array $info the array for search
343
	 * @param string $path slash "/" separated array path.
344
	 * @return mixed the value array using the path
345
	 */
346
	private function searchResources($info, $path='/')
347
	{
348
		$index = explode('/', $path);
349
350
		$resource = $info;
351
		for($i = 0, $k = count($index); $i < $k; ++$i)
352
		{
353
354
			$resource = $resource->get($index[$i], false);
355
			if($resource === null)
356
				return null;
357
		}
358
359
		return $this->simplify($resource);
360
	}
361
362
	/**
363
	 * Gets the culture name in the format
364
	 * "<languagecode2>_(country/regioncode2)".
365
	 * @return string culture name.
366
	 */
367
	public function getName()
368
	{
369
		return $this->culture;
370
	}
371
372
	/**
373
	 * Gets the DateTimeFormatInfo that defines the culturally appropriate
374
	 * format of displaying dates and times.
375
	 * @return DateTimeFormatInfo date time format information for the culture.
376
	 */
377
	public function getDateTimeFormat()
378
	{
379
		if($this->dateTimeFormat === null)
380
		{
381
			$this->setDateTimeFormat(new DateTimeFormatInfo($this));
0 ignored issues
show
Deprecated Code introduced by
The class Prado\I18N\core\DateTimeFormatInfo has been deprecated with message: since 4.0.2

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
382
		}
383
384
		return $this->dateTimeFormat;
385
	}
386
387
	/**
388
	 * Set the date time format information.
389
	 * @param DateTimeFormatInfo $dateTimeFormat the new date time format info.
390
	 */
391
	public function setDateTimeFormat($dateTimeFormat)
392
	{
393
		$this->dateTimeFormat = $dateTimeFormat;
394
	}
395
396
	/**
397
	 * Gets the default calendar used by the culture, e.g. "gregorian".
398
	 * @return string the default calendar.
399
	 */
400
	public function getCalendar()
401
	{
402
		return $this->findInfo('calendar/default');
403
	}
404
405
	/**
406
	 * Gets the culture name in the language that the culture is set
407
	 * to display. Returns <code>array('Language','Country');</code>
408
	 * 'Country' is omitted if the culture is neutral.
409
	 * @return array array with language and country as elements, localized.
410
	 */
411
	public function getNativeName()
412
	{
413
		$lang = substr($this->culture, 0, 2);
414
		$reg = substr($this->culture, 3, 2);
415
		$language = $this->findInfo("Languages/{$lang}");
416
		$region = $this->findInfo("Countries/{$reg}");
417
		if($region)
0 ignored issues
show
Bug Best Practice introduced by
The expression $region of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
418
			return $language.' ('.$region.')';
419
		else
420
			return $language;
421
	}
422
423
	/**
424
	 * Gets the culture name in English.
425
	 * Returns <code>array('Language','Country');</code>
426
	 * 'Country' is omitted if the culture is neutral.
427
	 * @return string language (country), it may locale code string if english name does not exist.
428
	 */
429
	public function getEnglishName()
430
	{
431
		$lang = substr($this->culture, 0, 2);
432
		$reg = substr($this->culture, 3, 2);
433
		$culture = $this->getInvariantCulture();
434
435
		$language = $culture->findInfo("Languages/{$lang}");
436
		if (count($language) == 0) {
437
			return $this->culture;
438
		}
439
440
		$region = $culture->findInfo("Countries/{$reg}");
441
		if($region)
0 ignored issues
show
Bug Best Practice introduced by
The expression $region of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
442
			return $language.' ('.$region.')';
443
		else
444
			return $language;
445
	}
446
447
	/**
448
	 * Gets the CultureInfo that is culture-independent (invariant).
449
	 * Any changes to the invariant culture affects all other
450
	 * instances of the invariant culture.
451
	 * The invariant culture is assumed to be "en";
452
	 * @return CultureInfo invariant culture info is "en".
453
	 */
454
	public static function getInvariantCulture()
455
	{
456
		static $invariant;
457
		if ($invariant === null) {
458
			$invariant = new CultureInfo();
459
		}
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 bool true if culture is neutral, false otherwise.
468
	 */
469
	public 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
	public function getNumberFormat()
480
	{
481
		if($this->numberFormat === null)
482
		{
483
			$this->setNumberFormat(new NumberFormatInfo($this));
484
		}
485
		return $this->numberFormat;
486
	}
487
488
	/**
489
	 * Set the number format information.
490
	 * @param NumberFormatInfo $numberFormat the new number format info.
491
	 */
492
	public function setNumberFormat($numberFormat)
493
	{
494
		$this->numberFormat = $numberFormat;
495
	}
496
497
	/**
498
	 * Gets the default number format used by the culture, e.g. "latn".
499
	 * @return string the default number format.
500
	 */
501
	public function getDefaultNumberFormat()
502
	{
503
		return $this->findInfo('NumberElements/default');
504
	}
505
506
	/**
507
	 * Gets the CultureInfo that represents the parent culture of the
508
	 * current CultureInfo
509
	 * @return CultureInfo parent culture information.
510
	 */
511
	public function getParent()
512
	{
513
		if (strlen($this->culture) == 2) {
514
			return $this->getInvariantCulture();
515
		}
516
517
		$lang = substr($this->culture, 0, 2);
518
		return new CultureInfo($lang);
519
	}
520
521
	/**
522
	 * Gets the list of supported cultures filtered by the specified
523
	 * culture type. This is an EXPENSIVE function, it needs to traverse
524
	 * a list of ICU files in the data directory.
525
	 * This function can be called statically.
526
	 * @param int $type culture type, CultureInfo::ALL, CultureInfo::NEUTRAL
527
	 * or CultureInfo::SPECIFIC.
528
	 * @return array list of culture information available.
529
	 */
530
	public static function getCultures($type=CultureInfo::ALL)
531
	{
532
		$all = \ResourceBundle::getLocales('');
533
534
		switch($type)
535
		{
536
			case CultureInfo::ALL :
537
				return $all;
538 View Code Duplication
			case CultureInfo::NEUTRAL :
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
539
				foreach($all as $key => $culture)
540
				{
541
					if(strlen($culture) != 2)
542
						unset($all[$key]);
543
				}
544
				return $all;
545 View Code Duplication
			case CultureInfo::SPECIFIC :
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
546
				foreach($all as $key => $culture)
547
				{
548
					if(strlen($culture) == 2)
549
						unset($all[$key]);
550
				}
551
				return $all;
552
		}
553
	}
554
555
	/**
556
	 * Simplify a single element array into its own value.
557
	 * E.g. <code>array(0 => array('hello'), 1 => 'world');</code>
558
	 * becomes <code>array(0 => 'hello', 1 => 'world');</code>
559
	 * @param array $array with single elements arrays
0 ignored issues
show
Bug introduced by
There is no parameter named $array. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
560
	 * @return array simplified array.
561
	 */
562
	protected function simplify($obj)
563
	{
564
		if(is_scalar($obj)) {
565
			return $obj;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $obj; (integer|double|string|boolean) is incompatible with the return type documented by Prado\I18N\core\CultureInfo::simplify of type array.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
566
		} elseif($obj instanceof \ResourceBundle) {
567
			$array = array();
568
			foreach($obj as $k => $v)
0 ignored issues
show
Bug introduced by
The expression $obj of type object<ResourceBundle> is not traversable.
Loading history...
569
				$array[$k] = $v;
570
		} else {
571
			$array = $obj;
572
		}
573
574
		for($i = 0, $k = count($array); $i<$k; ++$i)
575
		{
576
			$key = key($array);
577
			if (is_array($array[$key])
578
				&& count($array[$key]) == 1) {
579
				$array[$key] = $array[$key][0];
580
			}
581
			next($array);
582
		}
583
		return $array;
584
	}
585
586
	/**
587
	 * Get a list of countries in the language of the localized version.
588
	 * @return array a list of localized country names.
589
	 */
590
	public function getCountries()
591
	{
592
		return $this->simplify($this->findInfo('Countries', true, 'Countries'));
593
	}
594
595
	/**
596
	 * Get a list of currencies in the language of the localized version.
597
	 * @return array a list of localized currencies.
598
	 */
599
	public function getCurrencies()
600
	{
601
		static $arr;
602
		if($arr === null)
603
		{
604
			$arr = $this->findInfo('Currencies', true, 'Currencies');
605
			foreach($arr as $k => $v)
606
				$arr[$k] = $this->simplify($v);
607
		}
608
		return $arr;
609
	}
610
611
	/**
612
	 * Get a list of languages in the language of the localized version.
613
	 * @return array list of localized language names.
614
	 */
615
	public function getLanguages()
616
	{
617
		return $this->simplify($this->findInfo('Languages', true, 'Languages'));
618
	}
619
620
	/**
621
	 * Get a list of scripts in the language of the localized version.
622
	 * @return array list of localized script names.
623
	 */
624
	public function getScripts()
625
	{
626
		return $this->simplify($this->findInfo('Scripts', true, 'Languages'));
627
	}
628
629
	/**
630
	 * Get a list of timezones in the language of the localized version.
631
	 * @return array list of localized timezones.
632
	 */
633
	public function getTimeZones()
634
	{
635
		static $arr;
636
		if($arr === null)
637
		{
638
			$validPrefixes = array('Africa', 'America', 'Antarctica', 'Arctic', 'Asia', 'Atlantic', 'Australia', 'Etc', 'Europe', 'Indian', 'Pacific');
639
			$tmp = $this->findInfo('zoneStrings', true, 'zoneStrings');
640
			foreach($tmp as $k => $v)
641
			{
642
				foreach($validPrefixes as $prefix)
643
				{
644
					if(strpos($k, $prefix) === 0)
645
					{
646
						$arr[] = str_replace(':', '/', $k);
647
						break;
648
					}
649
				}
650
			}
651
		}
652
		return $arr;
653
	}
654
}
655