wikimedia/mediawiki-extensions-Translate

View on GitHub
src/TranslatorInterface/Aid/TranslationAidDataProvider.php

Summary

Maintainability
A
1 hr
Test Coverage
<?php
declare( strict_types = 1 );

namespace MediaWiki\Extension\Translate\TranslatorInterface\Aid;

use Content;
use ContentHandler;
use MediaWiki\Extension\Translate\MessageGroupProcessing\RevTagStore;
use MediaWiki\Extension\Translate\MessageLoading\MessageHandle;
use MediaWiki\Extension\Translate\TranslatorInterface\TranslationHelperException;
use MediaWiki\MediaWikiServices;
use MediaWiki\Revision\RevisionRecord;
use MediaWiki\Revision\SlotRecord;
use MessageGroup;
use TextContent;
use Wikimedia\Rdbms\IDatabase;

/**
 * @author Niklas Laxström
 * @license GPL-2.0-or-later
 * @since 2018.01
 */
class TranslationAidDataProvider {
    /** @var MessageHandle */
    private $handle;
    /** @var MessageGroup */
    private $group;
    /** @var string|null */
    private $definition;
    /** @var array */
    private $translations;

    public function __construct( MessageHandle $handle ) {
        $this->handle = $handle;
        $this->group = $handle->getGroup();
    }

    /**
     * Get the message definition. Cached for performance.
     * @return string
     */
    public function getDefinition(): string {
        if ( $this->definition !== null ) {
            return $this->definition;
        }

        // Optional performance optimization
        if ( method_exists( $this->group, 'getMessageContent' ) ) {
            // @phan-suppress-next-line PhanUndeclaredMethod
            $this->definition = $this->group->getMessageContent( $this->handle );
        } else {
            $this->definition = $this->group->getMessage(
                $this->handle->getKey(),
                $this->group->getSourceLanguage()
            );
        }

        if ( $this->definition === null ) {
            throw new TranslationHelperException(
                'Did not find message definition for ' . $this->handle->getTitle()->getPrefixedText() .
                ' in group ' . $this->group->getId()
            );
        }
        return $this->definition;
    }

    public function hasDefinition(): bool {
        try {
            $this->getDefinition();
            return true;
        } catch ( TranslationHelperException $e ) {
            return false;
        }
    }

    public function getDefinitionContent(): Content {
        return ContentHandler::makeContent( $this->getDefinition(), $this->handle->getTitle() );
    }

    /**
     * Get the translations in all languages. Cached for performance.
     * Fuzzy translation are not included.
     * @return array Language code => Translation
     */
    public function getGoodTranslations(): array {
        if ( $this->translations !== null ) {
            return $this->translations;
        }

        $mwServices = MediaWikiServices::getInstance();
        $data = self::loadTranslationData(
            $mwServices->getDBLoadBalancer()->getConnection( DB_REPLICA ),
            $this->handle
        );
        $translations = [];
        $prefixLength = strlen( $this->handle->getTitleForBase()->getDBkey() . '/' );
        $languageNameUtils = $mwServices->getLanguageNameUtils();

        foreach ( $data as $page => $translation ) {
            // Could use MessageHandle here, but that queries the message index.
            // Instead, we can get away with simple string manipulation.
            $code = substr( $page, $prefixLength );
            if ( !$languageNameUtils->isKnownLanguageTag( $code ) ) {
                continue;
            }

            $translations[ $code ] = $translation;
        }

        $this->translations = $translations;

        return $translations;
    }

    private static function loadTranslationData( IDatabase $db, MessageHandle $handle ): array {
        $revisionStore = MediaWikiServices::getInstance()->getRevisionStore();
        $conditions = [];

        // The list of pages we want to select, and their latest versions
        $conditions['page_namespace'] = $handle->getTitle()->getNamespace();
        $base = $handle->getKey();
        $conditions[] = 'page_title ' . $db->buildLike( "$base/", $db->anyString() );
        $conditions[] = 'rev_id=page_latest';

        // For fuzzy tags we need the join with revtag and also:
        $conditions[ 'rt_type' ] = null;

        $rows = $revisionStore->newSelectQueryBuilder( $db )
            ->joinPage()
            ->joinComment()
            ->leftJoin( 'revtag', null, [
                'page_id=rt_page',
                'page_latest=rt_revision',
                'rt_type' => RevTagStore::FUZZY_TAG
            ] )
            ->where( $conditions )
            ->caller( __METHOD__ )
            ->fetchResultSet();

        $pages = [];
        $revisions = $revisionStore->newRevisionsFromBatch( $rows, [ 'slots' => [ SlotRecord::MAIN ] ] )
            ->getValue();
        foreach ( $rows as $row ) {
            /** @var RevisionRecord|null $rev */
            $rev = $revisions[$row->rev_id];
            if ( $rev && $rev->getContent( SlotRecord::MAIN ) instanceof TextContent ) {
                $pages[$row->page_title] = $rev->getContent( SlotRecord::MAIN )->getText();
            }
        }

        return $pages;
    }
}