Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
poimport.php
Go to the documentation of this file.
1<?php
12use MediaWiki\CommentStore\CommentStoreComment;
13use MediaWiki\Content\ContentHandler;
14use MediaWiki\Content\TextContent;
17use MediaWiki\Maintenance\Maintenance;
18use MediaWiki\MediaWikiServices;
19use MediaWiki\Revision\SlotRecord;
20use MediaWiki\Title\Title;
21use MediaWiki\User\User;
22
23// Standard boilerplate to define $IP
24if ( getenv( 'MW_INSTALL_PATH' ) !== false ) {
25 $IP = getenv( 'MW_INSTALL_PATH' );
26} else {
27 $dir = __DIR__;
28 $IP = "$dir/../../..";
29}
30require_once "$IP/maintenance/Maintenance.php";
31
32class Poimport extends Maintenance {
33 public function __construct() {
34 parent::__construct();
35 $this->addDescription( 'Po file importer (does not make changes unless specified).' );
36 $this->addOption(
37 'file',
38 'Gettext file to import (Translate specific formatting)',
39 true, /*required*/
40 true /*has arg*/
41 );
42 $this->addOption(
43 'user',
44 'User who makes edits to wiki',
45 true, /*required*/
46 true /*has arg*/
47 );
48 $this->addOption(
49 'really',
50 '(optional) Actually make changes',
51 false, /*required*/
52 false /*has arg*/
53 );
54 $this->requireExtension( 'Translate' );
55 }
56
57 public function execute() {
58 // Parse the po file.
59 $p = new PoImporter( $this->getOption( 'file' ) );
60 $p->setProgressCallback( [ $this, 'myOutput' ] );
61 [ $changes, $group ] = $p->parse();
62
63 if ( !count( $changes ) ) {
64 $this->output( "No changes to import\n" );
65 exit( 0 );
66 }
67
68 // Import changes to wiki.
69 $w = new WikiWriter(
70 $changes,
71 $group,
72 $this->getOption( 'user' ),
73 !$this->hasOption( 'really' )
74 );
75
76 $w->setProgressCallback( [ $this, 'myOutput' ] );
77 $w->execute();
78 }
79
87 public function myOutput( $text, $channel = null, $error = false ) {
88 if ( $error ) {
89 $this->error( $text );
90 } else {
91 $this->output( $text, $channel );
92 }
93 }
94}
95
102 private $progressCallback;
107 private $file;
108
110 public function __construct( $file ) {
111 $this->file = $file;
112 }
113
114 public function setProgressCallback( $callback ) {
115 $this->progressCallback = $callback;
116 }
117
119 protected function reportProgress( $text, $channel = null, $severity = 'status' ) {
120 if ( is_callable( $this->progressCallback ) ) {
121 $useErrorOutput = $severity === 'error';
122 call_user_func( $this->progressCallback, $text, $channel, $useErrorOutput );
123 }
124 }
125
133 protected function initMessages( $id, $code ) {
134 $group = MessageGroups::getGroup( $id );
135
136 $messages = $group->initCollection( $code );
137 $messages->loadTranslations();
138
139 return $messages;
140 }
141
146 public function parse() {
147 $data = file_get_contents( $this->file );
148 $data = TextContent::normalizeLineEndings( $data );
149
150 $matches = [];
151 if ( preg_match( '/X-Language-Code:\s+(.*)\\\n/', $data, $matches ) ) {
152 $code = $matches[1];
153 $this->reportProgress( "Detected language as $code", 'code' );
154 } else {
155 $this->reportProgress( 'Unable to determine language code', 'code', 'error' );
156
157 return false;
158 }
159
160 if ( preg_match( '/X-Message-Group:\s+(.*)\\\n/', $data, $matches ) ) {
161 $groupId = $matches[1];
162 $this->reportProgress( "Detected message group as $groupId", 'group' );
163 } else {
164 $this->reportProgress( 'Unable to determine message group', 'group', 'error' );
165
166 return false;
167 }
168
169 $contents = $this->initMessages( $groupId, $code );
170
171 echo "----\n";
172
173 $poformat = '".*"\n?(^".*"$\n?)*';
174 $quotePattern = '/(^"|"$\n?)/m';
175
176 $sections = preg_split( '/\n{2,}/', $data );
177 $changes = [];
178 foreach ( $sections as $section ) {
179 $matches = [];
180 if ( preg_match( "/^msgctxt\s($poformat)/mx", $section, $matches ) ) {
181 // Remove quoting
182 $key = preg_replace( $quotePattern, '', $matches[1] );
183
184 // Ignore unknown keys
185 if ( !isset( $contents[$key] ) ) {
186 continue;
187 }
188 } else {
189 continue;
190 }
191 $matches = [];
192 if ( preg_match( "/^msgstr\s($poformat)/mx", $section, $matches ) ) {
193 // Remove quoting
194 $translation = preg_replace( $quotePattern, '', $matches[1] );
195 // Restore new lines and remove quoting
196 $translation = stripcslashes( $translation );
197 } else {
198 continue;
199 }
200
201 // Fuzzy messages
202 if ( preg_match( '/^#, fuzzy$/m', $section ) ) {
203 $translation = TRANSLATE_FUZZY . $translation;
204 }
205
206 $oldtranslation = (string)$contents[$key]->translation();
207
208 if ( $translation !== $oldtranslation ) {
209 if ( $translation === '' ) {
210 $this->reportProgress( "Skipping empty translation in the po file for $key!\n" );
211 } else {
212 if ( $oldtranslation === '' ) {
213 $this->reportProgress( "New translation for $key\n" );
214 } else {
215 $this->reportProgress( "Translation of $key differs:\n$translation\n" );
216 }
217 $changes["$key/$code"] = $translation;
218 }
219 }
220 }
221
222 return [ $changes, $groupId ];
223 }
224}
225
231 private $progressCallback;
233 private $user;
235 private $changes;
237 private $dryrun;
239 private $group;
240
247 public function __construct( array $changes, $groupId, $user, $dryrun = true ) {
248 $this->changes = $changes;
249 $this->group = MessageGroups::getGroup( $groupId );
250 $this->user = MediaWikiServices::getInstance()
251 ->getUserFactory()
252 ->newFromName( $user );
253 $this->dryrun = $dryrun;
254 }
255
256 public function setProgressCallback( $callback ) {
257 $this->progressCallback = $callback;
258 }
259
261 protected function reportProgress( $text, $channel, $severity = 'status' ) {
262 if ( is_callable( $this->progressCallback ) ) {
263 $useErrorOutput = $severity === 'error';
264 call_user_func( $this->progressCallback, $text, $channel, $useErrorOutput );
265 }
266 }
267
271 public function execute() {
272 if ( !$this->group ) {
273 $this->reportProgress( 'Given group does not exist.', 'groupId', 'error' );
274
275 return;
276 }
277
278 if ( !$this->user->idForName() ) {
279 $this->reportProgress( 'Given user does not exist.', 'user', 'error' );
280
281 return;
282 }
283
284 $count = count( $this->changes );
285 $this->reportProgress( "Going to update $count pages.", 'pagecount' );
286
287 $ns = $this->group->getNamespace();
288
289 foreach ( $this->changes as $title => $text ) {
290 $this->updateMessage( $ns, $title, $text );
291 }
292 }
293
300 private function updateMessage( $namespace, $page, $text ) {
301 $title = Title::makeTitleSafe( $namespace, $page );
302
303 if ( !$title instanceof Title ) {
304 $this->reportProgress( 'INVALID TITLE!', $page, 'error' );
305
306 return;
307 }
308 $this->reportProgress( "Updating {$title->getPrefixedText()}... ", $title );
309
310 if ( $this->dryrun ) {
311 $this->reportProgress( 'DRY RUN!', $title );
312
313 return;
314 }
315
316 $page = MediaWikiServices::getInstance()->getWikiPageFactory()->newFromTitle( $title );
317 $content = ContentHandler::makeContent( $text, $title );
318 $updater = $page->newPageUpdater( $this->user )->setContent( SlotRecord::MAIN, $content );
319
320 if ( $this->user->authorizeWrite( 'autopatrol', $title ) ) {
321 $updater->setRcPatrolStatus( RecentChange::PRC_AUTOPATROLLED );
322 }
323
324 $summary = CommentStoreComment::newUnsavedComment( 'Updating translation from gettext import' );
325 $updater->saveRevision( $summary );
326 $status = $updater->getStatus();
327
328 if ( $status->isOK() ) {
329 $this->reportProgress( 'OK!', $title );
330 } else {
331 $this->reportProgress( 'Failed!', $title );
332 }
333 }
334}
335
336$maintClass = Poimport::class;
337require_once RUN_MAINTENANCE_IF_MAIN;
Factory class for accessing message groups individually by id or all of them as a list.
This file contains the class for core message collections implementation.
Parses a po file that has been exported from MediaWiki.
Definition poimport.php:100
parse()
Parses relevant stuff from the po file.
Definition poimport.php:146
reportProgress( $text, $channel=null, $severity='status')
Definition poimport.php:119
__construct( $file)
Definition poimport.php:110
initMessages( $id, $code)
Loads translations for comparison.
Definition poimport.php:133
myOutput( $text, $channel=null, $error=false)
Public alternative for protected Maintenance::output() as we need to get messages from the ChangeSync...
Definition poimport.php:87
Import changes to wiki as given user.
Definition poimport.php:229
execute()
Updates pages on by one.
Definition poimport.php:271
__construct(array $changes, $groupId, $user, $dryrun=true)
Definition poimport.php:247
reportProgress( $text, $channel, $severity='status')
Definition poimport.php:261