/**
	 * 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);
                 }
             }
         }
     }
 }
예제 #10
0
 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 = "&#160;<sup>" . $this->msg('translate-translations-history-short')->escaped() . "</sup>&#160;";
     $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));
 }
예제 #21
0
 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;
 }