getTitle() ); if ( $handle->isValid() ) { $editPage->suppressIntro = true; } } /** * Prevent translations to non-translatable languages for the group * Hook: getUserPermissionsErrorsExpensive * * @param Title $title * @param User $user * @param string $action * @param mixed &$result * @return bool */ public static function disallowLangTranslations( Title $title, User $user, $action, &$result ) { global $wgTranslateBlacklist; if ( $action !== 'edit' ) { return true; } $handle = new MessageHandle( $title ); if ( !$handle->isValid() ) { return true; } if ( $user->isAllowed( 'translate-manage' ) ) { return true; } $group = $handle->getGroup(); $languages = $group->getTranslatableLanguages(); $langCode = $handle->getCode(); if ( $languages !== null && $langCode && !isset( $languages[$langCode] ) ) { $result = [ 'translate-language-disabled' ]; return false; } $groupId = $group->getId(); $checks = [ $groupId, strtok( $groupId, '-' ), '*' ]; foreach ( $checks as $check ) { if ( isset( $wgTranslateBlacklist[$check][$langCode] ) ) { $reason = $wgTranslateBlacklist[$check][$langCode]; $result = [ 'translate-page-disabled', $reason ]; return false; } } return true; } /** * Adds the translation aids and navigation to the normal edit page. * Hook: EditPage::showEditForm:initial * @param EditPage $object * @return true */ public static function addTools( EditPage $object ) { $handle = new MessageHandle( $object->getTitle() ); if ( !$handle->isValid() ) { return true; } $object->editFormTextTop .= self::editBoxes( $object ); return true; } /** * Replace the normal save button with one that says if you are editing * message documentation to try to avoid accidents. * Hook: EditPageBeforeEditButtons * * @param EditPage $editpage * @param array &$buttons * @param int $tabindex */ public static function buttonHack( EditPage $editpage, &$buttons, $tabindex ) { $handle = new MessageHandle( $editpage->getTitle() ); if ( !$handle->isValid() ) { return; } $context = $editpage->getArticle()->getContext(); if ( $handle->isDoc() ) { $langCode = $context->getLanguage()->getCode(); $name = TranslateUtils::getLanguageName( $handle->getCode(), $langCode ); $attribs = [ 'id' => 'wpSave', 'name' => 'wpSave', 'tabindex' => ++$tabindex, ] + Linker::tooltipAndAccesskeyAttribs( 'save' ); $saveConfig = OOUI\Element::configFromHtmlAttributes( $attribs ); $buttons['save'] = new OOUI\ButtonInputWidget( [ // Support: IE 6 – Use , otherwise it can't distinguish which button was clicked 'useInputTag' => true, 'flags' => [ 'progressive', 'primary' ], 'label' => $context->msg( 'translate-save', $name )->text(), 'type' => 'submit', ] + $saveConfig ); } try { $supportUrl = SupportAid::getSupportUrl( $handle->getTitle() ); } catch ( TranslationHelperException $e ) { return; } $attribs = [ 'id' => 'wpSupport', 'name' => 'wpSupport', 'type' => 'button', 'tabindex' => ++$tabindex, 'title' => $context->msg( 'translate-js-support-title' )->text(), ]; $attribs += [ 'label' => $context->msg( 'translate-js-support' )->text(), 'href' => $supportUrl, 'target' => '_blank', ]; $saveConfig = OOUI\Element::configFromHtmlAttributes( $attribs ); $buttons['ask'] = new OOUI\ButtonWidget( $saveConfig ); } /** * @param EditPage $editpage * @return string */ private static function editBoxes( EditPage $editpage ) { $context = $editpage->getArticle()->getContext(); $request = $context->getRequest(); $groupId = $request->getText( 'loadgroup', '' ); $th = new TranslationHelpers( $editpage->getTitle(), $groupId ); if ( $editpage->firsttime && !$request->getCheck( 'oldid' ) && !$request->getCheck( 'undo' ) ) { $editpage->textbox1 = (string)$th->getTranslation(); } else { $th->setTranslation( $editpage->textbox1 ); } TranslationHelpers::addModules( $context->getOutput() ); return $th->getBoxes(); } /** * Runs message checks, adds tp:transver tags and updates statistics. * Hook: PageContentSaveComplete * @param WikiPage $wikiPage * @param User $user * @param Content $content * @param string $summary * @param bool $minor * @param string $_1 * @param bool $_2 * @param int $flags * @param Revision|null $revision * @return true */ public static function onSave( WikiPage $wikiPage, User $user, Content $content, $summary, $minor, $_1, $_2, $flags, Revision $revision = null ) { global $wgEnablePageTranslation; if ( !$content instanceof TextContent ) { // Screw it, not interested return true; } $text = $content->getNativeData(); $title = $wikiPage->getTitle(); $handle = new MessageHandle( $title ); if ( !$handle->isValid() ) { return true; } // Update it. if ( $revision === null ) { $rev = $wikiPage->getTitle()->getLatestRevID(); } else { $rev = $revision->getID(); } $fuzzy = self::checkNeedsFuzzy( $handle, $text ); self::updateFuzzyTag( $title, $rev, $fuzzy ); $group = $handle->getGroup(); // Update translation stats - source language should always be up to date if ( $handle->getCode() !== $group->getSourceLanguage() ) { // This will update in-process cache immediately, but the value is saved // to the database in a deferred update. See MessageGroupStats::queueUpdates. // In case an error happens before that, the stats may be stale, but that // would be fixed by the next update or purge. MessageGroupStats::clear( $handle ); } MessageGroupStatesUpdaterJob::onChange( $handle ); if ( $fuzzy === false ) { Hooks::run( 'Translate:newTranslation', [ $handle, $rev, $text, $user ] ); } TTMServer::onChange( $handle ); if ( $wgEnablePageTranslation && $handle->isPageTranslation() ) { // Updates for translatable pages only PageTranslationHooks::onSectionSave( $wikiPage, $user, $content, $summary, $minor, $flags, $revision, $handle ); } return true; } /** * @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 * @param int $revision * @param bool $fuzzy Whether to fuzzy or not * @return bool Whether status changed */ protected static function updateFuzzyTag( Title $title, $revision, $fuzzy ) { $dbw = wfGetDB( DB_MASTER ); $conds = [ 'rt_page' => $title->getArticleID(), 'rt_type' => RevTag::getType( 'fuzzy' ), 'rt_revision' => $revision ]; // Replace the existing fuzzy tag, if any if ( $fuzzy !== false ) { $index = array_keys( $conds ); $dbw->replace( 'revtag', [ $index ], $conds, __METHOD__ ); } else { $dbw->delete( 'revtag', $conds, __METHOD__ ); } return (bool)$dbw->affectedRows(); } /** * Adds tag which identifies the revision of source message at that time. * This is used to show diff against current version of source message * when updating a translation. * Hook: Translate:newTranslation * @param MessageHandle $handle * @param int $revision * @param string $text * @param User $user * @return bool */ public static function updateTransverTag( MessageHandle $handle, $revision, $text, User $user ) { if ( $user->isAllowed( 'bot' ) ) { return false; } $group = $handle->getGroup(); $title = $handle->getTitle(); $name = $handle->getKey() . '/' . $group->getSourceLanguage(); $definitionTitle = Title::makeTitleSafe( $title->getNamespace(), $name ); if ( !$definitionTitle || !$definitionTitle->exists() ) { return true; } $definitionRevision = $definitionTitle->getLatestRevID(); $dbw = wfGetDB( DB_MASTER ); $conds = [ 'rt_page' => $title->getArticleID(), 'rt_type' => RevTag::getType( 'tp:transver' ), 'rt_revision' => $revision, 'rt_value' => $definitionRevision, ]; $index = [ 'rt_type', 'rt_page', 'rt_revision' ]; $dbw->replace( 'revtag', [ $index ], $conds, __METHOD__ ); return true; } /** * Hook: ArticlePrepareTextForEdit * @param WikiPage $wikiPage * @param ParserOptions $popts * @return bool */ public static function disablePreSaveTransform( WikiPage $wikiPage, ParserOptions $popts ) { global $wgTranslateUsePreSaveTransform; if ( !$wgTranslateUsePreSaveTransform ) { $handle = new MessageHandle( $wikiPage->getTitle() ); if ( $handle->isMessageNamespace() && !$handle->isDoc() ) { $popts->setPreSaveTransform( false ); } } return true; } /** * Hook: ArticleContentOnDiff * @param DifferenceEngine $de * @param OutputPage $out * @return true */ public static function displayOnDiff( DifferenceEngine $de, OutputPage $out ) { $title = $de->getTitle(); $handle = new MessageHandle( $title ); if ( !$handle->isValid() ) { return true; } $th = new TranslationHelpers( $title, /*group*/false ); $th->setEditMode( false ); $de->loadNewText(); if ( method_exists( $de, 'getNewRevision' ) ) { $newRevision = $de->getNewRevision(); $newContent = $newRevision ? $newRevision->getContent( 'main' ) : null; } else { $newContent = $de->mNewRev ? $de->mNewRev->getContent() : null; } if ( $newContent instanceof TextContent ) { $th->setTranslation( $newContent->getNativeData() ); } else { // Screw you, not interested. return true; } TranslationHelpers::addModules( $out ); $boxes = []; $boxes[] = $th->callBox( 'documentation', [ $th, 'getDocumentationBox' ] ); $boxes[] = $th->callBox( 'definition', [ $th, 'getDefinitionBox' ] ); $boxes[] = $th->callBox( 'translation', [ $th, 'getTranslationDisplayBox' ] ); $output = implode( "\n", $boxes ); $output = Html::rawElement( 'div', [ 'class' => 'mw-sp-translate-edit-fields' ], $output ); $out->addHTML( $output ); return true; } }