summaryrefslogtreecommitdiff
blob: 261daf9257b6836dc722b9cc1edcdd7cbe83f06f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
<?php
declare( strict_types = 1 );

namespace MediaWiki\Extension\Translate\MessageGroupProcessing;

use MediaWiki\Extension\Translate\Services;
use MediaWiki\Extension\Translate\Utilities\BaseMaintenanceScript;
use MediaWiki\MediaWikiServices;
use Title;

/**
 * Script to import translations from a CSV file
 * @since 2022.06
 * @license GPL-2.0-or-later
 * @author Abijeet Patro
 */
class ImportTranslationsFromCsv extends BaseMaintenanceScript {
	public function __construct() {
		parent::__construct();
		$this->addDescription( 'Import translations for a CSV file' );

		$this->addArg(
			'file',
			'Path to CSV file to import',
			self::REQUIRED
		);

		$this->addOption(
			'summary',
			'The change summary when updating the translations',
			self::REQUIRED,
			self::HAS_ARG
		);

		$this->addOption(
			'user',
			'User ID of the user performing the import',
			self::REQUIRED,
			self::HAS_ARG
		);

		$this->addOption(
			'really',
			'Should the import actually be performed',
			self::OPTIONAL,
			self::NO_ARG
		);

		$this->requireExtension( 'Translate' );
	}

	public function execute() {
		$csvFilePath = $this->getArg( 0 );

		$username = $this->getOption( 'user' );
		$summary = $this->getOption( 'summary' );

		// Validate the parameters
		if ( trim( $summary ) === '' ) {
			$this->fatalError( 'Please provide a non-empty "summary"' );
		}

		$userFactory = MediaWikiServices::getInstance()->getUserFactory();
		$user = $userFactory->newFromName( $username );

		if ( $user === null || !$user->isRegistered() ) {
			$this->fatalError( "User $username does not exist." );
		}

		// Validate and parse the CSV file
		$csvImporter = Services::getInstance()->getCsvTranslationImporter();
		$status = $csvImporter->parseFile( $csvFilePath );

		if ( $status->isOK() ) {
			$messagesWithTranslations = $status->getValue();
			$output = "\n";
			foreach ( $messagesWithTranslations as $messageTranslations ) {
				$translations = $messageTranslations[ 'translations' ];
				$output .= '* ' . count( $this->filterEmptyTranslations( $translations ) ) .
					' translation(s) to import for ' .
					$messageTranslations['messageTitle'] . "\n";
			}

			$this->output( $output . "\n" );
		} else {
			$this->error( "Error during processing:\n" );
			$this->error( (string)$status );
			$this->fatalError( 'Exiting...' );
		}

		if ( !$this->hasOption( 'really' ) ) {
			$this->output( "\nUse option --really to perform the import.\n" );
			return true;
		}

		// Start the actual import of translations
		$this->output( "\nProceeding with import...\n\n" );
		$importStatus = $csvImporter->importData(
			$status->getValue(), $user, trim( $summary ), [ $this, 'progressReporter' ]
		 );

		if ( $importStatus->isOK() ) {
			$this->output( "\nSuccess: Import done\n" );
		} else {
			$this->output( "\nImport failed. See errors:\n" );
			$failedImportStatuses = $importStatus->getValue();
			foreach ( $failedImportStatuses as $translationTitleText => $status ) {
				$this->output( "\nImport failed for $translationTitleText:\n" );
				$this->output( $status );
			}

			return false;
		}

		return true;
	}

	public function progressReporter(
		Title $title,
		array $messageImportStatuses,
		int $total,
		int $processed
	): void {
		$paddedProcessed = str_pad( (string)$processed, strlen( (string)$total ), ' ', STR_PAD_LEFT );
		$progressCounter = "($paddedProcessed/$total)";

		$successCount = 0;
		$failCount = 0;
		foreach ( $messageImportStatuses as $messageImportStatus ) {
			if ( $messageImportStatus->isOK() ) {
				$successCount++;
			} else {
				$failCount++;
			}
		}

		$this->output(
			"$progressCounter Imported translations for {$title->getPrefixedText()} with $failCount " .
			"failure(s) and $successCount successful import(s) ...\n"
		);
	}

	private function filterEmptyTranslations( array $translations ): array {
		return array_filter( $translations, 'strlen' );
	}
}