public function execute() { $params = $this->extractRequestParams(); $title = Title::newFromText($params['title']); if (!$title) { $this->dieUsage('Invalid title', 'invalidtitle'); } $handle = new MessageHandle($title); if (!$handle->isValid()) { $this->dieUsage('Title does not correspond to a translatable message', 'nomessagefortitle'); } if (strval($params['group']) !== '') { $group = MessageGroups::getGroup($params['group']); } else { $group = $handle->getGroup(); } if (!$group) { $this->dieUsage('Invalid group', 'invalidgroup'); } $data = array(); $times = array(); $props = $params['prop']; $aggregator = new QueryAggregator(); // Figure out the intersection of supported and requested aids $types = $group->getTranslationAids(); $props = array_intersect($props, array_keys($types)); $result = $this->getResult(); // Create list of aids, populate web services queries $aids = array(); foreach ($props as $type) { $class = $types[$type]; $obj = new $class($group, $handle, $this); if ($obj instanceof QueryAggregatorAware) { $obj->setQueryAggregator($aggregator); $obj->populateQueries(); } $aids[$type] = $obj; } // Execute all web service queries asynchronously to save time $start = microtime(true); $aggregator->run(); $times['query_aggregator'] = round(microtime(true) - $start, 3); // Construct the result data structure foreach ($aids as $type => $obj) { $start = microtime(true); try { $aid = $obj->getData(); } catch (TranslationHelperException $e) { $aid = array('error' => $e->getMessage()); } if (isset($aid['**'])) { $result->setIndexedTagName($aid, $aid['**']); unset($aid['**']); } $data[$type] = $aid; $times[$type] = round(microtime(true) - $start, 3); } $result->addValue(null, 'helpers', $data); $result->addValue(null, 'times', $times); }
/** * Shovels the new translation into translation memory. * Hook: Translate:newTranslation * * @param $handle MessageHandle * @param $revision * @param $text string * @param $user User * * @return bool */ public static function update( MessageHandle $handle, $revision, $text, User $user ) { global $wgContLang; $dbw = self::getDatabaseHandle(); // Not in use or misconfigured if ( $dbw === null ) { return true; } // Skip definitions to not slow down mass imports etc. // These will be added when first translation is made if ( $handle->getCode() === 'en' ) { return true; } $group = $handle->getGroup(); $key = $handle->getKey(); $code = $handle->getCode(); $ns_text = $wgContLang->getNsText( $group->getNamespace() ); $definition = $group->getMessage( $key, 'en' ); if ( !is_string( $definition ) || !strlen( $definition ) ) { wfDebugLog( 'tmserver', "Unable to get definition for $ns_text:$key/$code" ); return true; } $tmDefinition = array( 'text' => $definition, 'context' => "$ns_text:$key", 'length' => strlen( $definition ), 'lang' => 'en' ); // Check that the definition exists, add it if not $source_id = $dbw->selectField( '`sources`', 'sid', $tmDefinition, __METHOD__ ); if ( $source_id === false ) { $dbw->insert( '`sources`', $tmDefinition, __METHOD__ ); $source_id = $dbw->insertId(); wfDebugLog( 'tmserver', "Inserted new tm-definition for $ns_text:$key:\n$definition\n----------" ); } $delete = array( 'sid' => $source_id, 'lang' => $code, ); $insert = $delete + array( 'text' => $text, 'time' => wfTimestamp(), ); // Purge old translations for this message $dbw->delete( '`targets`', $delete, __METHOD__ ); // We only do SQlite which does not need to know unique indexes $dbw->replace( '`targets`', null, $insert, __METHOD__ ); wfDebugLog( 'tmserver', "Inserted new tm-translation for $ns_text:$key/$code" ); return true; }
/** * @since 2012-01-04 * @return array */ public static function getGroupIds(MessageHandle $handle) { $namespace = $handle->getTitle()->getNamespace(); $key = $handle->getKey(); $normkey = strtr(strtolower("{$namespace}:{$key}"), " ", "_"); $index = self::singleton()->retrieve(); if (isset($index[$normkey])) { return (array) $index[$normkey]; } else { return array(); } }
/** * Retrieves a list of groups given MessageHandle belongs to. * @since 2012-01-04 * @param MessageHandle $handle * @return array */ public static function getGroupIds(MessageHandle $handle) { $namespace = $handle->getTitle()->getNamespace(); $key = $handle->getKey(); $normkey = TranslateUtils::normaliseKey($namespace, $key); $value = self::singleton()->get($normkey); if ($value !== null) { return (array) $value; } else { return array(); } }
public function execute() { global $wgUser; if ( !$wgUser->isallowed( self::$right ) ) { $this->dieUsage( 'Permission denied', 'permissiondenied' ); } $params = $this->extractRequestParams(); $revision = Revision::newFromId( $params['revision'] ); if ( !$revision ) { $this->dieUsage( 'Invalid revision', 'invalidrevision' ); } $title = $revision->getTitle(); $handle = new MessageHandle( $title ); if ( !$handle->isValid() ) { $this->dieUsage( 'Unknown message', 'unknownmessage' ); } if ( $handle->isFuzzy() ) { $this->dieUsage( 'Cannot review fuzzy translations', 'fuzzymessage' ); } if ( $revision->getUser() == $wgUser->getId() ) { $this->dieUsage( 'Cannot review own translations', 'owntranslation' ); } $dbw = wfGetDB( DB_MASTER ); $table = 'translate_reviews'; $row = array( 'trr_user' => $wgUser->getId(), 'trr_page' => $revision->getPage(), 'trr_revision' => $revision->getId(), ); $options = array( 'IGNORE' ); $res = $dbw->insert( $table, $row, __METHOD__, $options ); if ( !$dbw->affectedRows() ) { $this->setWarning( 'Already marked as reviewed by you' ); } else { $logger = new LogPage( 'translationreview' ); $params = array( $revision->getId() ); $logger->addEntry( 'message', $title, null, $params, $wgUser ); } $output = array( 'review' => array( 'title' => $title->getPrefixedText(), 'pageid' => $revision->getPage(), 'revision' => $revision->getId() ) ); $this->getResult()->addValue( null, $this->getModuleName(), $output ); }
/** * Adds link in toolbox to Special:Prefixindex to show all other * available translations for a message. Only shown when it * actually is a translatable/translated message. * * @param $quickTemplate QuickTemplate * * @return bool */ static function toolboxAllTranslations(&$quickTemplate) { $title = $quickTemplate->getSkin()->getTitle(); $handle = new MessageHandle($title); if ($handle->isValid()) { $message = $title->getNsText() . ':' . $handle->getKey(); $desc = wfMessage('translate-sidebar-alltrans')->escaped(); $url = htmlspecialchars(SpecialPage::getTitleFor('Translations')->getLocalURL(array('message' => $message))); // Add the actual toolbox entry. // Add newlines and tabs for nicer HTML output. echo "\n\t\t\t\t<li id=\"t-alltrans\"><a href=\"{$url}\">{$desc}</a></li>\n"; } return true; }
public static function getGroupsWithTransitions(MessageHandle $handle) { $listeners = array(); foreach ($handle->getGroupIds() as $id) { $group = MessageGroups::getGroup($id); // No longer exists? if (!$group) { continue; } $conds = $group->getMessageGroupStates()->getConditions(); if ($conds) { $listeners[$id] = $conds; } } return $listeners; }
protected function formatTranslation(StashedTranslation $translation) { $title = $translation->getTitle(); $handle = new MessageHandle($title); // Prepare for the worst $definition = ''; $comparison = ''; if ($handle->isValid()) { $groupId = MessageIndex::getPrimaryGroupId($handle); $group = MessageGroups::getGroup($groupId); $key = $handle->getKey(); $definition = $group->getMessage($key, $group->getSourceLanguage()); $comparison = $group->getMessage($key, $handle->getCode()); } return array('title' => $title->getPrefixedText(), 'definition' => $definition, 'translation' => $translation->getValue(), 'comparison' => $comparison, 'metadata' => $translation->getMetadata()); }
public function execute() { $params = $this->extractRequestParams(); $title = Title::newFromText($params['title']); if (!$title) { $this->dieUsage('Invalid title'); } $handle = new MessageHandle($title); if (!$handle->isValid()) { $this->dieUsage('Title does not correspond to a translatable message'); } $base = Title::makeTitle($title->getNamespace(), $handle->getKey()); $namespace = $base->getNamespace(); $message = $base->getDBKey(); $dbr = wfGetDB(DB_SLAVE); $res = $dbr->select('page', array('page_namespace', 'page_title'), array('page_namespace' => $namespace, 'page_title ' . $dbr->buildLike("{$message}/", $dbr->anyString())), __METHOD__, array('ORDER BY' => 'page_title', 'USE INDEX' => 'name_title')); $titles = array(); foreach ($res as $row) { $titles[] = $row->page_title; } $pageInfo = TranslateUtils::getContents($titles, $namespace); $result = $this->getResult(); $pages = array(); $count = 0; foreach ($pageInfo as $key => $info) { if (++$count <= $params['offset']) { continue; } $tTitle = Title::makeTitle($namespace, $key); $tHandle = new MessageHandle($tTitle); $data = array('title' => $tTitle->getPrefixedText(), 'language' => $tHandle->getCode(), 'lasttranslator' => $info[1]); $fuzzy = MessageHandle::hasFuzzyString($info[0]) || $tHandle->isFuzzy(); if ($fuzzy) { $data['fuzzy'] = 'fuzzy'; } $translation = str_replace(TRANSLATE_FUZZY, '', $info[0]); $result->setContent($data, $translation); $fit = $result->addValue(array('query', $this->getModuleName()), null, $data); if (!$fit) { $this->setContinueEnumParameter('offset', $count); break; } } $result->setIndexedTagName_internal(array('query', $this->getModuleName()), 'message'); }
public function execute() { $params = $this->extractRequestParams(); $title = Title::newFromText($params['title']); if (!$title) { $this->dieUsage('Invalid title', 'invalidtitle'); } $handle = new MessageHandle($title); if (!$handle->isValid()) { $this->dieUsage('Title does not correspond to a translatable message', 'nomessagefortitle'); } $namespace = $title->getNamespace(); $pageInfo = self::getTranslations($handle); $result = $this->getResult(); $count = 0; foreach ($pageInfo as $key => $info) { if (++$count <= $params['offset']) { continue; } $tTitle = Title::makeTitle($namespace, $key); $tHandle = new MessageHandle($tTitle); $data = array('title' => $tTitle->getPrefixedText(), 'language' => $tHandle->getCode(), 'lasttranslator' => $info[1]); $fuzzy = MessageHandle::hasFuzzyString($info[0]) || $tHandle->isFuzzy(); if ($fuzzy) { $data['fuzzy'] = 'fuzzy'; } $translation = str_replace(TRANSLATE_FUZZY, '', $info[0]); if (defined('ApiResult::META_CONTENT')) { ApiResult::setContentValue($data, 'translation', $translation); } else { ApiResult::setContent($data, $translation); } $fit = $result->addValue(array('query', $this->getModuleName()), null, $data); if (!$fit) { $this->setContinueEnumParameter('offset', $count); break; } } if (defined('ApiResult::META_CONTENT')) { $result->addIndexedTagName(array('query', $this->getModuleName()), 'message'); } else { $result->setIndexedTagName_internal(array('query', $this->getModuleName()), 'message'); } }
public function getData() { $translation = null; $title = $this->handle->getTitle(); $translation = TranslateUtils::getMessageContent($this->handle->getKey(), $this->handle->getCode(), $title->getNamespace()); Hooks::run('TranslatePrefillTranslation', array(&$translation, $this->handle)); $fuzzy = MessageHandle::hasFuzzyString($translation) || $this->handle->isFuzzy(); $translation = str_replace(TRANSLATE_FUZZY, '', $translation); return array('language' => $this->handle->getCode(), 'fuzzy' => $fuzzy, 'value' => $translation); }
public function testParsing() { $title = Title::newFromText('MediaWiki:Ugakey/nl'); $page = WikiPage::factory($title); $content = ContentHandler::makeContent('$1 van $2', $title); $status = $page->doEditContent($content, __METHOD__); $value = $status->getValue(); /** * @var Revision $rev */ $rev = $value['revision']; $revision = $rev->getId(); $dbw = wfGetDB(DB_MASTER); $conds = array('rt_page' => $title->getArticleID(), 'rt_type' => RevTag::getType('fuzzy'), 'rt_revision' => $revision); $index = array_keys($conds); $dbw->replace('revtag', array($index), $conds, __METHOD__); $handle = new MessageHandle($title); $this->assertTrue($handle->isValid(), 'Message is known'); $this->assertTrue($handle->isFuzzy(), 'Message is fuzzy after database fuzzying'); // Update the translation without the fuzzy string $content = ContentHandler::makeContent('$1 van $2', $title); $page->doEditContent($content, __METHOD__); $this->assertFalse($handle->isFuzzy(), 'Message is unfuzzy after edit'); $content = ContentHandler::makeContent('!!FUZZY!!$1 van $2', $title); $page->doEditContent($content, __METHOD__); $this->assertTrue($handle->isFuzzy(), 'Message is fuzzy after manual fuzzying'); // Update the translation without the fuzzy string $content = ContentHandler::makeContent('$1 van $2', $title); $page->doEditContent($content, __METHOD__); $this->assertFalse($handle->isFuzzy(), 'Message is unfuzzy after edit'); }
function run() { global $wgTranslateDocumentationLanguageCode; $title = $this->title; $params = $this->params; $user = FuzzyBot::getUser(); $flags = EDIT_DEFER_UPDATES | EDIT_FORCE_BOT; $wikiPage = WikiPage::factory($title); $summary = wfMessage('translate-manage-import-summary')->inContentLanguage()->plain(); $content = ContentHandler::makeContent($params['content'], $title); $wikiPage->doEditContent($content, $summary, $flags, false, $user); // NOTE: message documentation is excluded from fuzzying! if ($params['fuzzy']) { $handle = new MessageHandle($title); $key = $handle->getKey(); $languages = TranslateUtils::getLanguageNames('en'); unset($languages[$wgTranslateDocumentationLanguageCode]); $languages = array_keys($languages); $dbw = wfGetDB(DB_MASTER); $fields = array('page_id', 'page_latest'); $conds = array('page_namespace' => $title->getNamespace()); $pages = array(); foreach ($languages as $code) { $otherTitle = Title::makeTitleSafe($title->getNamespace(), "{$key}/{$code}"); $pages[$otherTitle->getDBKey()] = true; } unset($pages[$title->getDBKey()]); if (count($pages) === 0) { return true; } $conds['page_title'] = array_keys($pages); $res = $dbw->select('page', $fields, $conds, __METHOD__); $inserts = array(); foreach ($res as $row) { $inserts[] = array('rt_type' => RevTag::getType('fuzzy'), 'rt_page' => $row->page_id, 'rt_revision' => $row->page_latest); } $dbw->replace('revtag', array(array('rt_type', 'rt_page', 'rt_revision')), $inserts, __METHOD__); } return true; }
public function update(MessageHandle $handle, $targetText) { global $wgContLang; if (!$handle->isValid() || $handle->getCode() === '') { return false; } $mkey = $handle->getKey(); $group = $handle->getGroup(); $targetLanguage = $handle->getCode(); $sourceLanguage = $group->getSourceLanguage(); $title = $handle->getTitle(); // Skip definitions to not slow down mass imports etc. // These will be added when the first translation is made if ($targetLanguage === $sourceLanguage) { return false; } $definition = $group->getMessage($mkey, $sourceLanguage); if (!is_string($definition) || !strlen(trim($definition))) { return false; } $dbw = $this->getDB(DB_MASTER); /* Check that the definition exists and fetch the sid. If not, add * the definition and retrieve the sid. If the definition changes, * we will create a new entry - otherwise we could at some point * get suggestions which do not match the original definition any * longer. The old translations are still kept until purged by * rerunning the bootstrap script. */ $conds = array('tms_context' => $title->getPrefixedText(), 'tms_text' => $definition); $sid = $dbw->selectField('translate_tms', 'tms_sid', $conds, __METHOD__); if ($sid === false) { $sid = $this->insertSource($title, $sourceLanguage, $definition); } // Delete old translations for this message if any. Could also use replace $deleteConds = array('tmt_sid' => $sid, 'tmt_lang' => $targetLanguage); $dbw->delete('translate_tmt', $deleteConds, __METHOD__); // Insert the new translation $row = $deleteConds + array('tmt_text' => $targetText); $dbw->insert('translate_tmt', $row, __METHOD__); return true; }
protected function mustBeTranslation() { if (!$this->handle->getCode()) { throw new TranslationHelperExpection('editing source language'); } }
public function batchInsertDefinitions(array $batch) { foreach ($batch as $key => $item) { list($title, $language, $text) = $item; $handle = new MessageHandle($title); $context = Title::makeTitle($handle->getTitle()->getNamespace(), $handle->getKey()); $this->sids[$key] = $this->insertSource($context, $language, $text); } wfWaitForSlaves(10); }
/** * Returns the message groups this message group is part of. * @since 2011-12-25 * @return array */ public static function getParentGroups(MessageGroup $group) { // Take the first message, get a handle for it and check // if that message belongs to other groups. Those are the // parent aggregate groups. Ideally we loop over all keys, // but this should be enough. $keys = array_keys($group->getDefinitions()); $title = Title::makeTitle($group->getNamespace(), $keys[0]); $handle = new MessageHandle($title); $ids = $handle->getGroupIds(); foreach ($ids as $index => $id) { if ($id === $group->getId()) { unset($ids[$index]); } } return $ids; }
/** * Get the translations in all languages. Cached for performance. * Fuzzy translation are not included. * * @return array Language code => Translation */ public function getTranslations() { static $cache = array(); $key = $this->handle->getTitle()->getPrefixedText(); if (array_key_exists($key, $cache)) { return $cache[$key]; } $data = ApiQueryMessageTranslations::getTranslations($this->handle); $namespace = $this->handle->getTitle()->getNamespace(); $cache[$key] = array(); foreach ($data as $page => $info) { $tTitle = Title::makeTitle($namespace, $page); $tHandle = new MessageHandle($tTitle); $fuzzy = MessageHandle::hasFuzzyString($info[0]) || $tHandle->isFuzzy(); if ($fuzzy) { continue; } $code = $tHandle->getCode(); $cache[$key][$code] = $info[0]; } return $cache[$key]; }
public static function update( MessageHandle $handle, $changes = array() ) { $dbw = wfGetDB( DB_MASTER ); $conds = array( 'tgs_group' => $handle->getGroupIds(), 'tgs_lang' => $handle->getCode(), ); $values = array(); foreach ( array( 'total', 'translated', 'fuzzy' ) as $type ) { if ( !isset( $changes[$type] ) ) { $values[] = "tgs_$type=tgs_$type" . self::stringifyNumber( $changes[$type] ); } } $dbw->update( self::TABLE, $values, $conds, __METHOD__ ); }
public function execute() { global $wgTranslateFuzzyBotName, $wgSitename; $days = (int) $this->getOption('days', 30); $hours = $days * 24; $top = (int) $this->getOption('top', -1); $bots = $this->hasOption('bots'); $namespaces = array(); if ($this->hasOption('ns')) { $input = explode(',', $this->getOption('ns')); foreach ($input as $namespace) { if (is_numeric($namespace)) { $namespaces[] = $namespace; } } } // Select set of edits to report on // Fetch some extrac fields that normally TranslateUtils::translationChanges wont $extraFields = array('rc_old_len', 'rc_new_len'); $rows = TranslateUtils::translationChanges($hours, $bots, $namespaces, $extraFields); // Get counts for edits per language code after filtering out edits by FuzzyBot $codes = array(); foreach ($rows as $_) { // Filter out edits by $wgTranslateFuzzyBotName if ($_->rc_user_text === $wgTranslateFuzzyBotName) { continue; } $handle = new MessageHandle(Title::newFromText($_->rc_title)); $code = $handle->getCode(); if (!isset($codes[$code])) { $codes[$code] = 0; } if ($this->hasOption('diff')) { $diff = abs($_->rc_new_len - $_->rc_old_len); } else { $diff = $_->rc_new_len; } $codes[$code] += $diff; } // Sort counts and report descending up to $top rows. arsort($codes); $i = 0; $total = 0; $this->output("Character edit stats for last {$days} days in {$wgSitename}\n"); $this->output("code\tname\tedit\n"); $this->output("-----------------------\n"); foreach ($codes as $code => $num) { if ($i++ === $top) { break; } $language = Language::fetchLanguageName($code); if (!$language) { // this will be very rare, but avoid division by zero in next line continue; } $charRatio = mb_strlen($language, 'UTF-8') / strlen($language); $num = intval($num * $charRatio); $total += $num; $this->output("{$code}\t{$language}\t{$num}\n"); } $this->output("-----------------------\n"); $this->output("Total\t\t{$total}\n"); }
/** * @param Title $title * @return bool|TranslatablePage */ public static function isTranslationPage(Title $title) { $handle = new MessageHandle($title); $key = $handle->getKey(); $code = $handle->getCode(); if ($key === '' || $code === '') { return false; } $codes = Language::fetchLanguageNames(); global $wgTranslateDocumentationLanguageCode; unset($codes[$wgTranslateDocumentationLanguageCode]); if (!isset($codes[$code])) { return false; } $newtitle = self::changeTitleText($title, $key); if (!$newtitle) { return false; } $page = TranslatablePage::newFromTitle($newtitle); if ($page->getMarkedTag() === false) { return false; } return $page; }
/** * @param MessageGroup $group * @param string $code * @param string $type * @param array $params * @param int $limit * @return string HTML */ protected function formatChange(MessageGroup $group, $code, $type, $params, &$limit) { $key = $params['key']; $title = Title::makeTitleSafe($group->getNamespace(), "{$key}/{$code}"); $id = self::changeId($group->getId(), $code, $type, $key); if ($title && $title->exists() && $type === 'addition') { // The message has for some reason dropped out from cache // or perhaps it is being reused. In any case treat it // as a change for display, so the admin can see if // action is needed and let the message be processed. // Otherwise it will end up in the postponed category // forever and will prevent rebuilding the cache, which // leads to many other annoying problems. $type = 'change'; } elseif ($title && !$title->exists() && ($type === 'deletion' || $type === 'change')) { return ''; } $text = ''; if ($type === 'deletion') { $wiki = ContentHandler::getContentText(Revision::newFromTitle($title)->getContent()); $oldContent = ContentHandler::makeContent($wiki, $title); $newContent = ContentHandler::makeContent('', $title); $this->diff->setContent($oldContent, $newContent); $text = $this->diff->getDiff(Linker::link($title), ''); } elseif ($type === 'addition') { $oldContent = ContentHandler::makeContent('', $title); $newContent = ContentHandler::makeContent($params['content'], $title); $this->diff->setContent($oldContent, $newContent); $text = $this->diff->getDiff('', Linker::link($title)); } elseif ($type === 'change') { $wiki = ContentHandler::getContentText(Revision::newFromTitle($title)->getContent()); $handle = new MessageHandle($title); if ($handle->isFuzzy()) { $wiki = '!!FUZZY!!' . str_replace(TRANSLATE_FUZZY, '', $wiki); } $label = $this->msg('translate-manage-action-ignore')->text(); $actions = Xml::checkLabel($label, "i/{$id}", "i/{$id}"); $limit--; if ($group->getSourceLanguage() === $code) { $label = $this->msg('translate-manage-action-fuzzy')->text(); $actions .= ' ' . Xml::checkLabel($label, "f/{$id}", "f/{$id}", true); $limit--; } $oldContent = ContentHandler::makeContent($wiki, $title); $newContent = ContentHandler::makeContent($params['content'], $title); $this->diff->setContent($oldContent, $newContent); $text .= $this->diff->getDiff(Linker::link($title), $actions); } $hidden = Html::hidden($id, 1); $limit--; $text .= $hidden; $classes = "mw-translate-smg-change smg-change-{$type}"; if ($limit < 0) { // Don't add if one of the fields might get dropped of at submission return ''; } return Html::rawElement('div', array('class' => $classes), $text); }
/** * @param MessageHandle $handle * @return TTMServerMessageUpdateJob */ public static function newJob(MessageHandle $handle) { $job = new self($handle->getTitle()); return $job; }
/** * @see schema.xml */ protected function createDocument(MessageHandle $handle, $text, $revId) { $language = $handle->getCode(); $translationTitle = $handle->getTitle(); $title = Title::makeTitle($handle->getTitle()->getNamespace(), $handle->getKey()); $wiki = wfWikiId(); $messageid = $title->getPrefixedText(); $globalid = "{$wiki}-{$messageid}-{$revId}/{$language}"; $doc = new Solarium_Document_ReadWrite(); $doc->wiki = $wiki; $doc->uri = $translationTitle->getCanonicalUrl(); $doc->messageid = $messageid; $doc->globalid = $globalid; $doc->language = $language; $doc->content = $text; $doc->setField('group', $handle->getGroupIds()); return $doc; }
public function execute() { $target = $this->getOption('target'); if (!is_writable($target)) { $this->error("Target directory is not writable ({$target}).", 1); } $threshold = $this->getOption('threshold'); $noFuzzy = $this->hasOption('no-fuzzy'); $noLocation = ''; if ($this->hasOption('no-location')) { $noLocation = '--no-location '; } $skip = array(); if ($this->hasOption('skip')) { $skip = array_map('trim', explode(',', $this->getOption('skip'))); } $reqLangs = TranslateUtils::parseLanguageCodes($this->getOption('lang')); $reqLangs = array_flip($reqLangs); foreach ($skip as $skipLang) { unset($reqLangs[$skipLang]); } $reqLangs = array_flip($reqLangs); $codemapOnly = $this->hasOption('codemaponly'); $groupIds = explode(',', trim($this->getOption('group'))); $groupIds = MessageGroups::expandWildcards($groupIds); $groups = MessageGroups::getGroupsById($groupIds); /** @var FileBasedMessageGroup $group */ foreach ($groups as $groupId => $group) { if ($group->isMeta()) { $this->output("Skipping meta message group {$groupId}.\n"); unset($groups[$groupId]); continue; } if (!$group instanceof FileBasedMessageGroup) { $this->output("EE2: Unexportable message group {$groupId}.\n"); unset($groups[$groupId]); continue; } } if (!count($groups)) { $this->error("EE1: No valid message groups identified.", 1); } $changeFilter = false; $hours = $this->getOption('hours'); if ($hours) { $namespaces = array(); /** @var FileBasedMessageGroup $group */ foreach ($groups as $group) { $namespaces[$group->getNamespace()] = true; } $namespaces = array_keys($namespaces); $bots = true; $changeFilter = array(); $rows = TranslateUtils::translationChanges($hours, $bots, $namespaces); foreach ($rows as $row) { $title = Title::makeTitle($row->rc_namespace, $row->rc_title); $handle = new MessageHandle($title); $code = $handle->getCode(); if (!$code) { continue; } $groupIds = $handle->getGroupIds(); foreach ($groupIds as $groupId) { $changeFilter[$groupId][$code] = true; } } } $skipGroups = array(); if ($this->hasOption('skipgroup')) { $skipGroups = array_map('trim', explode(',', $this->getOption('skipgroup'))); } foreach ($groups as $groupId => $group) { if (in_array($groupId, $skipGroups)) { $this->output("Group {$groupId} is in skipgroup.\n"); continue; } // No changes to this group at all if (is_array($changeFilter) && !isset($changeFilter[$groupId])) { $this->output("No recent changes to {$groupId}.\n"); continue; } $langs = $reqLangs; if ($codemapOnly) { foreach ($langs as $index => $code) { if ($group->mapCode($code) === $code) { unset($langs[$index]); } } } if ($threshold) { $stats = MessageGroupStats::forGroup($groupId); foreach ($langs as $index => $code) { if (!isset($stats[$code])) { unset($langs[$index]); continue; } $total = $stats[$code][MessageGroupStats::TOTAL]; $translated = $stats[$code][MessageGroupStats::TRANSLATED]; if ($translated / $total * 100 < $threshold) { unset($langs[$index]); } } } // Filter out unchanged languages from requested languages if (is_array($changeFilter)) { $langs = array_intersect($langs, array_keys($changeFilter[$groupId])); } if (!count($langs)) { continue; } $this->output("Exporting {$groupId}...\n"); $ffs = $group->getFFS(); $ffs->setWritePath($target); $sourceLanguage = $group->getSourceLanguage(); $collection = $group->initCollection($sourceLanguage); $definitionFile = false; if ($this->hasOption('ppgettext') && $ffs instanceof GettextFFS) { global $wgMaxShellMemory, $wgTranslateGroupRoot; // Need more shell memory for msgmerge. $wgMaxShellMemory = 402400; $path = $group->getSourceFilePath($sourceLanguage); $definitionFile = str_replace($wgTranslateGroupRoot, $this->getOption('ppgettext'), $path); } $whitelist = $group->getTranslatableLanguages(); foreach ($langs as $lang) { // Do not export languges that are blacklisted (or not whitelisted). // Also check that whitelist is not null, which means that all // languages are allowed for translation and export. if (is_array($whitelist) && !isset($whitelist[$lang])) { continue; } $collection->resetForNewLanguage($lang); $collection->loadTranslations(); // Don't export ignored, unless it is the source language // or message documentation global $wgTranslateDocumentationLanguageCode; if ($lang !== $wgTranslateDocumentationLanguageCode && $lang !== $sourceLanguage) { $collection->filter('ignored'); } if ($noFuzzy) { $collection->filter('fuzzy'); } $ffs->write($collection); // Do post processing if requested. if ($definitionFile) { if (is_file($definitionFile)) { $targetFileName = $ffs->getWritePath() . "/" . $group->getTargetFilename($collection->code); $cmd = "msgmerge --quiet " . $noLocation . "--output-file=" . $targetFileName . ' ' . $targetFileName . ' ' . $definitionFile; wfShellExec($cmd, $ret); // Report on errors. if ($ret) { $this->error("ERROR: {$ret}"); } } else { $this->error("{$definitionFile} does not exist.", 1); } } } } }
/** * Hook: ArticleContentOnDiff */ public static function displayOnDiff(DifferenceEngine $de, OutputPage $out) { $title = $de->getTitle(); $handle = new MessageHandle($title); if (!$handle->isValid()) { return true; } $th = new TranslationHelpers($title, false); $th->setEditMode(false); $de->loadNewText(); if ($de->mNewContent instanceof TextContent) { $th->setTranslation($de->mNewContent->getNativeData()); } else { // Screw you, not interested. return true; } TranslationHelpers::addModules($out); $boxes = array(); $boxes[] = $th->callBox('documentation', array($th, 'getDocumentationBox')); $boxes[] = $th->callBox('definition', array($th, 'getDefinitionBox')); $boxes[] = $th->callBox('translation', array($th, 'getTranslationDisplayBox')); $output = implode("\n", $boxes); $output = Html::rawElement('div', array('class' => 'mw-sp-translate-edit-fields'), $output); $out->addHtml($output); return true; }
protected function extractMessages($resultset, $offset, $limit) { $messages = $documents = $ret = array(); $language = $this->params['language']; foreach ($resultset->getResults() as $document) { $data = $document->getData(); if (!$this->server->isLocalSuggestion($data)) { continue; } $title = Title::newFromText($data['localid']); if (!$title) { continue; } $handle = new MessageHandle($title); if (!$handle->isValid()) { continue; } $key = $title->getNamespace() . ':' . $title->getDBKey(); $messages[$key] = $data['content']; } $definitions = new MessageDefinitions($messages); $collection = MessageCollection::newFromDefinitions($definitions, $language); $filter = $this->params['filter']; if ($filter === 'untranslated') { $collection->filter('hastranslation', true); } elseif (in_array($filter, $this->getAvailableFilters())) { $collection->filter($filter, false); } $total = count($collection); $offset = $collection->slice($offset, $limit); $left = count($collection); $offsets = array('start' => $offset[2], 'left' => $left, 'total' => $total); if ($filter === 'translated' || $filter === 'fuzzy') { $collection->loadTranslations(); } foreach ($collection->keys() as $mkey => $title) { $documents[$mkey]['content'] = $messages[$mkey]; if ($filter === 'translated' || $filter === 'fuzzy') { $documents[$mkey]['content'] = $collection[$mkey]->translation(); } $handle = new MessageHandle($title); $documents[$mkey]['localid'] = $handle->getTitleForBase()->getPrefixedText(); $documents[$mkey]['language'] = $language; $ret[] = $documents[$mkey]; } return array($ret, $offsets); }
/** * Hook to update source and destination translation pages on moving translation units * Hook: TitleMoveComplete * @since 2014.08 */ public static function onMoveTranslationUnits(Title &$ot, Title &$nt, User &$user, $oldid, $newid, $reason) { // Do the update only once. In case running by job queue, the update is not done here if (self::$jobQueueRunning) { return; } $groupLast = null; foreach (array($ot, $nt) as $title) { $handle = new MessageHandle($title); if (!$handle->isValid()) { continue; } // Documentation pages are never translation pages if ($handle->isDoc()) { continue; } $group = $handle->getGroup(); if (!$group instanceof WikiPageMessageGroup) { continue; } $language = $handle->getCode(); // Ignore pages such as Translations:Page/unit without language code if (strval($code) === '') { continue; } // Update the page only once if source and destination units // belong to the same page if ($group !== $groupLast) { $groupLast = $group; $page = TranslatablePage::newFromTitle($group->getTitle()); self::updateTranslationPage($page, $language, $user, 0, $reason); } } }
/** * Hook: ArticleContentOnDiff */ public static function displayOnDiff( DifferenceEngine $de, OutputPage $out ) { $title = $de->getTitle(); $handle = new MessageHandle( $title ); if ( !$handle->isValid() ) { return true; } $de->loadNewText(); $out->setRevisionId( $de->mNewRev->getId() ); $th = new TranslationHelpers( $title, /*group*/false ); $th->setEditMode( false ); $th->setTranslation( $de->mNewtext ); TranslationHelpers::addModules( $out ); $boxes = array(); $boxes[] = $th->getDocumentationBox(); $boxes[] = $th->getDefinitionBox(); $boxes[] = $th->getTranslationDisplayBox(); $output = Html::rawElement( 'div', array( 'class' => 'mw-sp-translate-edit-fields' ), implode( "\n\n", $boxes ) ); $out->addHtml( $output ); return false; }
public function getDefinitions() { if (!$this->language) { throw new MWException("Language not set"); } $db = wfGetDB(DB_SLAVE); $tables = 'recentchanges'; $fields = array('rc_namespace', 'rc_title'); $conds = $this->getQueryConditions(); $options = array('ORDER BY' => 'rc_id DESC', 'LIMIT' => 5000); $res = $db->select($tables, $fields, $conds, __METHOD__, $options); $defs = array(); foreach ($res as $row) { $title = Title::makeTitle($row->rc_namespace, $row->rc_title); $handle = new MessageHandle($title); if (!$handle->isValid() || !$this->matchingMessage($handle)) { continue; } $messageKey = $handle->getKey(); $fullKey = $row->rc_namespace . ':' . $messageKey; /* Note: due to bugs, getMessage might return null even for * known messages. These negatives are not cached, but that * should be rare enough case to not affect performance. */ if (!isset($defs[$fullKey])) { $group = $handle->getGroup(); $msg = $group->getMessage($messageKey, $group->getSourceLanguage()); if ($msg !== null) { $defs[$fullKey] = $msg; } } } return $defs; }