/** * 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; }
public function getOtherLanguagesBox() { $code = $this->handle->getCode(); $page = $this->handle->getKey(); $ns = $this->handle->getTitle()->getNamespace(); $boxes = array(); foreach (self::getFallbacks($code) as $fbcode) { $text = TranslateUtils::getMessageContent($page, $fbcode, $ns); if ($text === null) { continue; } $context = RequestContext::getMain(); $label = TranslateUtils::getLanguageName($fbcode, $context->getLanguage()->getCode()) . $context->msg('word-separator')->text() . $context->msg('parentheses', wfBCP47($fbcode))->text(); $target = Title::makeTitleSafe($ns, "{$page}/{$fbcode}"); if ($target) { $label = self::ajaxEditLink($target, htmlspecialchars($label)); } $dialogID = $this->dialogID(); $id = Sanitizer::escapeId("other-{$fbcode}-{$dialogID}"); $params = array('class' => 'mw-translate-edit-item'); $display = TranslateUtils::convertWhiteSpaceToHTML($text); $display = Html::rawElement('div', array('lang' => $fbcode, 'dir' => Language::factory($fbcode)->getDir()), $display); $contents = self::legend($label) . "\n" . $this->adder($id, $fbcode) . $display . self::clear(); $boxes[] = Html::rawElement('div', $params, $contents) . $this->wrapInsert($id, $text); } if (count($boxes)) { $sep = Html::element('hr', array('class' => 'mw-translate-sep')); return TranslateUtils::fieldset(wfMessage('translate-edit-in-other-languages', $page)->escaped(), implode("{$sep}\n", $boxes), array('class' => 'mw-sp-translate-edit-inother')); } return null; }
public function update(MessageHandle $handle, $targetText) { if (!$handle->isValid() || $handle->getCode() === '') { return false; } $mkey = $handle->getKey(); $group = $handle->getGroup(); $targetLanguage = $handle->getCode(); $sourceLanguage = $group->getSourceLanguage(); // 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; } $context = Title::makeTitle($handle->getTitle()->getNamespace(), $mkey); $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' => $context->getPrefixedText(), 'tms_text' => $definition); $sid = $dbw->selectField('translate_tms', 'tms_sid', $conds, __METHOD__); if ($sid === false) { $sid = $this->insertSource($context, $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 if ($targetText !== null) { $row = $deleteConds + array('tmt_text' => $targetText); $dbw->insert('translate_tmt', $row, __METHOD__); } return true; }
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 run() { $title = $this->title; $handle = new MessageHandle($title); $code = $handle->getCode(); if (!$handle->isValid() && !$code) { return true; } $groups = self::getGroupsWithTransitions($handle); foreach ($groups as $id => $transitions) { $group = MessageGroups::getGroup($id); $stats = MessageGroupStats::forItem($id, $code); $state = self::getNewState($stats, $transitions); if ($state) { ApiGroupReview::changeState($group, $code, $state, FuzzyBot::getUser()); } } return true; }
protected function parseQueryString($queryString, array $opts) { $fields = $highlights = array(); $terms = preg_split('/\\s+/', $queryString); $match = $opts['match']; $case = $opts['case']; // Map each word in the query string with its corresponding field foreach ($terms as $term) { $prefix = strstr($term, '*', true); if ($prefix) { // For wildcard search $fields['content.prefix_complete'][] = $prefix; } elseif ($case === '1') { // For case sensitive search $fields['content.case_sensitive'][] = $term; } else { $fields['content'][] = $term; } } // Allow searching either by message content or message id (page name // without language subpage) with exact match only. $searchQuery = new \Elastica\Query\Bool(); foreach ($fields as $analyzer => $words) { foreach ($words as $word) { $boolQuery = new \Elastica\Query\Bool(); $contentQuery = new \Elastica\Query\Match(); $contentQuery->setFieldQuery($analyzer, $word); $boolQuery->addShould($contentQuery); $messageQuery = new \Elastica\Query\Term(); $messageQuery->setTerm('localid', $word); $boolQuery->addShould($messageQuery); if ($match === 'all') { $searchQuery->addMust($boolQuery); } else { $searchQuery->addShould($boolQuery); } // Fields for highlighting $highlights[$analyzer] = array('number_of_fragments' => 0); // Allow searching by exact message title (page name with // language subpage). $title = Title::newFromText($word); if (!$title) { continue; } $handle = new MessageHandle($title); if ($handle->isValid() && $handle->getCode() !== '') { $localid = $handle->getTitleForBase()->getPrefixedText(); $boolQuery = new \Elastica\Query\Bool(); $messageId = new \Elastica\Query\Term(); $messageId->setTerm('localid', $localid); $boolQuery->addMust($messageId); $searchQuery->addShould($boolQuery); } } } return array($searchQuery, $highlights); }
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"); }
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); } } } } }
protected function mustBeTranslation() { if (!$this->handle->getCode()) { throw new TranslationHelperExpection('editing source language'); } }
public function execute($par) { global $wgLanguageCode; $this->setHeaders(); $this->checkPermissions(); $server = TTMServer::primary(); if (!$server instanceof SearchableTTMServer) { throw new ErrorPageError('tux-sst-nosolr-title', 'tux-sst-nosolr-body'); } $out = $this->getOutput(); $out->addModuleStyles('jquery.uls.grid'); $out->addModuleStyles('ext.translate.special.searchtranslations.styles'); $out->addModuleStyles('ext.translate.special.translate.styles'); $out->addModules('ext.translate.special.searchtranslations'); $out->addModules('ext.translate.special.searchtranslations.operatorsuggest'); TranslateUtils::addSpecialHelpLink($out, 'Help:Extension:Translate#searching'); $this->opts = $opts = new FormOptions(); $opts->add('query', ''); $opts->add('sourcelanguage', $wgLanguageCode); $opts->add('language', ''); $opts->add('group', ''); $opts->add('grouppath', ''); $opts->add('filter', ''); $opts->add('match', ''); $opts->add('case', ''); $opts->add('limit', $this->limit); $opts->add('offset', 0); $opts->fetchValuesFromRequest($this->getRequest()); $queryString = $opts->getValue('query'); if ($queryString === '') { $this->showEmptySearch(); return; } $params = $opts->getAllValues(); $filter = $opts->getValue('filter'); try { $translationSearch = new CrossLanguageTranslationSearchQuery($params, $server); if (in_array($filter, $translationSearch->getAvailableFilters())) { if ($opts->getValue('language') === '') { $params['language'] = $this->getLanguage()->getCode(); $opts->setValue('language', $params['language']); } $documents = $translationSearch->getDocuments(); $total = $translationSearch->getTotalHits(); $resultset = $translationSearch->getResultSet(); } else { $resultset = $server->search($queryString, $params, $this->hl); $documents = $server->getDocuments($resultset); $total = $server->getTotalHits($resultset); } } catch (TTMServerException $e) { error_log('Translation search server unavailable:' . $e->getMessage()); throw new ErrorPageError('tux-sst-solr-offline-title', 'tux-sst-solr-offline-body'); } // Part 1: facets $facets = $server->getFacets($resultset); $facetHtml = ''; if (count($facets['language']) > 0) { if ($filter !== '') { $facets['language'] = array_merge($facets['language'], array($opts->getValue('language') => $total)); } $facetHtml = Html::element('div', array('class' => 'row facet languages', 'data-facets' => FormatJson::encode($this->getLanguages($facets['language'])), 'data-language' => $opts->getValue('language')), $this->msg('tux-sst-facet-language')); } if (count($facets['group']) > 0) { $facetHtml .= Html::element('div', array('class' => 'row facet groups', 'data-facets' => FormatJson::encode($this->getGroups($facets['group'])), 'data-group' => $opts->getValue('group')), $this->msg('tux-sst-facet-group')); } // Part 2: results $resultsHtml = ''; $title = Title::newFromText($queryString); if ($title && !in_array($filter, $translationSearch->getAvailableFilters())) { $handle = new MessageHandle($title); $code = $handle->getCode(); $language = $opts->getValue('language'); if ($handle->isValid() && $code !== '' && $code !== $language) { $groupId = $handle->getGroup()->getId(); $helpers = new TranslationHelpers($title, $groupId); $document['wiki'] = wfWikiId(); $document['localid'] = $handle->getTitleForBase()->getPrefixedText(); $document['content'] = $helpers->getTranslation(); $document['language'] = $handle->getCode(); array_unshift($documents, $document); $total++; } } foreach ($documents as $document) { $text = $document['content']; $text = TranslateUtils::convertWhiteSpaceToHTML($text); list($pre, $post) = $this->hl; $text = str_replace($pre, '<strong class="tux-search-highlight">', $text); $text = str_replace($post, '</strong>', $text); $title = Title::newFromText($document['localid'] . '/' . $document['language']); if (!$title) { // Should not ever happen but who knows... continue; } $resultAttribs = array('class' => 'row tux-message', 'data-title' => $title->getPrefixedText(), 'data-language' => $document['language']); $handle = new MessageHandle($title); if ($handle->isValid()) { $groupId = $handle->getGroup()->getId(); $helpers = new TranslationHelpers($title, $groupId); $resultAttribs['data-definition'] = $helpers->getDefinition(); $resultAttribs['data-translation'] = $helpers->getTranslation(); $resultAttribs['data-group'] = $groupId; $uri = $title->getLocalUrl(array('action' => 'edit')); $link = Html::element('a', array('href' => $uri), $this->msg('tux-sst-edit')->text()); } else { $url = wfParseUrl($document['uri']); $domain = $url['host']; $link = Html::element('a', array('href' => $document['uri']), $this->msg('tux-sst-view-foreign', $domain)->text()); } $access = Html::rawElement('div', array('class' => 'row tux-edit tux-message-item'), $link); $titleText = $title->getPrefixedText(); $titleAttribs = array('class' => 'row tux-title', 'dir' => 'ltr'); $textAttribs = array('class' => 'row tux-text', 'lang' => wfBCP47($document['language']), 'dir' => Language::factory($document['language'])->getDir()); $resultsHtml = $resultsHtml . Html::openElement('div', $resultAttribs) . Html::rawElement('div', $textAttribs, $text) . Html::element('div', $titleAttribs, $titleText) . $access . Html::closeElement('div'); } $resultsHtml .= Html::rawElement('hr', array('class' => 'tux-pagination-line')); $prev = $next = ''; $offset = $this->opts->getValue('offset'); $params = $this->opts->getChangedValues(); if ($total - $offset > $this->limit) { $newParams = array('offset' => $offset + $this->limit) + $params; $attribs = array('class' => 'mw-ui-button pager-next', 'href' => $this->getPageTitle()->getLocalUrl($newParams)); $next = Html::element('a', $attribs, $this->msg('tux-sst-next')->text()); } if ($offset) { $newParams = array('offset' => max(0, $offset - $this->limit)) + $params; $attribs = array('class' => 'mw-ui-button pager-prev', 'href' => $this->getPageTitle()->getLocalUrl($newParams)); $prev = Html::element('a', $attribs, $this->msg('tux-sst-prev')->text()); } $resultsHtml .= Html::rawElement('div', array('class' => 'tux-pagination-links'), "{$prev} {$next}"); $search = $this->getSearchInput($queryString); $count = $this->msg('tux-sst-count')->numParams($total); $this->showSearch($search, $count, $facetHtml, $resultsHtml, $total); }
/** * 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]; }
/** * Filters out messages that should not be translated under normal * conditions. * * @param MessageHandle $handle Handle for the translation target. * @return boolean * @since 2013.10 */ public static function isTranslatableMessage(MessageHandle $handle) { static $cache = array(); if (!$handle->isValid()) { return false; } $group = $handle->getGroup(); $groupId = $group->getId(); $language = $handle->getCode(); $cacheKey = "{$groupId}:{$language}"; if (!isset($cache[$cacheKey])) { $allowed = true; $discouraged = false; $whitelist = $group->getTranslatableLanguages(); if (is_array($whitelist) && !isset($whitelist[$language])) { $allowed = false; } if (self::getPriority($group) === 'discouraged') { $discouraged = true; } else { $priorityLanguages = TranslateMetadata::get($groupId, 'prioritylangs'); if ($priorityLanguages) { $map = array_flip(explode(',', $priorityLanguages)); if (!isset($map[$language])) { $discouraged = true; } } } $cache[$cacheKey] = array('relevant' => $allowed && !$discouraged, 'tags' => array()); $groupTags = $group->getTags(); foreach (array('ignored', 'optional') as $tag) { if (isset($groupTags[$tag])) { foreach ($groupTags[$tag] as $key) { // TODO: ucfirst should not be here $cache[$cacheKey]['tags'][ucfirst($key)] = true; } } } } return $cache[$cacheKey]['relevant'] && !isset($cache[$cacheKey]['tags'][ucfirst($handle->getKey())]); }
/** * 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); } } }
/** * @param MessageHandle $handle * @param string $text * @return bool */ protected static function checkNeedsFuzzy(MessageHandle $handle, $text) { // Check for explicit tag. $fuzzy = MessageHandle::hasFuzzyString($text); // Docs are exempt for checks if ($handle->isDoc()) { return $fuzzy; } // Not all groups have checkers $group = $handle->getGroup(); $checker = $group->getChecker(); if (!$checker) { return $fuzzy; } $code = $handle->getCode(); $key = $handle->getKey(); $en = $group->getMessage($key, $group->getSourceLanguage()); $message = new FatMessage($key, $en); // Take the contents from edit field as a translation. $message->setTranslation($text); $checks = $checker->checkMessage($message, $code); if (count($checks)) { $fuzzy = true; } return $fuzzy; }
/** * @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; }
public static function translateRenderParserFunction($parser) { $pageTitle = $parser->getTitle(); $handle = new MessageHandle($pageTitle); $code = $handle->getCode(); if (Language::isKnownLanguageTag($code)) { return '/' . $code; } return ''; }
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__ ); }
/** * @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; }
/** * Builds a table with all translations of $title. * * @param Title $title (default: null) */ function showTranslations(Title $title) { $handle = new MessageHandle($title); $namespace = $title->getNamespace(); $message = $handle->getKey(); if (!$handle->isValid()) { $this->getOutput()->addWikiMsg('translate-translations-no-message', $title->getPrefixedText()); return; } $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')); if (!$res->numRows()) { $this->getOutput()->addWikiMsg('translate-translations-no-message', $title->getPrefixedText()); return; } else { $this->getOutput()->addWikiMsg('translate-translations-count', $this->getLanguage()->formatNum($res->numRows())); } // Normal output. $titles = array(); foreach ($res as $s) { $titles[] = $s->page_title; } $pageInfo = TranslateUtils::getContents($titles, $namespace); $tableheader = Xml::openElement('table', array('class' => 'mw-sp-translate-table sortable')); $tableheader .= Xml::openElement('tr'); $tableheader .= Xml::element('th', null, $this->msg('allmessagesname')->text()); $tableheader .= Xml::element('th', null, $this->msg('allmessagescurrent')->text()); $tableheader .= Xml::closeElement('tr'); // Adapted version of TranslateUtils:makeListing() by Nikerabbit. $out = $tableheader; $canTranslate = $this->getUser()->isAllowed('translate'); $ajaxPageList = array(); $historyText = " <sup>" . $this->msg('translate-translations-history-short')->escaped() . "</sup> "; $separator = $this->msg('word-separator')->plain(); foreach ($res as $s) { $key = $s->page_title; $tTitle = Title::makeTitle($s->page_namespace, $key); $ajaxPageList[] = $tTitle->getPrefixedDBkey(); $tHandle = new MessageHandle($tTitle); $code = $tHandle->getCode(); $text = TranslateUtils::getLanguageName($code, $this->getLanguage()->getCode()); $text .= $separator; $text .= $this->msg('parentheses')->params($code)->plain(); $text = htmlspecialchars($text); if ($canTranslate) { $tools['edit'] = TranslationHelpers::ajaxEditLink($tTitle, $text); } else { $tools['edit'] = Linker::link($tTitle, $text); } $tools['history'] = Linker::link($tTitle, $historyText, array('action', 'title' => $this->msg('history-title', $tTitle->getPrefixedDBkey())->text()), array('action' => 'history')); if (MessageHandle::hasFuzzyString($pageInfo[$key][0]) || $tHandle->isFuzzy()) { $class = 'orig'; } else { $class = 'def'; } $leftColumn = $tools['history'] . $tools['edit']; $out .= Xml::tags('tr', array('class' => $class), Xml::tags('td', null, $leftColumn) . Xml::tags('td', array('lang' => $code, 'dir' => Language::factory($code)->getDir()), TranslateUtils::convertWhiteSpaceToHTML($pageInfo[$key][0]))); } $out .= Xml::closeElement('table'); $this->getOutput()->addHTML($out); $vars = array('trlKeys' => $ajaxPageList); $this->getOutput()->addScript(Skin::makeVariablesScript($vars)); }
public static function onSectionSave($article, User $user, $text, $summary, $minor, $_, $_, $flags, $revision) { $title = $article->getTitle(); // Some checks $handle = new MessageHandle($title); // We are only interested in the translations namespace if (!$handle->isPageTranslation() || !$handle->isValid()) { return true; } // Do not trigger renders for fuzzy if (strpos($text, TRANSLATE_FUZZY) !== false) { return true; } $group = $handle->getGroup(); if (!$group instanceof WikiPageMessageGroup) { return true; } // Finally we know the title and can construct a Translatable page $page = TranslatablePage::newFromTitle($group->getTitle()); // Add a tracking mark if ($revision !== null) { self::addSectionTag($title, $revision->getId(), $page->getMarkedTag()); } // Update the target translation page if (!$handle->isDoc()) { $code = $handle->getCode(); self::updateTranslationPage($page, $code, $user, $flags, $summary); } return true; }