00001 <?php 00017 class axLocale implements IteratorAggregate { 00018 00023 const CACHE_FILE = 'locale.cache.php'; 00024 00029 protected static $_accepted_languages_cache; 00030 00035 protected $_file; 00036 00041 protected $_lang; 00042 00047 protected $_cache_dir; 00048 00053 protected $tree; 00054 00065 public function __construct ($lang_file, $lang = "auto", $default_lang = "en", $cache_dir = false) { 00066 $this->_lang = strtolower($lang); 00067 $this->_cache_dir = $cache_dir !== false ? realpath($cache_dir) : false; 00068 $this->_tree = array(); 00069 00070 if (!$this->_file = realpath($lang_file)) { 00071 throw new axMissingFileException($lang_file); 00072 } 00073 00074 if ($this->_lang !== 'auto' && !setlocale(LC_ALL^LC_MESSAGES, $this->_lang)) { 00075 throw new RuntimeException("Cannot set locale to {$this->_lang}"); 00076 } 00077 } 00078 00084 public function __get ($key) { 00085 return $this->getIterator()->$key; 00086 } 00087 00093 public function getIterator () { 00094 if (empty($this->_tree)) { 00095 if ($this->_cache_dir && is_readable($c = $this->_cache_dir . '/' . self::CACHE_FILE)) { 00096 require $c; 00097 $this->_tree = $tree; 00098 } 00099 else { 00100 $this->_generateTree(); 00101 $this->_cache(); 00102 } 00103 } 00104 00105 if ($this->_lang === 'auto' && !($this->_lang = $this->_determineLanguage())) 00106 $this->_lang = $this->_default_lang; 00107 00108 if (!isset($this->_tree[$this->_lang])) 00109 throw new RuntimeException("Lang {$this->_lang} not available"); 00110 00111 return $this->_tree[$this->_lang]; 00112 } 00113 00123 public function setLang ($lang) { 00124 if ($lang === "auto" && !($this->_lang = $this->_determineLanguage())) 00125 return false; 00126 00127 if (!setlocale(LC_ALL, $this->_lang = $lang)) 00128 return false; 00129 00130 return $this; 00131 } 00132 00137 public function getLang () { 00138 return $this->_lang; 00139 } 00140 00146 public function conv () { 00147 return localeconv(); 00148 } 00149 00159 public function date ($time = null) { 00160 if ($time === null) 00161 return date($this->date->format); 00162 else 00163 return date($this->date->format, $time); 00164 } 00165 00176 public function date2string ($date) { 00177 if (is_int($date)) { 00178 $date = date('ymdHi', $date); 00179 } 00180 00181 $minusdate = date('ymdHi') - $date; 00182 00183 if($minusdate > 88697640 && $minusdate < 100000000) { 00184 $minusdate = $minusdate - 88697640; 00185 } 00186 00187 switch ($minusdate) { 00188 case ($minusdate < 99): 00189 if($minusdate == 1) { 00190 $date_string = $this->i18n('date.minutes_ago', 1) +1; 00191 } 00192 elseif($minusdate > 59) { 00193 $date_string = $this->i18n('date.minutes_ago', $minusdate - 40); 00194 } 00195 elseif($minusdate > 1 && $minusdate < 59) { 00196 $date_string = $this->i18n('date.minutes_ago', $minusdate); 00197 } 00198 break; 00199 00200 case ($minusdate > 99 && $minusdate < 2359): 00201 $flr = floor($minusdate * .01) +1; 00202 if($flr == 1) { 00203 $date_string = $this->i18n('date.hours_ago', 1);; 00204 } 00205 else { 00206 $date_string = $this->i18n('date.hours_ago', $flr); 00207 } 00208 break; 00209 00210 case ($minusdate > 2359 && $minusdate < 310000): 00211 $flr = floor($minusdate * .0001) +1; 00212 if($flr == 1) { 00213 $date_string = $this->i18n('date.days_ago', 1); 00214 } 00215 else { 00216 $date_string = $this->i18n('date.days_ago', $flr); 00217 } 00218 break; 00219 00220 case ($minusdate > 310001 && $minusdate < 12320000): 00221 $flr = floor($minusdate * .000001) +1; 00222 if($flr == 1) { 00223 $date_string = $this->i18n('date.months_ago', 1); 00224 } 00225 else { 00226 $date_string = $this->i18n('date.months_ago', $flr); 00227 } 00228 break; 00229 00230 case ($minusdate > 100000000): 00231 default: 00232 $flr = floor($minusdate * .00000001) +1; 00233 if($flr == 1) { 00234 $date_string = $this->i18n('date.years_ago', 1); 00235 } 00236 else { 00237 $date_string = $this->i18n('date.years_ago', $flr); 00238 } 00239 } 00240 00241 return $date_string; 00242 } 00243 00251 public function i18n ($key) { 00252 $args = func_get_args(); 00253 array_shift($args); 00254 00255 $msg = $this->getIterator(); 00256 foreach (explode('.', $key) as $k) 00257 $msg = $msg->$k; 00258 00259 if (!$msg->getValue()) { 00260 trigger_error("Undefined translation {$key}"); 00261 return ""; 00262 } 00263 00264 switch (count($args)) { 00265 case 0: return $msg; break; 00266 case 1: return sprintf((string)$msg, $args[0]); break; 00267 case 2: return sprintf((string)$msg, $args[0], $args[1]); break; 00268 case 3: return sprintf((string)$msg, $args[0], $args[1], $args[2]); break; 00269 case 4: return sprintf((string)$msg, $args[0], $args[1], $args[2], $args[3]); break; 00270 case 5: return sprintf((string)$msg, $args[0], $args[1], $args[2], $args[3], $args[4]); break; 00271 default: 00272 array_unshift($msg, $args); 00273 return call_user_func_array('sprintf', $args); 00274 break; 00275 } 00276 } 00277 00284 protected function _generateTree () { 00285 if (!is_file($this->_file) || !is_readable($this->_file)) 00286 throw new axMissingFileException($this->_file); 00287 00288 if (!$ini = parse_ini_file($this->_file, true)) 00289 throw new RuntimeException("Cannot parse $file"); 00290 00291 foreach (array_keys($ini) as $key) { 00292 if (($offset = strpos($key, ':')) !== false && isset($ini[trim(substr($key, $offset+1))])) 00293 $ini[$key] += $ini[trim(substr($key, $offset+1))]; 00294 00295 $tree = new axTreeItem; 00296 foreach ($ini[$key] as $k => $v) { 00297 $p = explode('.', $k); 00298 $c = $tree; 00299 foreach ($p as $sk) 00300 $c = $c->$sk; 00301 $c->setValue($v); 00302 } 00303 $this->_tree[trim(substr($key, 0, $offset ? $offset : strlen($key)))] = $tree; 00304 } 00305 } 00306 00314 protected function _cache () { 00315 if (!$this->_cache_dir) 00316 return false; 00317 00318 $buffer = '<?php $tree=' . var_export($this->_tree, true) . '; ?>'; 00319 return (boolean)file_put_contents($this->_cache_dir . '/' . self::CACHE_FILE, $buffer); 00320 } 00321 00329 protected static function _getAcceptedLanguages () { 00330 if (isset(self::$_accepted_languages_cache)) 00331 return self::$_accepted_languages_cache; 00332 00333 $httplanguages = $_SERVER['HTTP_ACCEPT_LANGUAGE']; 00334 $languages = array(); 00335 if (empty($httplanguages)) { 00336 return $languages; 00337 } 00338 00339 foreach (explode(',', $httplanguages) as $accept) { 00340 $result = preg_match('/^([a-z]{1,8}(?:[-_][a-z]{1,8})*)(?:;\s*q=(0(?:\.[0-9]{1,3})?|1(?:\.0{1,3})?))?$/i', $accept, $match); 00341 00342 if (!$result) { 00343 continue; 00344 } 00345 if (isset($match[2])) { 00346 $quality = (float)$match[2]; 00347 } 00348 else { 00349 $quality = 1.0; 00350 } 00351 00352 $countries = explode('-', $match[1]); 00353 $region = array_shift($countries); 00354 $country_sub = explode('_', $region); 00355 $region = array_shift($country_sub); 00356 00357 foreach($countries as $country) 00358 $languages[$region . '_' . strtoupper($country)] = $quality; 00359 00360 foreach($country_sub as $country) 00361 $languages[$region . '_' . strtoupper($country)] = $quality; 00362 00363 $languages[$region] = $quality; 00364 } 00365 00366 return self::$_accepted_languages_cache = $languages; 00367 } 00368 00376 protected function _determineLanguage () { 00377 foreach ($this->_getAcceptedLanguages() as $accept => $priority) { 00378 if (isset($this->_tree[$accept])) 00379 return $accept; 00380 } 00381 return false; 00382 } 00383 }