Development

/branches/1.1/lib/i18n/sfCultureInfo.class.php

You must first sign up to be able to contribute.

root/branches/1.1/lib/i18n/sfCultureInfo.class.php

Revision 19912, 17.5 kB (checked in by FabianLange, 5 years ago)

[1.0, 1.1, 1.2, 1.3] calling dataDir statically as it should be in sfCultureInfo (fixes #6604)

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