protected function loadMessagesFromCache($groups) { $messages = array(); foreach ($groups as $group) { if ($group instanceof MessageGroupOld) { $messages += $group->getDefinitions(); continue; } if ($group instanceof AggregateMessageGroup) { $messages += $this->loadMessagesFromCache($group->getGroups()); continue; } $cache = new MessageGroupCache($group); if ($cache->exists()) { foreach ($cache->getKeys() as $key) { $messages[$key] = $cache->get($key); } } } return $messages; }
/** * @param $hugearray array * @param $g * @param $ignore bool */ protected function checkAndAdd(&$hugearray, $g, $ignore = false) { if ($g instanceof MessageGroupBase) { $cache = new MessageGroupCache($g); if ($cache->exists()) { $keys = $cache->getKeys(); } else { $keys = array_keys($g->getDefinitions()); } } else { $messages = $g->getDefinitions(); if (!is_array($messages)) { return; } $keys = array_keys($messages); } $id = $g->getId(); STDOUT("{$id} ", 'main'); $namespace = $g->getNamespace(); foreach ($keys as $key) { # Force all keys to lower case, because the case doesn't matter and it is # easier to do comparing when the case of first letter is unknown, because # mediawiki forces it to upper case $key = TranslateUtils::normaliseKey($namespace, $key); if (isset($hugearray[$key])) { if (!$ignore) { $to = implode(', ', (array) $hugearray[$key]); STDERR("Key {$key} already belongs to {$to}, conflict with {$id}"); } if (is_array($hugearray[$key])) { // Hard work is already done, just add a new reference $hugearray[$key][] =& $id; } else { // Store the actual reference, then remove it from array, to not // replace the references value, but to store a array of new // references instead. References are hard! $value =& $hugearray[$key]; unset($hugearray[$key]); $hugearray[$key] = array(&$value, &$id); } } else { $hugearray[$key] =& $id; } } unset($id); // Disconnect the previous references to this $id }
protected function getPotTime() { $defs = new MessageGroupCache( $this->group ); return $defs->exists() ? $defs->getTimestamp() : wfTimestampNow(); }
/** * This is the detective novel. We have three sources of information: * - current message state in the file * - current message state in the wiki * - cached message state since cache was last build * (usually after export from wiki) * * Now we must try to guess what in earth has driven the file state and * wiki state out of sync. Then we must compile list of events that would * bring those to sync. Types of events are addition, deletion, (content) * change and possible rename in the future. After that the list of events * are stored for later processing of a translation administrator, who can * decide what actions to take on those events to bring the state more or * less in sync. * * @param FileBasedMessageGroup $group * @param string $code Language code. * @param int $reason * @param MessageGroupCache $cache * @throws MWException */ protected function addMessageUpdateChanges(FileBasedMessageGroup $group, $code, $reason, $cache) { /* This throws a warning if message definitions are not yet * cached and will read the file for definitions. */ wfSuppressWarnings(); $wiki = $group->initCollection($code); wfRestoreWarnings(); $wiki->filter('hastranslation', false); $wiki->loadTranslations(); $wikiKeys = $wiki->getMessageKeys(); // By-pass cached message definitions /** @var FFS $ffs */ $ffs = $group->getFFS(); if ($code === $group->getSourceLanguage() && !$ffs->exists($code)) { $path = $group->getSourceFilePath($code); throw new MWException("Source message file for {$group->getId()} does not exist: {$path}"); } $file = $ffs->read($code); // Does not exist if ($file === false) { return; } // Something went wrong if (!isset($file['MESSAGES'])) { $id = $group->getId(); $ffsClass = get_class($ffs); error_log("{$id} has an FFS ({$ffsClass}) - it didn't return cake for {$code}"); return; } $fileKeys = array_keys($file['MESSAGES']); $common = array_intersect($fileKeys, $wikiKeys); $supportsFuzzy = $ffs->supportsFuzzy(); foreach ($common as $key) { $sourceContent = $file['MESSAGES'][$key]; /** @var TMessage $wikiMessage */ $wikiMessage = $wiki[$key]; $wikiContent = $wikiMessage->translation(); // If FFS doesn't support it, ignore fuzziness as difference $wikiContent = str_replace(TRANSLATE_FUZZY, '', $wikiContent); // But if it does, ensure we have exactly one fuzzy marker prefixed if ($supportsFuzzy === 'yes' && $wikiMessage->hasTag('fuzzy')) { $wikiContent = TRANSLATE_FUZZY . $wikiContent; } if (self::compareContent($sourceContent, $wikiContent)) { // File and wiki stage agree, nothing to do continue; } // Check against interim cache to see whether we have changes // in the wiki, in the file or both. if ($reason !== MessageGroupCache::NO_CACHE) { $cacheContent = $cache->get($key); /* We want to ignore the common situation that the string * in the wiki has been changed since the last export. * Hence we check that source === cache && cache !== wiki * and if so we skip this string. */ if (!self::compareContent($wikiContent, $cacheContent) && self::compareContent($sourceContent, $cacheContent)) { continue; } } $this->addChange('change', $code, $key, $sourceContent); } $added = array_diff($fileKeys, $wikiKeys); foreach ($added as $key) { $sourceContent = $file['MESSAGES'][$key]; if (trim($sourceContent) === '') { continue; } $this->addChange('addition', $code, $key, $sourceContent); } /* Should the cache not exist, don't consider the messages * missing from the file as deleted - they probably aren't * yet exported. For example new language translations are * exported the first time. */ if ($reason !== MessageGroupCache::NO_CACHE) { $deleted = array_diff($wikiKeys, $fileKeys); foreach ($deleted as $key) { if ($cache->get($key) === false) { /* This message has never existed in the cache, so it * must be a newly made in the wiki. */ continue; } $this->addChange('deletion', $code, $key, null); } } }
/** * @param string $key Message key * @param string $code Language code * @return string|null */ public function getMessage($key, $code) { $cache = new MessageGroupCache($this, $code); if ($cache->exists()) { $msg = $cache->get($key); if ($msg !== false) { return $msg; } // Try harder $nkey = str_replace(' ', '_', strtolower($key)); $keys = $cache->getKeys(); foreach ($keys as $k) { if ($nkey === str_replace(' ', '_', strtolower($k))) { return $cache->get($k); } } return null; } else { return null; } }
public function initCollection( $code ) { $messages = array(); foreach ( $this->getGroups() as $group ) { $cache = new MessageGroupCache( $group ); if ( $cache->exists() ) { foreach ( $cache->getKeys() as $key ) { $messages[$key] = $cache->get( $key ); } } else { // BC for MessageGroupOld $messages += $group->load( $this->getSourceLanguage() ); } } $namespace = $this->getNamespace(); $definitions = new MessageDefinitions( $messages, $namespace ); $collection = MessageCollection::newFromDefinitions( $definitions, $code ); $this->setTags( $collection ); return $collection; }
protected function processSubmit() { $req = $this->getRequest(); $out = $this->getOutput(); $jobs = array(); $jobs[] = MessageIndexRebuildJob::newJob(); $changefile = TranslateUtils::cacheFile(self::CHANGEFILE); $reader = CdbReader::open($changefile); $groups = unserialize($reader->get('#keys')); $postponed = array(); foreach ($groups as $groupId) { $group = MessageGroups::getGroup($groupId); $changes = unserialize($reader->get($groupId)); foreach ($changes as $code => $subchanges) { foreach ($subchanges as $type => $messages) { foreach ($messages as $index => $params) { $id = self::changeId($groupId, $code, $type, $params['key']); if ($req->getVal($id) === null) { // We probably hit the limit with number of post parameters. $postponed[$groupId][$code][$type][$index] = $params; continue; } if ($type === 'deletion' || $req->getCheck("i/{$id}")) { continue; } $fuzzy = $req->getCheck("f/{$id}") ? 'fuzzy' : false; $key = $params['key']; $title = Title::makeTitleSafe($group->getNamespace(), "{$key}/{$code}"); $jobs[] = MessageUpdateJob::newJob($title, $params['content'], $fuzzy); } } if (!isset($postponed[$groupId][$code])) { $cache = new MessageGroupCache($groupId, $code); $cache->create(); } } } JobQueueGroup::singleton()->push($jobs); $reader->close(); rename($changefile, $changefile . '-' . wfTimestamp()); if (count($postponed)) { $changefile = TranslateUtils::cacheFile(self::CHANGEFILE); $writer = CdbWriter::open($changefile); $keys = array_keys($postponed); $writer->set('#keys', serialize($keys)); foreach ($postponed as $groupId => $changes) { $writer->set($groupId, serialize($changes)); } $writer->close(); $this->showChanges(true, $this->getLimit()); } else { $out->addWikiMsg('translate-smg-submitted'); } }
/** * @param $group MessageGroup */ public function doModLangs($group) { global $wgLang; $languages = array_keys(Language::getLanguageNames(false)); $modified = $codes = array(); foreach ($languages as $code) { if ($code === 'en') { continue; } $cache = new MessageGroupCache($group, $code); if ($cache->isValid()) { continue; } $link = $this->skin->link($this->getTitle(), htmlspecialchars(TranslateUtils::getLanguageName($code, false, $wgLang->getCode()) . " ({$code})"), array(), array('group' => $group->getId(), 'language' => $code)); if (!$cache->exists()) { $modified[] = wfMsgHtml('translate-manage-modlang-new', $link); } else { $modified[] = $link; } $codes[] = $code; } if (count($modified)) { $this->out->addWikiMsg('translate-manage-modlangs', $wgLang->formatNum(count($modified))); if ($this->user->isAllowed('translate-manage')) { $this->out->addHTML($this->rebuildButton($group, $codes, 'import')); } $this->out->addHTML('<ul><li>' . implode("</li>\n<li>", $modified) . '</li></ul>'); } }