filePath = $filePath; } if ($cachePath != NULL) { $this->cachePath = $cachePath; } if ($fallbackLang != NULL) { $this->fallbackLang = $fallbackLang; } if ($prefix != NULL) { $this->prefix = $prefix; } } public function init() { if ($this->isInitialized()) { throw new BadMethodCallException( 'This object from class ' . __CLASS__ . ' is already initialized. ' . 'It is not possible to init one object twice!'); } $this->isInitialized = true; $this->userLangs = $this->getUserLangs(); // search for language file $this->appliedLang = NULL; foreach ($this->userLangs as $priority => $langcode) { $this->langFilePath = $this->getConfigFilename($langcode); if (file_exists($this->langFilePath)) { $this->appliedLang = $langcode; break; } } if ($this->appliedLang == NULL) { throw new RuntimeException('No language file was found.'); } // search for cache file $this->cacheFilePath = $this->cachePath . '/php_i18n_' . md5_file(__FILE__) . '_' . $this->prefix . '_' . $this->appliedLang . '.cache.php'; // whether we need to create a new cache file $outdated = !file_exists($this->cacheFilePath) || filemtime($this->cacheFilePath) < filemtime($this->langFilePath) || // the language config was updated ($this->mergeFallback && filemtime($this->cacheFilePath) < filemtime($this->getConfigFilename($this->fallbackLang))); // the fallback language config was updated if ($outdated || true) { $config = $this->load($this->langFilePath); if ($this->mergeFallback) $config = array_replace_recursive($this->load($this->getConfigFilename($this->fallbackLang)), $config); $compiled = "prefix . " {\n" . $this->compile($config) . ' public static function __callStatic($string, $args) {' . "\n" . ' if (defined("self::" . $string)) {' . "\n" . ' return vsprintf(constant("self::" . $string), $args);' . "\n" . " }\n" . " }\n" . " function ".$this->prefix .'($string, $args=NULL) {' . "\n" . ' $return = constant("'.$this->prefix.'::".$string);' . "\n" . ' return $args ? vsprintf($return,$args) : $return;' . "\n" . " }\n" . '}'; if( ! is_dir($this->cachePath)) { mkdir($this->cachePath, 0755, true); } if (file_put_contents($this->cacheFilePath, $compiled) === FALSE) { throw new Exception("Could not write cache file to path '" . $this->cacheFilePath . "'. Is it writable?"); } chmod($this->cacheFilePath, 0755); } require_once $this->cacheFilePath; } public function isInitialized() { return $this->isInitialized; } public function getAppliedLang() { return $this->appliedLang; } public function getCachePath() { return $this->cachePath; } public function getFallbackLang() { return $this->fallbackLang; } public function setFilePath($filePath) { $this->fail_after_init(); $this->filePath = $filePath; } public function setCachePath($cachePath) { $this->fail_after_init(); $this->cachePath = $cachePath; } public function setFallbackLang($fallbackLang) { $this->fail_after_init(); $this->fallbackLang = $fallbackLang; } public function setMergeFallback($mergeFallback) { $this->fail_after_init(); $this->mergeFallback = $mergeFallback; } public function setPrefix($prefix) { $this->fail_after_init(); $this->prefix = $prefix; } public function setForcedLang($forcedLang) { $this->fail_after_init(); $this->forcedLang = $forcedLang; } public function setSectionSeparator($sectionSeparator) { $this->fail_after_init(); $this->sectionSeparator = $sectionSeparator; } /** * @deprecated Use setSectionSeparator. */ public function setSectionSeperator($sectionSeparator) { $this->setSectionSeparator($sectionSeparator); } /** * Returns the user languages * * Normally it returns an array like this: * 1. Forced language * 2. Language in $_GET['lang'] * 3. Language in $_SESSION['lang'] * 4. HTTP_ACCEPT_LANGUAGE * 5. Fallback language * Note: duplicate values are deleted. * * @return array with the user languages sorted by priority. */ public function getUserLangs() { $userLangs = array(); // Highest priority: forced language if ($this->forcedLang != NULL) { $userLangs[] = $this->forcedLang; } // 2nd highest priority: GET parameter 'lang' if (isset($_GET['lang']) && is_string($_GET['lang'])) { $userLangs[] = $_GET['lang']; } // 3rd highest priority: SESSION parameter 'lang' if (isset($_SESSION['lang']) && is_string($_SESSION['lang'])) { $userLangs[] = $_SESSION['lang']; } // 4th highest priority: HTTP_ACCEPT_LANGUAGE if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { foreach (explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']) as $part) { $userLangs[] = strtolower(substr($part, 0, 2)); } } // Lowest priority: fallback $userLangs[] = $this->fallbackLang; // remove duplicate elements $userLangs = array_unique($userLangs); // remove illegal userLangs $userLangs2 = array(); foreach ($userLangs as $key => $value) { // only allow a-z, A-Z and 0-9 and _ and - if (preg_match('/^[a-zA-Z0-9_-]*$/', $value) === 1) $userLangs2[$key] = $value; } return $userLangs2; } protected function getConfigFilename($langcode) { return str_replace('{LANGUAGE}', $langcode, $this->filePath); } protected function load($filename) { $ext = substr(strrchr($filename, '.'), 1); switch ($ext) { case 'properties': case 'ini': $config = parse_ini_file($filename, true); break; case 'yml': case 'yaml': $config = spyc_load_file($filename); break; case 'json': $config = json_decode(file_get_contents($filename), true); break; default: throw new InvalidArgumentException($ext . " is not a valid extension!"); } return $config; } /** * Recursively compile an associative array to PHP code. */ protected function compile($config, $prefix = '') { $code = ''; foreach ($config as $key => $value) { if (is_array($value)) { $code .= $this->compile($value, $prefix . $key . $this->sectionSeparator); } else { $fullName = $prefix . $key; if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $fullName)) { throw new InvalidArgumentException(__CLASS__ . ": Cannot compile translation key " . $fullName . " because it is not a valid PHP identifier."); } $code .= ' const ' . $fullName . ' = \'' . str_replace('\'', '\\\'', $value) . "';\n"; } } return $code; } protected function fail_after_init() { if ($this->isInitialized()) { throw new BadMethodCallException('This ' . __CLASS__ . ' object is already initalized, so you can not change any settings.'); } } }