/** * @param string $code * * @return array */ protected function getSpecialPageAliases($code) { $file = Language::getMessagesFileName($code); if (is_readable($file)) { include $file; if (isset($specialPageAliases) && $specialPageAliases !== null) { return $specialPageAliases; } } return []; }
public function execute() { foreach ($this->mLangs as $code) { $filename = Language::getMessagesFileName($code); $this->output("Loading language [{$code}] ... "); unset($digitTransformTable); require_once $filename; if (!isset($digitTransformTable)) { $this->error("\$digitTransformTable not found for lang: {$code}"); continue; } $this->output("OK\n\$digitTransformTable = array(\n"); foreach ($digitTransformTable as $latin => $translation) { $htmlent = utf8ToHexSequence($translation); $this->output("'{$latin}' => '{$translation}', # &#x{$htmlent};\n"); } $this->output(");\n"); } }
/** * Rewrite a messages array. * * @param $code The language code. * @param $write Write to the messages file? */ function rebuildLanguage($code, $write) { global $wgLanguages, $wg; # Get messages $messages = $wgLanguages->getMessages($code); $messages = $messages['all']; # Rewrite messages array $messagesText = writeMessagesArray($messages, $code == 'en'); # Write to the file if ($write) { $filename = Language::getMessagesFileName($code); $contents = file_get_contents($filename); if (strpos($contents, '$messages') !== false) { $new = explode('$messages', $contents); $new = $new[0]; $new .= $messagesText; $new .= "\n?>\n"; file_put_contents($filename, $new); echo "Generated and wrote messages in language {$code}.\n"; } } else { echo "Generated messages in language {$code}.\n"; } }
<?php /** * @file * @ingroup MaintenanceLanguage */ require '../commandLine.inc'; # A list of unicode numerals is available at: # http://www.fileformat.info/info/unicode/category/Nd/list.htm $langs = array('Ar', 'As', 'Bh', 'Bo', 'Dz', 'Fa', 'Gu', 'Hi', 'Km', 'Kn', 'Ks', 'Lo', 'Ml', 'Mr', 'Ne', 'New', 'Or', 'Pa', 'Pi', 'Sa'); foreach ($langs as $code) { $filename = Language::getMessagesFileName($code); echo "Loading language [{$code}] ... "; unset($digitTransformTable); require_once $filename; if (!isset($digitTransformTable)) { print "\$digitTransformTable not found\n"; continue; } print "OK\n\$digitTransformTable = array(\n"; foreach ($digitTransformTable as $latin => $translation) { $htmlent = utf8ToHexSequence($translation); print "'{$latin}' => '{$translation}', # &#x{$htmlent};\n"; } print ");\n"; }
/** * Read the data from the source files for a given language, and register * the relevant dependencies in the $deps array. If the localisation * exists, the data array is returned, otherwise false is returned. */ protected function readSourceFilesAndRegisterDeps($code, &$deps) { global $IP; wfProfileIn(__METHOD__); // This reads in the PHP i18n file with non-messages l10n data $fileName = Language::getMessagesFileName($code); if (!file_exists($fileName)) { $data = array(); } else { $deps[] = new FileDependency($fileName); $data = $this->readPHPFile($fileName, 'core'); } # Load CLDR plural rules for JavaScript $data['pluralRules'] = $this->getPluralRules($code); # And for PHP $data['compiledPluralRules'] = $this->getCompiledPluralRules($code); # Load plural rule types $data['pluralRuleTypes'] = $this->getPluralRuleTypes($code); $deps['plurals'] = new FileDependency("{$IP}/languages/data/plurals.xml"); $deps['plurals-mw'] = new FileDependency("{$IP}/languages/data/plurals-mediawiki.xml"); wfProfileOut(__METHOD__); return $data; }
/** * Update the MediaWiki Core Messages. * * @param $verbose Boolean * * @return Integer: the amount of updated messages */ public static function updateMediawikiMessages($verbose, $coreUrl) { // Find the changed English strings (as these messages won't be updated in ANY language). $localUrl = Language::getMessagesFileName('en'); $repoUrl = str_replace('$2', 'languages/messages/MessagesEn.php', $coreUrl); $changedEnglishStrings = self::compareFiles($repoUrl, $localUrl, $verbose); // Count the changes. $changedCount = 0; $languages = Language::fetchLanguageNames(null, 'mwfile'); foreach (array_keys($languages) as $code) { $localUrl = Language::getMessagesFileName($code); // Not prefixed with $IP $filename = Language::getFilename('languages/messages/Messages', $code); $repoUrl = str_replace('$2', $filename, $coreUrl); // Compare the files. $changedCount += self::compareFiles($repoUrl, $localUrl, $verbose, $changedEnglishStrings, false, true); } // Log some nice info. self::myLog("{$changedCount} MediaWiki messages are updated"); return $changedCount; }
/** * Read the data from the source files for a given language, and register * the relevant dependencies in the $deps array. If the localisation * exists, the data array is returned, otherwise false is returned. */ protected function readSourceFilesAndRegisterDeps($code, &$deps) { $fileName = Language::getMessagesFileName($code); if (!file_exists($fileName)) { return false; } $deps[] = new FileDependency($fileName); $data = $this->readPHPFile($fileName, 'core'); # Load CLDR plural rules for JavaScript $data['pluralRules'] = $this->getPluralRules($code); # And for PHP $data['compiledPluralRules'] = $this->getCompiledPluralRules($code); $deps['plurals'] = new FileDependency(__DIR__ . "/../languages/data/plurals.xml"); $deps['plurals-mw'] = new FileDependency(__DIR__ . "/../languages/data/plurals-mediawiki.xml"); return $data; }
/** * Load localisation data for a given language for both core and extensions * and save it to the persistent cache store and the process cache */ public function recache($code) { static $recursionGuard = array(); global $wgExtensionMessagesFiles, $wgExtensionAliasesFiles; 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); 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'); 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) { # Guard against circular references if (isset($recursionGuard[$code])) { throw new MWException("Error: Circular fallback reference in language code {$code}"); } $recursionGuard[$code] = true; # Load the fallback localisation item by item and merge it $deps = array_merge($deps, $this->getItem($coreData['fallback'], 'deps')); foreach (self::$allKeys as $key) { if (is_null($coreData[$key]) || $this->isMergeableKey($key)) { $fallbackValue = $this->getItem($coreData['fallback'], $key); $this->mergeItem($key, $coreData[$key], $fallbackValue); } } $fallbackSequence = $this->getItem($coreData['fallback'], 'fallbackSequence'); array_unshift($fallbackSequence, $coreData['fallback']); $coreData['fallbackSequence'] = $fallbackSequence; unset($recursionGuard[$code]); } else { $coreData['fallbackSequence'] = array(); } $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); } } # Load deprecated $wgExtensionAliasesFiles foreach ($wgExtensionAliasesFiles as $fileName) { $data = $this->readPHPFile($fileName, 'aliases'); if (!isset($data['aliases'])) { continue; } $used = $this->mergeExtensionItem($codeSequence, 'specialPageAliases', $allData['specialPageAliases'], $data['aliases']); 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['wgExtensionAliasesFiles'] = new GlobalDependency('wgExtensionAliasesFiles'); $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); # Fix broken defaultUserOptionOverrides if (!is_array($allData['defaultUserOptionOverrides'])) { $allData['defaultUserOptionOverrides'] = array(); } # 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['defaultUserOptionOverrides'])) { 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__); }
function generatePatch() { global $wgOut, $wgRequest, $wgTmpDirectory, $IP; $originalMsgs = $wgRequest->getVal('originalMsgs'); if (!$originalMsgs) { throw new MWException('Bad form input: no originalMsgs'); } $originalMsgs = json_decode(gzinflate(base64_decode($originalMsgs))); if (!$originalMsgs) { throw new MWException('Bad form input: originalMsgs invalid'); } $newMsgs = $wgRequest->getArray('msg'); $messageName = $wgRequest->getVal('messageName'); if (!$newMsgs) { throw new MWException('Bad form input: msg missing'); } if (!$messageName) { throw new MWException('Bad form input: messageName missing'); } $quote = "['\"]"; $encMsgName = preg_quote($messageName, '/'); $warnings = array(); //( 'parse' => array(), 'file' => array(), 'mismatch' => array() ); $escapedChars = array('single' => array("'", "\\"), 'double' => array('n', 'r', 't', 'v', 'f', "\\", '$', '"')); $dcRegex = '/[0-7]{1,3}|x[0-9A-Fa-f]{1,2}/'; wfMkdirParents("{$wgTmpDirectory}/EditMessages"); $out = ''; foreach ($originalMsgs as $lang => $origValue) { if (!isset($newMsgs[$lang])) { continue; } $newValue = $newMsgs[$lang]; if ($newValue === $origValue) { # No change requested continue; } $fileName = Language::getMessagesFileName($lang); $text = file_get_contents($fileName); if (!$text) { $warnings['file'][] = $lang; continue; } # Find the message name in the file text if (!preg_match("/^\\s*{$quote}{$encMsgName}{$quote}\\s*=>\\s*/m", $text, $m, PREG_OFFSET_CAPTURE)) { $warnings['parse1'][] = $lang; continue; } # Determine the starting quote character $i = $startPos = $m[0][1] + strlen($m[0][0]); $quoteChar = substr($text, $i, 1); if ($quoteChar == '"') { $mode = 'double'; } elseif ($quoteChar == "'") { $mode = 'single'; } else { $warnings['parse2'][] = $lang; continue; } # Search for the end of the string, respecting escaping $i++; $found = false; do { $curChar = substr($text, $i, 1); if ($curChar === '\\') { $nextChar = substr($text, $i + 1, 1); if (in_array($nextChar, $escapedChars[$mode])) { $i += 2; continue; } if ($mode == 'double' && preg_match($dcRegex, $text, $m, 0, $i + 1)) { $i += strlen($m[0]) + 1; continue; } } elseif ($curChar === $quoteChar) { $found = true; break; } ++$i; } while ($i < strlen($text)); if (!$found) { $warnings['parse3'][] = $lang; continue; } $length = $i - $startPos + 1; # Evaluate the string that we just got from the message file, so that we can # see if it matches the expected starting value $fileValue = eval('return ' . substr($text, $startPos, $length) . ';'); if ($fileValue !== $origValue) { $warnings['mismatch'][] = $lang; continue; } # Escape the new value, keeping the same quoting style if ($mode == 'single') { $encNewValue = var_export($newValue, true); } else { $encNewValue = '"' . strtr($newValue, array('\\' => '\\\\', '"' => '\\"', '$' => '\\$')) . '"'; } # Replace the string with the new value $newText = substr_replace($text, $encNewValue, $startPos, $length); # Generate the diff $tempName = "{$wgTmpDirectory}/EditMessages/" . basename($fileName); file_put_contents($tempName, $newText); $cmd = "diff -u " . wfEscapeShellArg($fileName, $tempName); $diff = "{$cmd}\n" . wfShellExec($cmd); $diff = str_replace("{$IP}/", '', $diff); $diff = str_replace("\r", '', $diff); $out .= $diff; } foreach ($warnings as $warningType => $warnings2) { if (count($warnings2)) { $wgOut->addWikiMsg('editmsg-warning-' . $warningType, implode(', ', $warnings2)); } } if ($out !== '') { $wgOut->addHTML('<pre><bdo dir="ltr">' . htmlspecialchars($out) . '</bdo></pre>'); $encodedValue = chunk_split(base64_encode(gzdeflate($out)), 120, ' '); $encAction = $this->getTitle()->escapeFullUrl(); $wgOut->addHTML("<p>\n" . "<form method=\"POST\" action=\"{$encAction}\">" . "<input type=\"hidden\" name=\"diffText\" value=\"{$encodedValue}\"/>" . '<input type="submit" name="editmsg_apply_patch" value="' . htmlspecialchars(wfMsg('editmsg-apply-patch')) . '"/>' . '</form></p>'); } }
public function __construct($code) { parent::__construct($code); $this->data['core'] = array('label' => 'MediaWiki Core', 'var' => 'namespaceNames', 'file' => Language::getMessagesFileName(self::PLACEHOLDER), 'code' => false); }
/** * 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']); // wikia change begin // author: mech // allow extensions to use hook to add messages files // this can be used in cases when enumerating message files is expensive so it shouldn't be done in setup file wfRunHooks('BeforeExtensionMessagesRecache', array(&$wgExtensionMessagesFiles)); // wikia change end # 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__); }