CLocale.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. <?php
  2. /**
  3. * CLocale class file.
  4. *
  5. * @author Qiang Xue <qiang.xue@gmail.com>
  6. * @link http://www.yiiframework.com/
  7. * @copyright 2008-2013 Yii Software LLC
  8. * @license http://www.yiiframework.com/license/
  9. */
  10. /**
  11. * CLocale represents the data relevant to a locale.
  12. *
  13. * The data includes the number formatting information and date formatting information.
  14. *
  15. * @property string $id The locale ID (in canonical form).
  16. * @property CNumberFormatter $numberFormatter The number formatter for this locale.
  17. * @property CDateFormatter $dateFormatter The date formatter for this locale.
  18. * @property string $decimalFormat The decimal format.
  19. * @property string $currencyFormat The currency format.
  20. * @property string $percentFormat The percent format.
  21. * @property string $scientificFormat The scientific format.
  22. * @property array $monthNames Month names indexed by month values (1-12).
  23. * @property array $weekDayNames The weekday names indexed by weekday values (0-6, 0 means Sunday, 1 Monday, etc.).
  24. * @property string $aMName The AM name.
  25. * @property string $pMName The PM name.
  26. * @property string $dateFormat Date format.
  27. * @property string $timeFormat Date format.
  28. * @property string $dateTimeFormat Datetime format, i.e., the order of date and time.
  29. * @property string $orientation The character orientation, which is either 'ltr' (left-to-right) or 'rtl' (right-to-left).
  30. * @property array $pluralRules Plural forms expressions.
  31. *
  32. * @author Qiang Xue <qiang.xue@gmail.com>
  33. * @package system.i18n
  34. * @since 1.0
  35. */
  36. class CLocale extends CComponent
  37. {
  38. /**
  39. * @var string the directory that contains the locale data. If this property is not set,
  40. * the locale data will be loaded from 'framework/i18n/data'.
  41. * @since 1.1.0
  42. */
  43. public static $dataPath;
  44. private $_id;
  45. private $_data;
  46. private $_dateFormatter;
  47. private $_numberFormatter;
  48. /**
  49. * Returns the instance of the specified locale.
  50. * Since the constructor of CLocale is protected, you can only use
  51. * this method to obtain an instance of the specified locale.
  52. * @param string $id the locale ID (e.g. en_US)
  53. * @return CLocale the locale instance
  54. */
  55. public static function getInstance($id)
  56. {
  57. static $locales=array();
  58. if(isset($locales[$id]))
  59. return $locales[$id];
  60. else
  61. return $locales[$id]=new CLocale($id);
  62. }
  63. /**
  64. * @return array IDs of the locales which the framework can recognize
  65. */
  66. public static function getLocaleIDs()
  67. {
  68. static $locales;
  69. if($locales===null)
  70. {
  71. $locales=array();
  72. $dataPath=self::$dataPath===null ? dirname(__FILE__).DIRECTORY_SEPARATOR.'data' : self::$dataPath;
  73. $folder=@opendir($dataPath);
  74. while(($file=@readdir($folder))!==false)
  75. {
  76. $fullPath=$dataPath.DIRECTORY_SEPARATOR.$file;
  77. if(substr($file,-4)==='.php' && is_file($fullPath))
  78. $locales[]=substr($file,0,-4);
  79. }
  80. closedir($folder);
  81. sort($locales);
  82. }
  83. return $locales;
  84. }
  85. /**
  86. * Constructor.
  87. * Since the constructor is protected, please use {@link getInstance}
  88. * to obtain an instance of the specified locale.
  89. * @param string $id the locale ID (e.g. en_US)
  90. * @throws CException if given locale id is not recognized
  91. */
  92. protected function __construct($id)
  93. {
  94. $this->_id=self::getCanonicalID($id);
  95. $dataPath=self::$dataPath===null ? dirname(__FILE__).DIRECTORY_SEPARATOR.'data' : self::$dataPath;
  96. $dataFile=$dataPath.DIRECTORY_SEPARATOR.$this->_id.'.php';
  97. if(is_file($dataFile))
  98. $this->_data=require($dataFile);
  99. else
  100. throw new CException(Yii::t('yii','Unrecognized locale "{locale}".',array('{locale}'=>$id)));
  101. }
  102. /**
  103. * Converts a locale ID to its canonical form.
  104. * In canonical form, a locale ID consists of only underscores and lower-case letters.
  105. * @param string $id the locale ID to be converted
  106. * @return string the locale ID in canonical form
  107. */
  108. public static function getCanonicalID($id)
  109. {
  110. return strtolower(str_replace('-','_',$id));
  111. }
  112. /**
  113. * @return string the locale ID (in canonical form)
  114. */
  115. public function getId()
  116. {
  117. return $this->_id;
  118. }
  119. /**
  120. * @return CNumberFormatter the number formatter for this locale
  121. */
  122. public function getNumberFormatter()
  123. {
  124. if($this->_numberFormatter===null)
  125. $this->_numberFormatter=new CNumberFormatter($this);
  126. return $this->_numberFormatter;
  127. }
  128. /**
  129. * @return CDateFormatter the date formatter for this locale
  130. */
  131. public function getDateFormatter()
  132. {
  133. if($this->_dateFormatter===null)
  134. $this->_dateFormatter=new CDateFormatter($this);
  135. return $this->_dateFormatter;
  136. }
  137. /**
  138. * @param string $currency 3-letter ISO 4217 code. For example, the code "USD" represents the US Dollar and "EUR" represents the Euro currency.
  139. * @return string the localized currency symbol. Null if the symbol does not exist.
  140. */
  141. public function getCurrencySymbol($currency)
  142. {
  143. return isset($this->_data['currencySymbols'][$currency]) ? $this->_data['currencySymbols'][$currency] : null;
  144. }
  145. /**
  146. * @param string $name symbol name
  147. * @return string symbol
  148. */
  149. public function getNumberSymbol($name)
  150. {
  151. return isset($this->_data['numberSymbols'][$name]) ? $this->_data['numberSymbols'][$name] : null;
  152. }
  153. /**
  154. * @return string the decimal format
  155. */
  156. public function getDecimalFormat()
  157. {
  158. return $this->_data['decimalFormat'];
  159. }
  160. /**
  161. * @return string the currency format
  162. */
  163. public function getCurrencyFormat()
  164. {
  165. return $this->_data['currencyFormat'];
  166. }
  167. /**
  168. * @return string the percent format
  169. */
  170. public function getPercentFormat()
  171. {
  172. return $this->_data['percentFormat'];
  173. }
  174. /**
  175. * @return string the scientific format
  176. */
  177. public function getScientificFormat()
  178. {
  179. return $this->_data['scientificFormat'];
  180. }
  181. /**
  182. * @param integer $month month (1-12)
  183. * @param string $width month name width. It can be 'wide', 'abbreviated' or 'narrow'.
  184. * @param boolean $standAlone whether the month name should be returned in stand-alone format
  185. * @return string the month name
  186. */
  187. public function getMonthName($month,$width='wide',$standAlone=false)
  188. {
  189. if($standAlone)
  190. return isset($this->_data['monthNamesSA'][$width][$month]) ? $this->_data['monthNamesSA'][$width][$month] : $this->_data['monthNames'][$width][$month];
  191. else
  192. return isset($this->_data['monthNames'][$width][$month]) ? $this->_data['monthNames'][$width][$month] : $this->_data['monthNamesSA'][$width][$month];
  193. }
  194. /**
  195. * Returns the month names in the specified width.
  196. * @param string $width month name width. It can be 'wide', 'abbreviated' or 'narrow'.
  197. * @param boolean $standAlone whether the month names should be returned in stand-alone format
  198. * @return array month names indexed by month values (1-12)
  199. */
  200. public function getMonthNames($width='wide',$standAlone=false)
  201. {
  202. if($standAlone)
  203. return isset($this->_data['monthNamesSA'][$width]) ? $this->_data['monthNamesSA'][$width] : $this->_data['monthNames'][$width];
  204. else
  205. return isset($this->_data['monthNames'][$width]) ? $this->_data['monthNames'][$width] : $this->_data['monthNamesSA'][$width];
  206. }
  207. /**
  208. * @param integer $day weekday (0-7, 0 and 7 means Sunday)
  209. * @param string $width weekday name width. It can be 'wide', 'abbreviated' or 'narrow'.
  210. * @param boolean $standAlone whether the week day name should be returned in stand-alone format
  211. * @return string the weekday name
  212. */
  213. public function getWeekDayName($day,$width='wide',$standAlone=false)
  214. {
  215. $day=$day%7;
  216. if($standAlone)
  217. return isset($this->_data['weekDayNamesSA'][$width][$day]) ? $this->_data['weekDayNamesSA'][$width][$day] : $this->_data['weekDayNames'][$width][$day];
  218. else
  219. return isset($this->_data['weekDayNames'][$width][$day]) ? $this->_data['weekDayNames'][$width][$day] : $this->_data['weekDayNamesSA'][$width][$day];
  220. }
  221. /**
  222. * Returns the week day names in the specified width.
  223. * @param string $width weekday name width. It can be 'wide', 'abbreviated' or 'narrow'.
  224. * @param boolean $standAlone whether the week day name should be returned in stand-alone format
  225. * @return array the weekday names indexed by weekday values (0-6, 0 means Sunday, 1 Monday, etc.)
  226. */
  227. public function getWeekDayNames($width='wide',$standAlone=false)
  228. {
  229. if($standAlone)
  230. return isset($this->_data['weekDayNamesSA'][$width]) ? $this->_data['weekDayNamesSA'][$width] : $this->_data['weekDayNames'][$width];
  231. else
  232. return isset($this->_data['weekDayNames'][$width]) ? $this->_data['weekDayNames'][$width] : $this->_data['weekDayNamesSA'][$width];
  233. }
  234. /**
  235. * @param integer $era era (0,1)
  236. * @param string $width era name width. It can be 'wide', 'abbreviated' or 'narrow'.
  237. * @return string the era name
  238. */
  239. public function getEraName($era,$width='wide')
  240. {
  241. return $this->_data['eraNames'][$width][$era];
  242. }
  243. /**
  244. * @return string the AM name
  245. */
  246. public function getAMName()
  247. {
  248. return $this->_data['amName'];
  249. }
  250. /**
  251. * @return string the PM name
  252. */
  253. public function getPMName()
  254. {
  255. return $this->_data['pmName'];
  256. }
  257. /**
  258. * @param string $width date format width. It can be 'full', 'long', 'medium' or 'short'.
  259. * @return string date format
  260. */
  261. public function getDateFormat($width='medium')
  262. {
  263. return $this->_data['dateFormats'][$width];
  264. }
  265. /**
  266. * @param string $width time format width. It can be 'full', 'long', 'medium' or 'short'.
  267. * @return string date format
  268. */
  269. public function getTimeFormat($width='medium')
  270. {
  271. return $this->_data['timeFormats'][$width];
  272. }
  273. /**
  274. * @return string datetime format, i.e., the order of date and time.
  275. */
  276. public function getDateTimeFormat()
  277. {
  278. return $this->_data['dateTimeFormat'];
  279. }
  280. /**
  281. * @return string the character orientation, which is either 'ltr' (left-to-right) or 'rtl' (right-to-left)
  282. * @since 1.1.2
  283. */
  284. public function getOrientation()
  285. {
  286. return isset($this->_data['orientation']) ? $this->_data['orientation'] : 'ltr';
  287. }
  288. /**
  289. * @return array plural forms expressions
  290. */
  291. public function getPluralRules()
  292. {
  293. return isset($this->_data['pluralRules']) ? $this->_data['pluralRules'] : array(0=>'true');
  294. }
  295. /**
  296. * Converts a locale ID to a language ID.
  297. * A language ID consists of only the first group of letters before an underscore or dash.
  298. * @param string $id the locale ID to be converted
  299. * @return string the language ID
  300. * @since 1.1.9
  301. */
  302. public function getLanguageID($id)
  303. {
  304. // normalize id
  305. $id = $this->getCanonicalID($id);
  306. // remove sub tags
  307. if(($underscorePosition=strpos($id, '_'))!== false)
  308. {
  309. $id = substr($id, 0, $underscorePosition);
  310. }
  311. return $id;
  312. }
  313. /**
  314. * Converts a locale ID to a script ID.
  315. * A script ID consists of only the last four characters after an underscore or dash.
  316. * @param string $id the locale ID to be converted
  317. * @return string the script ID
  318. * @since 1.1.9
  319. */
  320. public function getScriptID($id)
  321. {
  322. // normalize id
  323. $id = $this->getCanonicalID($id);
  324. // find sub tags
  325. if(($underscorePosition=strpos($id, '_'))!==false)
  326. {
  327. $subTag = explode('_', $id);
  328. // script sub tags can be distinguished from territory sub tags by length
  329. if (strlen($subTag[1])===4)
  330. {
  331. $id = $subTag[1];
  332. }
  333. else
  334. {
  335. $id = null;
  336. }
  337. }
  338. else
  339. {
  340. $id = null;
  341. }
  342. return $id;
  343. }
  344. /**
  345. * Converts a locale ID to a territory ID.
  346. * A territory ID consists of only the last two to three letter or digits after an underscore or dash.
  347. * @param string $id the locale ID to be converted
  348. * @return string the territory ID
  349. * @since 1.1.9
  350. */
  351. public function getTerritoryID($id)
  352. {
  353. // normalize id
  354. $id = $this->getCanonicalID($id);
  355. // find sub tags
  356. if (($underscorePosition=strpos($id, '_'))!== false)
  357. {
  358. $subTag = explode('_', $id);
  359. // territory sub tags can be distinguished from script sub tags by length
  360. if (isset($subTag[2]) && strlen($subTag[2])<4)
  361. {
  362. $id = $subTag[2];
  363. }
  364. elseif (strlen($subTag[1])<4)
  365. {
  366. $id = $subTag[1];
  367. }
  368. else
  369. {
  370. $id = null;
  371. }
  372. }
  373. else
  374. {
  375. $id = null;
  376. }
  377. return $id;
  378. }
  379. /**
  380. * Gets a localized name from i18n data file (one of framework/i18n/data/ files).
  381. *
  382. * @param string $id array key from an array named by $category.
  383. * @param string $category data category. One of 'languages', 'scripts' or 'territories'.
  384. * @return string the localized name for the id specified. Null if data does not exist.
  385. * @since 1.1.9
  386. */
  387. public function getLocaleDisplayName($id=null, $category='languages')
  388. {
  389. $id = $this->getCanonicalID($id);
  390. if (($category == 'languages') && (isset($this->_data[$category][$id])))
  391. {
  392. return $this->_data[$category][$id];
  393. }
  394. elseif (($category == 'scripts') && ($val=$this->getScriptID($id)) && (isset($this->_data[$category][$val])))
  395. {
  396. return $this->_data[$category][$val];
  397. }
  398. elseif (($category == 'territories') && ($val=$this->getTerritoryID($id)) && (isset($this->_data[$category][$val])))
  399. {
  400. return $this->_data[$category][$val];
  401. }
  402. elseif (isset($this->_data[$category][$id]))
  403. {
  404. return $this->_data[$category][$id];
  405. }
  406. else {
  407. return null;
  408. }
  409. }
  410. /**
  411. * @param string $id Unicode language identifier from IETF BCP 47. For example, the code "en_US" represents U.S. English and "en_GB" represents British English.
  412. * @return string the local display name for the language. Null if the language code does not exist.
  413. * @since 1.1.9
  414. */
  415. public function getLanguage($id)
  416. {
  417. $id = $this->getLanguageID($id);
  418. return $this->getLocaleDisplayName($id, 'languages');
  419. }
  420. /**
  421. * @param string $id Unicode script identifier from IETF BCP 47. For example, the code "en_US" represents U.S. English and "en_GB" represents British English.
  422. * @return string the local display name for the script. Null if the script code does not exist.
  423. * @since 1.1.9
  424. */
  425. public function getScript($id)
  426. {
  427. return $this->getLocaleDisplayName($id, 'scripts');
  428. }
  429. /**
  430. * @param string $id Unicode territory identifier from IETF BCP 47. For example, the code "en_US" represents U.S. English and "en_GB" represents British English.
  431. * @return string the local display name for the territory. Null if the territory code does not exist.
  432. * @since 1.1.9
  433. */
  434. public function getTerritory($id)
  435. {
  436. return $this->getLocaleDisplayName($id, 'territories');
  437. }
  438. }