wikimedia/mediawiki-extensions-Translate

View on GitHub
src/PageTranslation/ParserOutput.php

Summary

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

namespace MediaWiki\Extension\Translate\PageTranslation;

use InvalidArgumentException;
use Language;
use MediaWiki\Extension\Translate\MessageLoading\Message;
use MediaWiki\Parser\Parser;

/**
 * Represents a parsing output produced by TranslatablePageParser.
 *
 * It is required generate translatable and translation page sources or just get the list of
 * translations units.
 *
 * @author Niklas Laxström
 * @license GPL-2.0-or-later
 * @since 2020.08
 */
class ParserOutput {
    private string $template;
    /** @var Section[] */
    private array $sectionMap;
    /** @var TranslationUnit[] */
    private array $unitMap;

    public function __construct( string $template, array $sectionMap, array $unitMap ) {
        $this->assertContainsOnlyInstancesOf( Section::class, '$sectionMap', $sectionMap );
        $this->assertContainsOnlyInstancesOf( TranslationUnit::class, '$unitMap', $unitMap );

        $this->template = $template;
        $this->sectionMap = $sectionMap;
        $this->unitMap = $unitMap;
    }

    /** Returns template that contains <translate> tags */
    public function sourcePageTemplate(): string {
        $replacements = [];
        foreach ( $this->sectionMap as $ph => $section ) {
            $replacements[$ph] = $section->wrappedContents();
        }

        return strtr( $this->template, $replacements );
    }

    /** Returns template that does not contain <translate> tags */
    public function translationPageTemplate(): string {
        $replacements = [];
        foreach ( $this->sectionMap as $ph => $section ) {
            $replacements[$ph] = $section->contents();
        }

        return strtr( $this->template, $replacements );
    }

    /** @return TranslationUnit[] */
    public function units(): array {
        return $this->unitMap;
    }

    /** Returns the source page wikitext used for rendering the page. */
    public function sourcePageTextForRendering( Language $sourceLanguage ): string {
        return $this->getPageTextForRendering( $sourceLanguage, $sourceLanguage, false );
    }

    /**
     * @param Language $sourceLanguage Language of the translatable page
     * @param Language $targetLanguage Language of the translation page; same as
     *  $sourceLanguage when rendering the translatable page
     * @param bool $wrapUntranslated Whether to wrap untranslated units in `<span>` or `<div>`
     *  with appropriate language and directionality set
     * @param array<string,Message> $messages Translations by translation unit;
     *  empty when rendering the translatable page
     * @param Parser|null $parser Wikitext parser to use when generating anchors for translated
     *  headings; if `null`, no anchors will be generated
     */
    public function getPageTextForRendering(
        Language $sourceLanguage,
        Language $targetLanguage,
        bool $wrapUntranslated,
        array $messages = [],
        ?Parser $parser = null
    ): string {
        $text = $this->translationPageTemplate();

        foreach ( $this->unitMap as $ph => $s ) {
            $t = $s->getTextForRendering(
                $messages[$s->id] ?? null,
                $sourceLanguage,
                $targetLanguage,
                $wrapUntranslated,
                $parser
            );
            $text = str_replace( $ph, $t, $text );
        }

        // Replace {{TRANSLATIONLANGUAGE}} usage outside of translation units (T224810)
        $text = preg_replace(
            TranslationUnit::TRANSLATIONLANGUAGE_REGEX,
            $targetLanguage->getCode(),
            $text
        );

        return $text;
    }

    /** Returns the source page with translation unit markers. */
    public function sourcePageTextForSaving(): string {
        $text = $this->sourcePageTemplate();

        foreach ( $this->unitMap as $ph => $s ) {
            $text = str_replace( $ph, $s->getMarkedText(), $text );
        }

        return $text;
    }

    /** Returns the page text with translation tags and unit placeholders for easy diffs */
    public function sourcePageTemplateForDiffs(): string {
        $text = $this->sourcePageTemplate();

        foreach ( $this->unitMap as $ph => $s ) {
            $text = str_replace( $ph, "<!--T:{$s->id}-->", $text );
        }

        return $text;
    }

    private function assertContainsOnlyInstancesOf(
        string $expected,
        string $name,
        array $x
    ): void {
        foreach ( $x as $item ) {
            if ( !$item instanceof $expected ) {
                $actual = get_debug_type( $item );
                throw new InvalidArgumentException(
                    "Parameter $name must only contain instances of class $expected. Got $actual."
                );
            }
        }
    }
}