Development

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

You must first sign up to be able to contribute.

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

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