/** * Load localisation data for a given language for both core and extensions * and save it to the persistent cache store and the process cache * @param string $code * @throws MWException */ public function recache($code) { global $wgExtensionMessagesFiles; if (!$code) { throw new MWException("Invalid language code requested"); } $this->recachedLangs[$code] = true; # Initial values $initialData = array_fill_keys(self::$allKeys, null); $coreData = $initialData; $deps = []; # Load the primary localisation from the source file $data = $this->readSourceFilesAndRegisterDeps($code, $deps); if ($data === false) { wfDebug(__METHOD__ . ": no localisation file for {$code}, using fallback to en\n"); $coreData['fallback'] = 'en'; } else { wfDebug(__METHOD__ . ": got localisation for {$code} from source\n"); # Merge primary localisation foreach ($data as $key => $value) { $this->mergeItem($key, $coreData[$key], $value); } } # Fill in the fallback if it's not there already if (is_null($coreData['fallback'])) { $coreData['fallback'] = $code === 'en' ? false : 'en'; } if ($coreData['fallback'] === false) { $coreData['fallbackSequence'] = []; } else { $coreData['fallbackSequence'] = array_map('trim', explode(',', $coreData['fallback'])); $len = count($coreData['fallbackSequence']); # Ensure that the sequence ends at en if ($coreData['fallbackSequence'][$len - 1] !== 'en') { $coreData['fallbackSequence'][] = 'en'; } } $codeSequence = array_merge([$code], $coreData['fallbackSequence']); $messageDirs = $this->getMessagesDirs(); # Load non-JSON localisation data for extensions $extensionData = array_fill_keys($codeSequence, $initialData); foreach ($wgExtensionMessagesFiles as $extension => $fileName) { if (isset($messageDirs[$extension])) { # This extension has JSON message data; skip the PHP shim continue; } $data = $this->readPHPFile($fileName, 'extension'); $used = false; foreach ($data as $key => $item) { foreach ($codeSequence as $csCode) { if (isset($item[$csCode])) { $this->mergeItem($key, $extensionData[$csCode][$key], $item[$csCode]); $used = true; } } } if ($used) { $deps[] = new FileDependency($fileName); } } # Load the localisation data for each fallback, then merge it into the full array $allData = $initialData; foreach ($codeSequence as $csCode) { $csData = $initialData; # Load core messages and the extension localisations. foreach ($messageDirs as $dirs) { foreach ((array) $dirs as $dir) { $fileName = "{$dir}/{$csCode}.json"; $data = $this->readJSONFile($fileName); foreach ($data as $key => $item) { $this->mergeItem($key, $csData[$key], $item); } $deps[] = new FileDependency($fileName); } } # Merge non-JSON extension data if (isset($extensionData[$csCode])) { foreach ($extensionData[$csCode] as $key => $item) { $this->mergeItem($key, $csData[$key], $item); } } if ($csCode === $code) { # Merge core data into extension data foreach ($coreData as $key => $item) { $this->mergeItem($key, $csData[$key], $item); } } else { # Load the secondary localisation from the source file to # avoid infinite cycles on cyclic fallbacks $fbData = $this->readSourceFilesAndRegisterDeps($csCode, $deps); if ($fbData !== false) { # Only merge the keys that make sense to merge foreach (self::$allKeys as $key) { if (!isset($fbData[$key])) { continue; } if (is_null($coreData[$key]) || $this->isMergeableKey($key)) { $this->mergeItem($key, $csData[$key], $fbData[$key]); } } } } # Allow extensions an opportunity to adjust the data for this # fallback Hooks::run('LocalisationCacheRecacheFallback', [$this, $csCode, &$csData]); # Merge the data for this fallback into the final array if ($csCode === $code) { $allData = $csData; } else { foreach (self::$allKeys as $key) { if (!isset($csData[$key])) { continue; } if (is_null($allData[$key]) || $this->isMergeableKey($key)) { $this->mergeItem($key, $allData[$key], $csData[$key]); } } } } # Add cache dependencies for any referenced globals $deps['wgExtensionMessagesFiles'] = new GlobalDependency('wgExtensionMessagesFiles'); // The 'MessagesDirs' config setting is used in LocalisationCache::getMessagesDirs(). // We use the key 'wgMessagesDirs' for historical reasons. $deps['wgMessagesDirs'] = new MainConfigDependency('MessagesDirs'); $deps['version'] = new ConstantDependency('LocalisationCache::VERSION'); # Add dependencies to the cache entry $allData['deps'] = $deps; # Replace spaces with underscores in namespace names $allData['namespaceNames'] = str_replace(' ', '_', $allData['namespaceNames']); # And do the same for special page aliases. $page is an array. foreach ($allData['specialPageAliases'] as &$page) { $page = str_replace(' ', '_', $page); } # Decouple the reference to prevent accidental damage unset($page); # If there were no plural rules, return an empty array if ($allData['pluralRules'] === null) { $allData['pluralRules'] = []; } if ($allData['compiledPluralRules'] === null) { $allData['compiledPluralRules'] = []; } # If there were no plural rule types, return an empty array if ($allData['pluralRuleTypes'] === null) { $allData['pluralRuleTypes'] = []; } # Set the list keys $allData['list'] = []; foreach (self::$splitKeys as $key) { $allData['list'][$key] = array_keys($allData[$key]); } # Run hooks $purgeBlobs = true; Hooks::run('LocalisationCacheRecache', [$this, $code, &$allData, &$purgeBlobs]); if (is_null($allData['namespaceNames'])) { throw new MWException(__METHOD__ . ': Localisation data failed sanity check! ' . 'Check that your languages/messages/MessagesEn.php file is intact.'); } # Set the preload key $allData['preload'] = $this->buildPreload($allData); # Save to the process cache and register the items loaded $this->data[$code] = $allData; foreach ($allData as $key => $item) { $this->loadedItems[$code][$key] = true; } # Save to the persistent cache $this->store->startWrite($code); foreach ($allData as $key => $value) { if (in_array($key, self::$splitKeys)) { foreach ($value as $subkey => $subvalue) { $this->store->set("{$key}:{$subkey}", $subvalue); } } else { $this->store->set($key, $value); } } $this->store->finishWrite(); # Clear out the MessageBlobStore # HACK: If using a null (i.e. disabled) storage backend, we # can't write to the MessageBlobStore either if ($purgeBlobs && !$this->store instanceof LCStoreNull) { $blobStore = new MessageBlobStore(); $blobStore->clear(); } }
/** * Load localisation data for a given language for both core and extensions * and save it to the persistent cache store and the process cache * @param $code * @throws MWException */ public function recache($code) { global $wgExtensionMessagesFiles, $wgMessagesDirs; wfProfileIn(__METHOD__); if (!$code) { wfProfileOut(__METHOD__); throw new MWException("Invalid language code requested"); } $this->recachedLangs[$code] = true; # Initial values $initialData = array_combine(self::$allKeys, array_fill(0, count(self::$allKeys), null)); $coreData = $initialData; $deps = array(); # Load the primary localisation from the source file $data = $this->readSourceFilesAndRegisterDeps($code, $deps); if ($data === false) { wfDebug(__METHOD__ . ": no localisation file for {$code}, using fallback to en\n"); $coreData['fallback'] = 'en'; } else { wfDebug(__METHOD__ . ": got localisation for {$code} from source\n"); # Merge primary localisation foreach ($data as $key => $value) { $this->mergeItem($key, $coreData[$key], $value); } } # Fill in the fallback if it's not there already if (is_null($coreData['fallback'])) { $coreData['fallback'] = $code === 'en' ? false : 'en'; } if ($coreData['fallback'] === false) { $coreData['fallbackSequence'] = array(); } else { $coreData['fallbackSequence'] = array_map('trim', explode(',', $coreData['fallback'])); $len = count($coreData['fallbackSequence']); # Ensure that the sequence ends at en if ($coreData['fallbackSequence'][$len - 1] !== 'en') { $coreData['fallbackSequence'][] = 'en'; } # Load the fallback localisation item by item and merge it foreach ($coreData['fallbackSequence'] as $fbCode) { # Load the secondary localisation from the source file to # avoid infinite cycles on cyclic fallbacks $fbData = $this->readSourceFilesAndRegisterDeps($fbCode, $deps); if ($fbData === false) { continue; } foreach (self::$allKeys as $key) { if (!isset($fbData[$key])) { continue; } if (is_null($coreData[$key]) || $this->isMergeableKey($key)) { $this->mergeItem($key, $coreData[$key], $fbData[$key]); } } } } $codeSequence = array_merge(array($code), $coreData['fallbackSequence']); # Load core messages and the extension localisations. wfProfileIn(__METHOD__ . '-extensions'); $allData = $initialData; foreach ($wgMessagesDirs as $dirs) { foreach ((array) $dirs as $dir) { foreach ($codeSequence as $csCode) { $fileName = "{$dir}/{$csCode}.json"; $data = $this->readJSONFile($fileName); foreach ($data as $key => $item) { $this->mergeItem($key, $allData[$key], $item); } $deps[] = new FileDependency($fileName); } } } foreach ($wgExtensionMessagesFiles as $extension => $fileName) { if (isset($wgMessagesDirs[$extension])) { # Already loaded the JSON files for this extension; skip the PHP shim continue; } $data = $this->readPHPFile($fileName, 'extension'); $used = false; foreach ($data as $key => $item) { if ($this->mergeExtensionItem($codeSequence, $key, $allData[$key], $item)) { $used = true; } } if ($used) { $deps[] = new FileDependency($fileName); } } # Merge core data into extension data foreach ($coreData as $key => $item) { $this->mergeItem($key, $allData[$key], $item); } wfProfileOut(__METHOD__ . '-extensions'); # Add cache dependencies for any referenced globals $deps['wgExtensionMessagesFiles'] = new GlobalDependency('wgExtensionMessagesFiles'); $deps['wgMessagesDirs'] = new GlobalDependency('wgMessagesDirs'); $deps['version'] = new ConstantDependency('MW_LC_VERSION'); # Add dependencies to the cache entry $allData['deps'] = $deps; # Replace spaces with underscores in namespace names $allData['namespaceNames'] = str_replace(' ', '_', $allData['namespaceNames']); # And do the same for special page aliases. $page is an array. foreach ($allData['specialPageAliases'] as &$page) { $page = str_replace(' ', '_', $page); } # Decouple the reference to prevent accidental damage unset($page); # If there were no plural rules, return an empty array if ($allData['pluralRules'] === null) { $allData['pluralRules'] = array(); } if ($allData['compiledPluralRules'] === null) { $allData['compiledPluralRules'] = array(); } # If there were no plural rule types, return an empty array if ($allData['pluralRuleTypes'] === null) { $allData['pluralRuleTypes'] = array(); } # Set the list keys $allData['list'] = array(); foreach (self::$splitKeys as $key) { $allData['list'][$key] = array_keys($allData[$key]); } # Run hooks $purgeBlobs = true; wfRunHooks('LocalisationCacheRecache', array($this, $code, &$allData, &$purgeBlobs)); if (is_null($allData['namespaceNames'])) { wfProfileOut(__METHOD__); throw new MWException(__METHOD__ . ': Localisation data failed sanity check! ' . 'Check that your languages/messages/MessagesEn.php file is intact.'); } # Set the preload key $allData['preload'] = $this->buildPreload($allData); # Save to the process cache and register the items loaded $this->data[$code] = $allData; foreach ($allData as $key => $item) { $this->loadedItems[$code][$key] = true; } # Save to the persistent cache wfProfileIn(__METHOD__ . '-write'); $this->store->startWrite($code); foreach ($allData as $key => $value) { if (in_array($key, self::$splitKeys)) { foreach ($value as $subkey => $subvalue) { $this->store->set("{$key}:{$subkey}", $subvalue); } } else { $this->store->set($key, $value); } } $this->store->finishWrite(); wfProfileOut(__METHOD__ . '-write'); # Clear out the MessageBlobStore # HACK: If using a null (i.e. disabled) storage backend, we # can't write to the MessageBlobStore either if ($purgeBlobs && !$this->store instanceof LCStoreNull) { MessageBlobStore::clear(); } wfProfileOut(__METHOD__); }
/** * Load localisation data for a given language for both core and extensions * and save it to the persistent cache store and the process cache * @param $code */ public function recache($code) { global $wgExtensionMessagesFiles; wfProfileIn(__METHOD__); if (!$code) { throw new MWException("Invalid language code requested"); } $this->recachedLangs[$code] = true; # Initial values $initialData = array_combine(self::$allKeys, array_fill(0, count(self::$allKeys), null)); $coreData = $initialData; $deps = array(); # Load the primary localisation from the source file $fileName = Language::getMessagesFileName($code); $addFileName = Language::getAdditionalMessagesFileName($code, 'core'); if (!file_exists($fileName)) { wfDebug(__METHOD__ . ": no localisation file for {$code}, using fallback to en\n"); $coreData['fallback'] = 'en'; } else { $deps[] = new FileDependency($fileName); $data = $this->readPHPFile($fileName, 'core'); // wikia changes begin if (file_exists($addFileName)) { $deps[] = new FileDependency($addFileName); $addData = $this->readPHPFile($addFileName, 'core'); if (!empty($addData['messages'])) { $data['messages'] = array_merge($data['messages'], $addData['messages']); } } // wikia changes end wfDebug(__METHOD__ . ": got localisation for {$code} from source\n"); # Merge primary localisation foreach ($data as $key => $value) { $this->mergeItem($key, $coreData[$key], $value); } } # Fill in the fallback if it's not there already if (is_null($coreData['fallback'])) { $coreData['fallback'] = $code === 'en' ? false : 'en'; } if ($coreData['fallback'] === false) { $coreData['fallbackSequence'] = array(); } else { $coreData['fallbackSequence'] = array_map('trim', explode(',', $coreData['fallback'])); $len = count($coreData['fallbackSequence']); # Ensure that the sequence ends at en if ($coreData['fallbackSequence'][$len - 1] !== 'en') { $coreData['fallbackSequence'][] = 'en'; } # Load the fallback localisation item by item and merge it foreach ($coreData['fallbackSequence'] as $fbCode) { # Load the secondary localisation from the source file to # avoid infinite cycles on cyclic fallbacks $fbFilename = Language::getMessagesFileName($fbCode); $fbAddFileName = Language::getAdditionalMessagesFileName($fbCode, 'core'); if (!file_exists($fbFilename)) { continue; } $deps[] = new FileDependency($fbFilename); $fbData = $this->readPHPFile($fbFilename, 'core'); // wikia changes begin if (file_exists($fbAddFileName)) { $deps[] = new FileDependency($fbAddFileName); $addData = $this->readPHPFile($fbAddFileName, 'core'); if (!empty($addData['messages'])) { $fbData['messages'] = array_merge($fbData['messages'], $addData['messages']); } } // wikia changes end wfDebug(__METHOD__ . ": got fallback localisation for {$fbCode} from source\n"); foreach (self::$allKeys as $key) { if (!isset($fbData[$key])) { continue; } if (is_null($coreData[$key]) || $this->isMergeableKey($key)) { $this->mergeItem($key, $coreData[$key], $fbData[$key]); } } } } $codeSequence = array_merge(array($code), $coreData['fallbackSequence']); # Load the extension localisations # This is done after the core because we know the fallback sequence now. # But it has a higher precedence for merging so that we can support things # like site-specific message overrides. $allData = $initialData; foreach ($wgExtensionMessagesFiles as $fileName) { $data = $this->readPHPFile($fileName, 'extension'); $used = false; foreach ($data as $key => $item) { if ($this->mergeExtensionItem($codeSequence, $key, $allData[$key], $item)) { $used = true; } } if ($used) { $deps[] = new FileDependency($fileName); } } # Merge core data into extension data foreach ($coreData as $key => $item) { $this->mergeItem($key, $allData[$key], $item); } # Add cache dependencies for any referenced globals $deps['wgExtensionMessagesFiles'] = new GlobalDependency('wgExtensionMessagesFiles'); $deps['version'] = new ConstantDependency('MW_LC_VERSION'); # Add dependencies to the cache entry $allData['deps'] = $deps; # Replace spaces with underscores in namespace names $allData['namespaceNames'] = str_replace(' ', '_', $allData['namespaceNames']); # And do the same for special page aliases. $page is an array. foreach ($allData['specialPageAliases'] as &$page) { $page = str_replace(' ', '_', $page); } # Decouple the reference to prevent accidental damage unset($page); # Set the list keys $allData['list'] = array(); foreach (self::$splitKeys as $key) { $allData['list'][$key] = array_keys($allData[$key]); } # Run hooks wfRunHooks('LocalisationCacheRecache', array($this, $code, &$allData)); if (is_null($allData['namespaceNames'])) { throw new MWException(__METHOD__ . ': Localisation data failed sanity check! ' . 'Check that your languages/messages/MessagesEn.php file is intact.'); } # Set the preload key $allData['preload'] = $this->buildPreload($allData); # Save to the process cache and register the items loaded $this->data[$code] = $allData; foreach ($allData as $key => $item) { $this->loadedItems[$code][$key] = true; } # Save to the persistent cache $this->store->startWrite($code); foreach ($allData as $key => $value) { if (in_array($key, self::$splitKeys)) { foreach ($value as $subkey => $subvalue) { $this->store->set("{$key}:{$subkey}", $subvalue); } } else { $this->store->set($key, $value); } } $this->store->finishWrite(); # Clear out the MessageBlobStore # HACK: If using a null (i.e. disabled) storage backend, we # can't write to the MessageBlobStore either if (!$this->store instanceof LCStore_Null) { MessageBlobStore::clear(); } wfProfileOut(__METHOD__); }