123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825 |
- <?php
- /**
- * sfCultureInfo 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 $Id: sfCultureInfo.class.php 11843 2008-09-29 11:25:50Z fabien $
- * @package symfony
- * @subpackage i18n
- */
- /**
- * sfCultureInfo class.
- *
- * Represents information about a specific culture including the
- * names of the culture, the calendar used, as well as access to
- * culture-specific objects that provide methods for common operations,
- * such as formatting dates, numbers, and currency.
- *
- * The sfCultureInfo class holds culture-specific information, such as the
- * associated language, sublanguage, country/region, calendar, and cultural
- * conventions. This class also provides access to culture-specific
- * instances of sfDateTimeFormatInfo and sfNumberFormatInfo. These objects
- * contain the information required for culture-specific operations,
- * such as formatting dates, numbers and currency.
- *
- * The culture names follow the format "<languagecode>_<country/regioncode>",
- * where <languagecode> is a lowercase two-letter code derived from ISO 639
- * codes. You can find a full list of the ISO-639 codes at
- * http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt
- *
- * The <country/regioncode2> is an uppercase two-letter code derived from
- * ISO 3166. A copy of ISO-3166 can be found at
- * http://www.chemie.fu-berlin.de/diverse/doc/ISO_3166.html
- *
- * For example, Australian English is "en_AU".
- *
- * @author Xiang Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @version v1.0, last update on Sat Dec 04 13:41:46 EST 2004
- * @package symfony
- * @subpackage i18n
- */
- class sfCultureInfo
- {
- /**
- * ICU data filename extension.
- * @var string
- */
- protected $dataFileExt = '.dat';
- /**
- * The ICU data array.
- * @var array
- */
- protected $data = array();
- /**
- * The current culture.
- * @var string
- */
- protected $culture;
- /**
- * Directory where the ICU data is stored.
- * @var string
- */
- protected $dataDir;
- /**
- * A list of ICU date files loaded.
- * @var array
- */
- protected $dataFiles = array();
- /**
- * The current date time format info.
- * @var sfDateTimeFormatInfo
- */
- protected $dateTimeFormat;
- /**
- * The current number format info.
- * @var sfNumberFormatInfo
- */
- protected $numberFormat;
-
- /**
- * A list of properties that are accessable/writable.
- * @var array
- */
- protected $properties = array();
- /**
- * Culture type, all.
- * @see getCultures()
- * @var int
- */
- const ALL = 0;
- /**
- * Culture type, neutral.
- * @see getCultures()
- * @var int
- */
- const NEUTRAL = 1;
- /**
- * Culture type, specific.
- *
- * @see getCultures()
- * @var int
- */
- const SPECIFIC = 2;
- /**
- * Gets the sfCultureInfo that for this culture string.
- *
- * @param string $culture The culture for this instance
- * @return sfCultureInfo Invariant culture info is "en"
- */
- public static function getInstance($culture = 'en')
- {
- static $instances = array();
- if (!isset($instances[$culture]))
- {
- $instances[$culture] = new sfCultureInfo($culture);
- }
- return $instances[$culture];
- }
- /**
- * Displays the culture name.
- *
- * @return string the culture name.
- * @see getName()
- */
- public function __toString()
- {
- return $this->getName();
- }
- /**
- * Allows functions that begins with 'set' to be called directly
- * as an attribute/property to retrieve the value.
- *
- * @param string $name The property to get
- * @return mixed
- */
- public function __get($name)
- {
- $getProperty = 'get'.$name;
- if (in_array($getProperty, $this->properties))
- {
- return $this->$getProperty();
- }
- else
- {
- throw new sfException(sprintf('Property %s does not exists.', $name));
- }
- }
- /**
- * Allows functions that begins with 'set' to be called directly
- * as an attribute/property to set the value.
- *
- * @param string $name The property to set
- * @param string $value The property value
- */
- public function __set($name, $value)
- {
- $setProperty = 'set'.$name;
- if (in_array($setProperty, $this->properties))
- {
- $this->$setProperty($value);
- }
- else
- {
- throw new sfException(sprintf('Property %s can not be set.', $name));
- }
- }
- /**
- * Initializes a new instance of the sfCultureInfo class based on the
- * culture specified by name. E.g. <code>new sfCultureInfo('en_AU');</code>
- * The culture indentifier must be of the form
- * "<language>_(country/region/variant)".
- *
- * @param string $culture a culture name, e.g. "en_AU".
- * @return return new sfCultureInfo.
- */
- public function __construct($culture = 'en')
- {
- $this->properties = get_class_methods($this);
- if (empty($culture))
- {
- $culture = 'en';
- }
- $this->dataDir = $this->dataDir();
- $this->dataFileExt = $this->fileExt();
- $this->setCulture($culture);
- $this->loadCultureData('root');
- $this->loadCultureData($culture);
- }
- /**
- * Gets the default directory for the ICU data.
- * The default is the "data" directory for this class.
- *
- * @return string directory containing the ICU data.
- */
- protected static function dataDir()
- {
- return dirname(__FILE__).'/data/';
- }
- /**
- * Gets the filename extension for ICU data. Default is ".dat".
- *
- * @return string filename extension for ICU data.
- */
- protected static function fileExt()
- {
- return '.dat';
- }
- /**
- * Determines if a given culture is valid. Simply checks that the
- * culture data exists.
- *
- * @param string $culture a culture
- * @return boolean true if valid, false otherwise.
- */
- static public function validCulture($culture)
- {
- if (preg_match('/^[a-z]{2}(_[A-Z]{2,5}){0,2}$/', $culture))
- {
- return is_file(self::dataDir().$culture.self::fileExt());
- }
- return false;
- }
- /**
- * Sets the culture for the current instance. The culture indentifier
- * must be of the form "<language>_(country/region)".
- *
- * @param string $culture culture identifier, e.g. "fr_FR_EURO".
- */
- protected function setCulture($culture)
- {
- if (!empty($culture))
- {
- if (!preg_match('/^[a-z]{2}(_[A-Z]{2,5}){0,2}$/', $culture))
- {
- throw new sfException(sprintf('Invalid culture supplied: %s', $culture));
- }
- }
- $this->culture = $culture;
- }
- /**
- * Loads the ICU culture data for the specific culture identifier.
- *
- * @param string $culture the culture identifier.
- */
- protected function loadCultureData($culture)
- {
- $file_parts = explode('_', $culture);
- $current_part = $file_parts[0];
- $files = array($current_part);
- for ($i = 1, $max = count($file_parts); $i < $max; $i++)
- {
- $current_part .= '_'.$file_parts[$i];
- $files[] = $current_part;
- }
- foreach ($files as $file)
- {
- $filename = $this->dataDir.$file.$this->dataFileExt;
- if (is_file($filename) == false)
- {
- throw new sfException(sprintf('Data file for "%s" was not found.', $file));
- }
- if (in_array($filename, $this->dataFiles) == false)
- {
- array_unshift($this->dataFiles, $file);
- $data = &$this->getData($filename);
- $this->data[$file] = &$data;
- if (isset($data['__ALIAS']))
- {
- $this->loadCultureData($data['__ALIAS'][0]);
- }
- unset($data);
- }
- }
- }
- /**
- * Gets the data by unserializing the ICU data from disk.
- * The data files are cached in a static variable inside
- * this function.
- *
- * @param string $filename the ICU data filename
- * @return array ICU data
- */
- protected function &getData($filename)
- {
- static $data = array();
- static $files = array();
- if (!in_array($filename, $files))
- {
- $data[$filename] = unserialize(file_get_contents($filename));
- $files[] = $filename;
- }
- return $data[$filename];
- }
- /**
- * Finds the specific ICU data information from the data.
- * The path to the specific ICU data is separated with a slash "/".
- * E.g. To find the default calendar used by the culture, the path
- * "calendar/default" will return the corresponding default calendar.
- * Use merge=true to return the ICU including the parent culture.
- * E.g. The currency data for a variant, say "en_AU" contains one
- * entry, the currency for AUD, the other currency data are stored
- * in the "en" data file. Thus to retrieve all the data regarding
- * currency for "en_AU", you need to use findInfo("Currencies,true);.
- *
- * @param string $path the data you want to find.
- * @param boolean $merge merge the data from its parents.
- * @return mixed the specific ICU data.
- */
- protected function findInfo($path = '/', $merge = false)
- {
- $result = array();
- foreach ($this->dataFiles as $section)
- {
- $info = $this->searchArray($this->data[$section], $path);
- if ($info)
- {
- if ($merge)
- {
- $result = array_merge($info, $result);
- }
- else
- {
- return $info;
- }
- }
- }
- return $result;
- }
- /**
- * Searches the array for a specific value using a path separated using
- * slash "/" separated path. e.g to find $info['hello']['world'],
- * the path "hello/world" will return the corresponding value.
- *
- * @param array $info the array for search
- * @param string $path slash "/" separated array path.
- * @return mixed the value array using the path
- */
- protected function searchArray($info, $path = '/')
- {
- $index = explode('/', $path);
- $array = $info;
- for ($i = 0, $max = count($index); $i < $max; $i++)
- {
- $k = $index[$i];
- if ($i < $max - 1 && isset($array[$k]))
- {
- $array = $array[$k];
- }
- else if ($i == $max - 1 && isset($array[$k]))
- {
- return $array[$k];
- }
- }
- }
-
- /**
- * Gets the culture name in the format
- * "<languagecode2>_(country/regioncode2)".
- *
- * @return string culture name.
- */
- public function getName()
- {
- return $this->culture;
- }
- /**
- * Gets the sfDateTimeFormatInfo that defines the culturally appropriate
- * format of displaying dates and times.
- *
- * @return sfDateTimeFormatInfo date time format information for the culture.
- */
- public function getDateTimeFormat()
- {
- if (is_null($this->dateTimeFormat))
- {
- $calendar = $this->getCalendar();
- $info = $this->findInfo("calendar/{$calendar}", true);
- $this->setDateTimeFormat(new sfDateTimeFormatInfo($info));
- }
- return $this->dateTimeFormat;
- }
- /**
- * Sets the date time format information.
- *
- * @param sfDateTimeFormatInfo $dateTimeFormat the new date time format info.
- */
- public function setDateTimeFormat($dateTimeFormat)
- {
- $this->dateTimeFormat = $dateTimeFormat;
- }
- /**
- * Gets the default calendar used by the culture, e.g. "gregorian".
- *
- * @return string the default calendar.
- */
- public function getCalendar()
- {
- $info = $this->findInfo('calendar/default');
- return $info[0];
- }
- /**
- * Gets the culture name in the language that the culture is set
- * to display. Returns <code>array('Language','Country');</code>
- * 'Country' is omitted if the culture is neutral.
- *
- * @return array array with language and country as elements, localized.
- */
- public function getNativeName()
- {
- $lang = substr($this->culture, 0, 2);
- $reg = substr($this->culture, 3, 2);
- $language = $this->findInfo("Languages/{$lang}");
- $region = $this->findInfo("Countries/{$reg}");
- if ($region)
- {
- return $language[0].' ('.$region[0].')';
- }
- else
- {
- return $language[0];
- }
- }
- /**
- * Gets the culture name in English.
- * Returns <code>array('Language','Country');</code>
- * 'Country' is omitted if the culture is neutral.
- *
- * @return array array with language and country as elements.
- */
- public function getEnglishName()
- {
- $lang = substr($this->culture, 0, 2);
- $reg = substr($this->culture, 3, 2);
- $culture = $this->getInvariantCulture();
- $language = $culture->findInfo("Languages/{$lang}");
- if (count($language) == 0)
- {
- return $this->culture;
- }
- $region = $culture->findInfo("Countries/{$reg}");
- return $region ? $language[0].' ('.$region[0].')' : $language[0];
- }
- /**
- * Gets the sfCultureInfo that is culture-independent (invariant).
- * Any changes to the invariant culture affects all other
- * instances of the invariant culture.
- * The invariant culture is assumed to be "en";
- *
- * @return sfCultureInfo invariant culture info is "en".
- */
- static function getInvariantCulture()
- {
- static $invariant;
- if (is_null($invariant))
- {
- $invariant = new sfCultureInfo();
- }
- return $invariant;
- }
- /**
- * Gets a value indicating whether the current sfCultureInfo
- * represents a neutral culture. Returns true if the culture
- * only contains two characters.
- *
- * @return boolean true if culture is neutral, false otherwise.
- */
- public function getIsNeutralCulture()
- {
- return strlen($this->culture) == 2;
- }
- /**
- * Gets the sfNumberFormatInfo that defines the culturally appropriate
- * format of displaying numbers, currency, and percentage.
- *
- * @return sfNumberFormatInfo the number format info for current culture.
- */
- public function getNumberFormat()
- {
- if (is_null($this->numberFormat))
- {
- $elements = $this->findInfo('NumberElements');
- $patterns = $this->findInfo('NumberPatterns');
- $currencies = $this->getCurrencies(null, true);
- $data = array('NumberElements' => $elements, 'NumberPatterns' => $patterns, 'Currencies' => $currencies);
- $this->setNumberFormat(new sfNumberFormatInfo($data));
- }
- return $this->numberFormat;
- }
- /**
- * Sets the number format information.
- *
- * @param sfNumberFormatInfo $numberFormat the new number format info.
- */
- public function setNumberFormat($numberFormat)
- {
- $this->numberFormat = $numberFormat;
- }
- /**
- * Gets the sfCultureInfo that represents the parent culture of the
- * current sfCultureInfo
- *
- * @return sfCultureInfo parent culture information.
- */
- public function getParent()
- {
- if (strlen($this->culture) == 2)
- {
- return $this->getInvariantCulture();
- }
- return new sfCultureInfo(substr($this->culture, 0, 2));
- }
- /**
- * Gets the list of supported cultures filtered by the specified
- * culture type. This is an EXPENSIVE function, it needs to traverse
- * a list of ICU files in the data directory.
- * This function can be called statically.
- *
- * @param int $type culture type, sfCultureInfo::ALL, sfCultureInfo::NEUTRAL
- * or sfCultureInfo::SPECIFIC.
- * @return array list of culture information available.
- */
- static function getCultures($type = sfCultureInfo::ALL)
- {
- $dataDir = sfCultureInfo::dataDir();
- $dataExt = sfCultureInfo::fileExt();
- $dir = dir($dataDir);
- $neutral = array();
- $specific = array();
- while (false !== ($entry = $dir->read()))
- {
- if (is_file($dataDir.$entry) && substr($entry, -4) == $dataExt && $entry != 'root'.$dataExt)
- {
- $culture = substr($entry, 0, -4);
- if (strlen($culture) == 2)
- {
- $neutral[] = $culture;
- }
- else
- {
- $specific[] = $culture;
- }
- }
- }
- $dir->close();
- switch ($type)
- {
- case sfCultureInfo::ALL:
- $all = array_merge($neutral, $specific);
- sort($all);
- return $all;
- break;
- case sfCultureInfo::NEUTRAL:
- return $neutral;
- break;
- case sfCultureInfo::SPECIFIC:
- return $specific;
- break;
- }
- }
- /**
- * Simplifies a single element array into its own value.
- * E.g. <code>array(0 => array('hello'), 1 => 'world');</code>
- * becomes <code>array(0 => 'hello', 1 => 'world');</code>
- *
- * @param array $array with single elements arrays
- * @return array simplified array.
- */
- static protected function simplify($array)
- {
- foreach ($array as &$item)
- {
- if (is_array($item) && count($item) == 1)
- {
- $item = $item[0];
- }
- }
- return $array;
- }
- /**
- * Get the country name in the current culture for the given code.
- *
- * @param string A valid country code
- *
- * @return string The country name in the current culture
- */
- public function getCountry($code)
- {
- $countries = $this->simplify($this->findInfo('Countries', true));
- if (!isset($countries[$code]))
- {
- throw new InvalidArgumentException(sprintf('The country %s does not exist.', $code));
- }
- return $countries[$code];
- }
- /**
- * Get the currency name in the current culture for the given code.
- *
- * @param string A valid currency code
- *
- * @return string The currency name in the current culture
- */
- public function getCurrency($code)
- {
- $currencies = $this->simplify($this->findInfo('Currencies', true));
- if (!isset($currencies[$code]))
- {
- throw new InvalidArgumentException(sprintf('The currency %s does not exist.', $code));
- }
- return $currencies[$code][1];
- }
- /**
- * Get the language name in the current culture for the given code.
- *
- * @param string A valid language code
- *
- * @return string The language name in the current culture
- */
- public function getLanguage($code)
- {
- $languages = $this->simplify($this->findInfo('Languages', true));
- if (!isset($languages[$code]))
- {
- throw new InvalidArgumentException(sprintf('The language %s does not exist.', $code));
- }
- return $languages[$code];
- }
- /**
- * Gets a list of countries in the language of the localized version.
- *
- * @param array An array of countries used to restrict the returned array (null by default, which means all countries)
- *
- * @return array a list of localized country names.
- */
- public function getCountries($countries = null)
- {
- $allCountries = $this->simplify($this->findInfo('Countries', true));
- // restrict countries to a sub-set
- if (!is_null($countries))
- {
- if ($problems = array_diff($countries, array_keys($allCountries)))
- {
- throw new InvalidArgumentException(sprintf('The following countries do not exist: %s.', implode(', ', $problems)));
- }
- $allCountries = array_intersect_key($allCountries, array_flip($countries));
- }
- asort($allCountries);
- return $allCountries;
- }
- /**
- * Gets a list of currencies in the language of the localized version.
- *
- * @param array An array of currencies used to restrict the returned array (null by default, which means all currencies)
- * @param Boolean Whether to return the symbol and the name or not (false by default)
- *
- * @return array a list of localized currencies.
- */
- public function getCurrencies($currencies = null, $full = false)
- {
- $allCurrencies = $this->findInfo('Currencies', true);
- // restrict countries to a sub-set
- if (!is_null($currencies))
- {
- if ($problems = array_diff($currencies, array_keys($allCurrencies)))
- {
- throw new InvalidArgumentException(sprintf('The following currencies do not exist: %s.', implode(', ', $problems)));
- }
- $allCurrencies = array_intersect_key($allCurrencies, array_flip($currencies));
- }
- asort($allCurrencies);
- if (!$full)
- {
- foreach ($allCurrencies as $key => $value)
- {
- $allCurrencies[$key] = $value[1];
- }
- }
- return $allCurrencies;
- }
- /**
- * Gets a list of languages in the language of the localized version.
- *
- * @param array An array of languages used to restrict the returned array (null by default, which means all languages)
- *
- * @return array list of localized language names.
- */
- public function getLanguages($languages = null)
- {
- $allLanguages = $this->simplify($this->findInfo('Languages', true));
- // restrict languages to a sub-set
- if (!is_null($languages))
- {
- if ($problems = array_diff($languages, array_keys($allLanguages)))
- {
- throw new InvalidArgumentException(sprintf('The following languages do not exist: %s.', implode(', ', $problems)));
- }
- $allLanguages = array_intersect_key($allLanguages, array_flip($languages));
- }
- asort($allLanguages);
- return $allLanguages;
- }
- /**
- * Gets a list of scripts in the language of the localized version.
- *
- * @return array list of localized script names.
- */
- public function getScripts()
- {
- return $this->simplify($this->findInfo('Scripts', true));
- }
- /**
- * Gets a list of timezones in the language of the localized version.
- *
- * @return array list of localized timezones.
- */
- public function getTimeZones()
- {
- return $this->simplify($this->findInfo('zoneStrings', true));
- }
- }
|