Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
poimport.php
Go to the documentation of this file.
1<?php
12use MediaWiki\MediaWikiServices;
13
14// Standard boilerplate to define $IP
15if ( getenv( 'MW_INSTALL_PATH' ) !== false ) {
16 $IP = getenv( 'MW_INSTALL_PATH' );
17} else {
18 $dir = __DIR__;
19 $IP = "$dir/../../..";
20}
21require_once "$IP/maintenance/Maintenance.php";
22
23class Poimport extends Maintenance {
24 public function __construct() {
25 parent::__construct();
26 $this->addDescription( 'Po file importer (does not make changes unless specified).' );
27 $this->addOption(
28 'file',
29 'Gettext file to import (Translate specific formatting)',
30 true, /*required*/
31 true /*has arg*/
32 );
33 $this->addOption(
34 'user',
35 'User who makes edits to wiki',
36 true, /*required*/
37 true /*has arg*/
38 );
39 $this->addOption(
40 'really',
41 '(optional) Actually make changes',
42 false, /*required*/
43 false /*has arg*/
44 );
45 $this->requireExtension( 'Translate' );
46 }
47
48 public function execute() {
49 // Parse the po file.
50 $p = new PoImporter( $this->getOption( 'file' ) );
51 $p->setProgressCallback( [ $this, 'myOutput' ] );
52 list( $changes, $group ) = $p->parse();
53
54 if ( !count( $changes ) ) {
55 $this->output( "No changes to import\n" );
56 exit( 0 );
57 }
58
59 // Import changes to wiki.
60 $w = new WikiWriter(
61 $changes,
62 $group,
63 $this->getOption( 'user' ),
64 !$this->hasOption( 'really' )
65 );
66
67 $w->setProgressCallback( [ $this, 'myOutput' ] );
68 $w->execute();
69 }
70
78 public function myOutput( $text, $channel = null, $error = false ) {
79 if ( $error ) {
80 $this->error( $text );
81 } else {
82 $this->output( $text, $channel );
83 }
84 }
85}
86
93 private $progressCallback;
98 private $file;
99
101 public function __construct( $file ) {
102 $this->file = $file;
103 }
104
105 public function setProgressCallback( $callback ) {
106 $this->progressCallback = $callback;
107 }
108
110 protected function reportProgress( $text, $channel = null, $severity = 'status' ) {
111 if ( is_callable( $this->progressCallback ) ) {
112 $useErrorOutput = $severity === 'error';
113 call_user_func( $this->progressCallback, $text, $channel, $useErrorOutput );
114 }
115 }
116
124 protected function initMessages( $id, $code ) {
125 $group = MessageGroups::getGroup( $id );
126
127 $messages = $group->initCollection( $code );
128 $messages->loadTranslations();
129
130 return $messages;
131 }
132
137 public function parse() {
138 $data = file_get_contents( $this->file );
139 $data = str_replace( "\r\n", "\n", $data );
140
141 $matches = [];
142 if ( preg_match( '/X-Language-Code:\s+(.*)\\\n/', $data, $matches ) ) {
143 $code = $matches[1];
144 $this->reportProgress( "Detected language as $code", 'code' );
145 } else {
146 $this->reportProgress( 'Unable to determine language code', 'code', 'error' );
147
148 return false;
149 }
150
151 if ( preg_match( '/X-Message-Group:\s+(.*)\\\n/', $data, $matches ) ) {
152 $groupId = $matches[1];
153 $this->reportProgress( "Detected message group as $groupId", 'group' );
154 } else {
155 $this->reportProgress( 'Unable to determine message group', 'group', 'error' );
156
157 return false;
158 }
159
160 $contents = $this->initMessages( $groupId, $code );
161
162 echo "----\n";
163
164 $poformat = '".*"\n?(^".*"$\n?)*';
165 $quotePattern = '/(^"|"$\n?)/m';
166
167 $sections = preg_split( '/\n{2,}/', $data );
168 $changes = [];
169 foreach ( $sections as $section ) {
170 $matches = [];
171 if ( preg_match( "/^msgctxt\s($poformat)/mx", $section, $matches ) ) {
172 // Remove quoting
173 $key = preg_replace( $quotePattern, '', $matches[1] );
174
175 // Ignore unknown keys
176 if ( !isset( $contents[$key] ) ) {
177 continue;
178 }
179 } else {
180 continue;
181 }
182 $matches = [];
183 if ( preg_match( "/^msgstr\s($poformat)/mx", $section, $matches ) ) {
184 // Remove quoting
185 $translation = preg_replace( $quotePattern, '', $matches[1] );
186 // Restore new lines and remove quoting
187 $translation = stripcslashes( $translation );
188 } else {
189 continue;
190 }
191
192 // Fuzzy messages
193 if ( preg_match( '/^#, fuzzy$/m', $section ) ) {
194 $translation = TRANSLATE_FUZZY . $translation;
195 }
196
197 $oldtranslation = (string)$contents[$key]->translation();
198
199 if ( $translation !== $oldtranslation ) {
200 if ( $translation === '' ) {
201 $this->reportProgress( "Skipping empty translation in the po file for $key!\n" );
202 } else {
203 if ( $oldtranslation === '' ) {
204 $this->reportProgress( "New translation for $key\n" );
205 } else {
206 $this->reportProgress( "Translation of $key differs:\n$translation\n" );
207 }
208 $changes["$key/$code"] = $translation;
209 }
210 }
211 }
212
213 return [ $changes, $groupId ];
214 }
215}
216
222 private $progressCallback;
224 private $user;
226 private $changes;
228 private $dryrun;
230 private $group;
231
238 public function __construct( array $changes, $groupId, $user, $dryrun = true ) {
239 $this->changes = $changes;
240 $this->group = MessageGroups::getGroup( $groupId );
241 $this->user = User::newFromName( $user );
242 $this->dryrun = $dryrun;
243 }
244
245 public function setProgressCallback( $callback ) {
246 $this->progressCallback = $callback;
247 }
248
250 protected function reportProgress( $text, $channel, $severity = 'status' ) {
251 if ( is_callable( $this->progressCallback ) ) {
252 $useErrorOutput = $severity === 'error';
253 call_user_func( $this->progressCallback, $text, $channel, $useErrorOutput );
254 }
255 }
256
260 public function execute() {
261 if ( !$this->group ) {
262 $this->reportProgress( 'Given group does not exist.', 'groupId', 'error' );
263
264 return;
265 }
266
267 if ( !$this->user->idForName() ) {
268 $this->reportProgress( 'Given user does not exist.', 'user', 'error' );
269
270 return;
271 }
272
273 $count = count( $this->changes );
274 $this->reportProgress( "Going to update $count pages.", 'pagecount' );
275
276 $ns = $this->group->getNamespace();
277
278 foreach ( $this->changes as $title => $text ) {
279 $this->updateMessage( $ns, $title, $text );
280 }
281 }
282
289 private function updateMessage( $namespace, $page, $text ) {
290 $title = Title::makeTitleSafe( $namespace, $page );
291
292 if ( !$title instanceof Title ) {
293 $this->reportProgress( 'INVALID TITLE!', $page, 'error' );
294
295 return;
296 }
297 $this->reportProgress( "Updating {$title->getPrefixedText()}... ", $title );
298
299 if ( $this->dryrun ) {
300 $this->reportProgress( 'DRY RUN!', $title );
301
302 return;
303 }
304
305 $page = MediaWikiServices::getInstance()->getWikiPageFactory()->newFromTitle( $title );
306 $content = ContentHandler::makeContent( $text, $title );
307 $status = $page->doUserEditContent(
308 $content,
309 $this->user,
310 'Updating translation from gettext import'
311 );
312
313 if ( $status === true || ( is_object( $status ) && $status->isOK() ) ) {
314 $this->reportProgress( 'OK!', $title );
315 } else {
316 $this->reportProgress( 'Failed!', $title );
317 }
318 }
319}
320
321$maintClass = Poimport::class;
322require_once RUN_MAINTENANCE_IF_MAIN;
Parses a po file that has been exported from Mediawiki.
Definition poimport.php:91
parse()
Parses relevant stuff from the po file.
Definition poimport.php:137
reportProgress( $text, $channel=null, $severity='status')
Definition poimport.php:110
__construct( $file)
Definition poimport.php:101
initMessages( $id, $code)
Loads translations for comparison.
Definition poimport.php:124
myOutput( $text, $channel=null, $error=false)
Public alternative for protected Maintenance::output() as we need to get messages from the ChangeSync...
Definition poimport.php:78
Import changes to wiki as given user.
Definition poimport.php:220
execute()
Updates pages on by one.
Definition poimport.php:260
__construct(array $changes, $groupId, $user, $dryrun=true)
Definition poimport.php:238
reportProgress( $text, $channel, $severity='status')
Definition poimport.php:250