/** * Loads a translation file * Will load from cache if possible, or generate cache if needed * * Also checks for translation files expiry based on mtime if RegionalSettings.TranslationCheckMTime is enabled * * @access private * @param string $locale * @param string $filename * @param string $requestedContext * * @return bool The operation status, true or false */ function loadTranslationFile($locale, $filename, $requestedContext) { // First try for current charset $charset = eZTextCodec::internalCharset(); $tsTimeStamp = false; $ini = eZINI::instance(); $checkMTime = $ini->variable('RegionalSettings', 'TranslationCheckMTime') === 'enabled'; if (!$this->RootCache) { $roots = array($ini->variable('RegionalSettings', 'TranslationRepository')); $extensionBase = eZExtension::baseDirectory(); $translationExtensions = $ini->variable('RegionalSettings', 'TranslationExtensions'); foreach ($translationExtensions as $translationExtension) { $extensionPath = $extensionBase . '/' . $translationExtension . '/translations'; if (!$checkMTime || file_exists($extensionPath)) { $roots[] = $extensionPath; } } $this->RootCache = array('roots' => $roots); } else { $roots = $this->RootCache['roots']; if (isset($this->RootCache['timestamp'])) { $tsTimeStamp = $this->RootCache['timestamp']; } } // Load cached translations if possible if ($this->UseCache == true) { if (!$tsTimeStamp) { $expiry = eZExpiryHandler::instance(); $globalTsTimeStamp = $expiry->getTimestamp(self::EXPIRY_KEY, 0); $localeTsTimeStamp = $expiry->getTimestamp(self::EXPIRY_KEY . '-' . $locale, 0); $tsTimeStamp = max($globalTsTimeStamp, $localeTsTimeStamp); if ($checkMTime && $tsTimeStamp < time()) { // iterate over each known TS file, and get the highest timestamp // this value will be used to check for cache validity foreach ($roots as $root) { $path = eZDir::path(array($root, $locale, $charset, $filename)); if (file_exists($path)) { $timestamp = filemtime($path); if ($timestamp > $tsTimeStamp) { $tsTimeStamp = $timestamp; } } else { $path = eZDir::path(array($root, $locale, $filename)); if (file_exists($path)) { $timestamp = filemtime($path); if ($timestamp > $tsTimeStamp) { $tsTimeStamp = $timestamp; } } } } } $this->RootCache['timestamp'] = $tsTimeStamp; } $key = 'cachecontexts'; if ($this->HasRestoredCache or eZTranslationCache::canRestoreCache($key, $tsTimeStamp)) { eZDebug::accumulatorStart('tstranslator_cache_load', 'tstranslator', 'TS cache load'); if (!$this->HasRestoredCache) { if (!eZTranslationCache::restoreCache($key)) { $this->BuildCache = true; } $contexts = eZTranslationCache::contextCache($key); if (!is_array($contexts)) { $contexts = array(); } $this->HasRestoredCache = $contexts; } else { $contexts = $this->HasRestoredCache; } if (!$this->BuildCache) { $contextName = $requestedContext; if (!isset($this->CachedMessages[$contextName])) { eZDebug::accumulatorStart('tstranslator_context_load', 'tstranslator', 'TS context load'); if (eZTranslationCache::canRestoreCache($contextName, $tsTimeStamp)) { if (!eZTranslationCache::restoreCache($contextName)) { $this->BuildCache = true; } $this->CachedMessages[$contextName] = eZTranslationCache::contextCache($contextName); foreach ($this->CachedMessages[$contextName] as $key => $msg) { $this->Messages[$key] = $msg; } } eZDebug::accumulatorStop('tstranslator_context_load'); } } eZDebugSetting::writeNotice('i18n-tstranslator', "Loading cached translation", __METHOD__); eZDebug::accumulatorStop('tstranslator_cache_load'); if (!$this->BuildCache) { return true; } } eZDebugSetting::writeNotice('i18n-tstranslator', "Translation cache has expired. Will rebuild it from source.", __METHOD__); $this->BuildCache = true; } $status = false; // first process country translation files // then process country variation translation files $localeParts = explode('@', $locale); $triedPaths = array(); $loadedPaths = array(); $ini = eZINI::instance("i18n.ini"); $fallbacks = $ini->variable('TranslationSettings', 'FallbackLanguages'); foreach ($localeParts as $localePart) { $localeCodeToProcess = isset($localeCodeToProcess) ? $localeCodeToProcess . '@' . $localePart : $localePart; // array with alternative subdirs to check $alternatives = array(array($localeCodeToProcess, $charset, $filename), array($localeCodeToProcess, $filename)); if (isset($fallbacks[$localeCodeToProcess]) && $fallbacks[$localeCodeToProcess]) { if ($fallbacks[$localeCodeToProcess] === 'eng-GB') { $fallbacks[$localeCodeToProcess] = 'untranslated'; } $alternatives[] = array($fallbacks[$localeCodeToProcess], $charset, $filename); $alternatives[] = array($fallbacks[$localeCodeToProcess], $filename); } foreach ($roots as $root) { if (!file_exists($root)) { continue; } unset($path); foreach ($alternatives as $alternative) { $pathParts = $alternative; array_unshift($pathParts, $root); $pathToTry = eZDir::path($pathParts); $triedPaths[] = $pathToTry; if (file_exists($pathToTry)) { $path = $pathToTry; break; } } if (!isset($path)) { continue; } eZDebug::accumulatorStart('tstranslator_load', 'tstranslator', 'TS load'); $doc = new DOMDocument('1.0', 'utf-8'); $success = $doc->load($path); if (!$success) { eZDebug::writeWarning("Unable to load XML from file {$path}", __METHOD__); continue; } if (!$this->validateDOMTree($doc)) { eZDebug::writeWarning("XML text for file {$path} did not validate", __METHOD__); continue; } $loadedPaths[] = $path; $status = true; $treeRoot = $doc->documentElement; $children = $treeRoot->childNodes; for ($i = 0; $i < $children->length; $i++) { $child = $children->item($i); if ($child->nodeType == XML_ELEMENT_NODE) { if ($child->tagName == "context") { $this->handleContextNode($child); } } } eZDebug::accumulatorStop('tstranslator_load'); } } eZDebugSetting::writeDebug('i18n-tstranslator', implode(PHP_EOL, $triedPaths), __METHOD__ . ': tried paths'); eZDebugSetting::writeDebug('i18n-tstranslator', implode(PHP_EOL, $loadedPaths), __METHOD__ . ': loaded paths'); // Save translation cache if ($this->UseCache == true && $this->BuildCache == true) { eZDebug::accumulatorStart('tstranslator_store_cache', 'tstranslator', 'TS store cache'); if (eZTranslationCache::contextCache('cachecontexts') == null) { $contexts = array_keys($this->CachedMessages); eZTranslationCache::setContextCache('cachecontexts', $contexts); eZTranslationCache::storeCache('cachecontexts'); $this->HasRestoredCache = $contexts; } foreach ($this->CachedMessages as $contextName => $context) { if (eZTranslationCache::contextCache($contextName) == null) { eZTranslationCache::setContextCache($contextName, $context); } eZTranslationCache::storeCache($contextName); } $this->BuildCache = false; eZDebug::accumulatorStop('tstranslator_store_cache'); } return $status; }