Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
MessageWebImporter.php
1<?php
2declare( strict_types = 1 );
3
5
6use InvalidArgumentException;
7use MediaWiki\CommentStore\CommentStoreComment;
8use MediaWiki\Content\ContentHandler;
9use MediaWiki\Context\IContextSource;
10use MediaWiki\Diff\DifferenceEngine;
15use MediaWiki\Html\Html;
16use MediaWiki\Language\Language;
17use MediaWiki\MediaWikiServices;
18use MediaWiki\RecentChanges\RecentChange;
19use MediaWiki\Revision\MutableRevisionRecord;
20use MediaWiki\Revision\SlotRecord;
21use MediaWiki\Title\Title;
22use MessageGroup;
23use RuntimeException;
24
35 private Title $title;
36 private MessageGroup $group;
37 private string $code;
39 private $time;
40
42 private const MAX_PROCESSING_TIME = 43;
43
51 public function __construct(
52 Title $title,
53 private readonly IContextSource $context,
54 $group = null,
55 string $code = 'en'
56 ) {
57 $this->setTitle( $title );
58 $this->setGroup( $group );
59 $this->setCode( $code );
60 }
61
63 public function getTitle(): Title {
64 return $this->title;
65 }
66
67 public function setTitle( Title $title ): void {
68 $this->title = $title;
69 }
70
71 public function getGroup(): MessageGroup {
72 return $this->group;
73 }
74
76 public function setGroup( $group ): void {
77 if ( is_string( $group ) ) {
78 $group = MessageGroups::getGroup( $group );
79 if ( !$group ) {
80 throw new InvalidArgumentException( __METHOD__ . ' called with invalid group ID ' . $group );
81 }
82 }
83 $this->group = $group;
84 }
85
86 public function getCode(): string {
87 return $this->code;
88 }
89
90 public function setCode( string $code = 'en' ): void {
91 $this->code = $code;
92 }
93
94 protected function getAction(): string {
95 return $this->getTitle()->getLocalURL();
96 }
97
98 protected function doHeader(): string {
99 $formParams = [
100 'method' => 'post',
101 'action' => $this->getAction(),
102 'class' => 'mw-translate-manage'
103 ];
104
105 $csrfTokenSet = $this->context->getCsrfTokenSet();
106 return Html::openElement( 'form', $formParams ) .
107 Html::hidden( 'title', $this->getTitle()->getPrefixedText() ) .
108 Html::hidden( 'token', $csrfTokenSet->getToken() ) .
109 Html::hidden( 'process', 1 );
110 }
111
112 protected function doFooter(): string {
113 return '</form>';
114 }
115
116 protected function allowProcess(): bool {
117 $request = $this->context->getRequest();
118 $csrfTokenSet = $this->context->getCsrfTokenSet();
119
120 return $request->wasPosted()
121 && $request->getBool( 'process' )
122 && $csrfTokenSet->matchTokenField( 'token' );
123 }
124
125 protected function getActions(): array {
126 return [
127 'import',
128 $this->code === 'en' ? 'fuzzy' : 'conflict',
129 'ignore',
130 ];
131 }
132
133 public function execute( array $messages ): bool {
134 $context = $this->context;
135 $output = $context->getOutput();
136
137 // Set up diff engine
138 $diff = new DifferenceEngine( $context );
139 $diff->showDiffStyle();
140 $diff->setReducedLineNumbers();
141
142 // Check whether we do processing
143 $process = $this->allowProcess();
144
145 // Initialise collection
146 $group = $this->getGroup();
147 $code = $this->getCode();
148 $collection = $group->initCollection( $code );
149 $collection->loadTranslations();
150
151 $output->addHTML( $this->doHeader() );
152
153 // Initialise variable to keep track whether all changes were imported
154 // or not. If we're allowed to process, initially assume they were.
155 $allDone = $process;
156
157 // Determine changes for each message.
158 $changed = [];
159
160 foreach ( $messages as $key => $value ) {
161 $old = null;
162 $isExistingMessageFuzzy = false;
163
164 if ( isset( $collection[$key] ) ) {
165 // This returns null if no existing translation is found
166 $old = $collection[$key]->translation();
167 $isExistingMessageFuzzy = $collection[$key]->hasTag( 'fuzzy' );
168 }
169
170 if ( $old === null ) {
171 // We found a new translation for this message of the
172 // current group: import it.
173 if ( $process ) {
174 $action = 'import';
175 $this->doAction(
176 $action,
177 $group,
178 $key,
179 $value
180 );
181 }
182 // Show the user that we imported the new translation
183 $para = '<code class="mw-tmi-new">' . htmlspecialchars( $key ) . '</code>';
184 $name = $context->msg( 'translate-manage-import-new' )->rawParams( $para )
185 ->escaped();
186 $text = Utilities::convertWhiteSpaceToHTML( $value );
187 $changed[] = self::makeSectionElement( $name, 'new', $text );
188 } else {
189 // No changes at all, ignore
190 if ( $old === (string)$value ) {
191 continue;
192 }
193
194 // Check if the message is already fuzzy in the system, and then determine if there are changes
195 $oldTextForDiff = $old;
196 if ( $isExistingMessageFuzzy ) {
197 if ( MessageHandle::makeFuzzyString( $old ) === (string)$value ) {
198 continue;
199 }
200
201 // Normalize the display of FUZZY message diffs so that if an old message has
202 // a fuzzy tag, then that is added to the text used in the diff.
203 $oldTextForDiff = MessageHandle::makeFuzzyString( $old );
204 }
205
206 // MutableRevisionRecord expects a page that can exist, so use a dummy non-special page.
207 $dummyMainPage = Title::makeTitle( NS_MAIN, 'Some title just for diff' );
208 $oldContent = ContentHandler::makeContent( $oldTextForDiff, $dummyMainPage );
209 $oldRevision = new MutableRevisionRecord( $dummyMainPage );
210 $oldRevision->setContent( SlotRecord::MAIN, $oldContent );
211
212 $newContent = ContentHandler::makeContent( $value, $dummyMainPage );
213 $newRevision = new MutableRevisionRecord( $dummyMainPage );
214 $newRevision->setContent( SlotRecord::MAIN, $newContent );
215
216 $diff->setRevisions( $oldRevision, $newRevision );
217 $text = $diff->getDiff( '', '' );
218
219 // This is a changed translation. Note it for the next steps.
220 $type = 'changed';
221
222 // Get the user instructions for the current message,
223 // submitted together with the form
224 $action = $context->getRequest()
225 ->getVal( self::escapeNameForPHP( "action-$type-$key" ) );
226
227 if ( $process ) {
228 if ( $changed === [] ) {
229 // Initialise the HTML list showing the changes performed
230 $changed[] = '<ul>';
231 }
232
233 if ( $action === null ) {
234 // We have been told to process the messages, but not
235 // what to do with this one. Tell the user.
236 $message = $context->msg(
237 'translate-manage-inconsistent',
238 wfEscapeWikiText( "action-$type-$key" )
239 )->parse();
240 $changed[] = "<li>$message</li></ul>";
241
242 // Also stop any further processing for the other messages.
243 $process = false;
244 } else {
245 // Check processing time
246 if ( $this->time === null ) {
247 $this->time = (int)wfTimestamp();
248 }
249
250 // We have all the necessary information on this changed
251 // translation: actually process the message
252 $messageKeyAndParams = $this->doAction(
253 $action,
254 $group,
255 $key,
256 $value
257 );
258
259 // Show what we just did, adding to the list of changes
260 $msgKey = array_shift( $messageKeyAndParams );
261 $params = $messageKeyAndParams;
262 $message = $context->msg( $msgKey, $params )->parse();
263 $changed[] = "<li>$message</li>";
264
265 // Stop processing further messages if too much time
266 // has been spent.
267 if ( $this->checkProcessTime() ) {
268 $process = false;
269 $message = $context->msg( 'translate-manage-toolong' )
270 ->numParams( self::MAX_PROCESSING_TIME )->parse();
271 $changed[] = "<li>$message</li></ul>";
272 }
273
274 continue;
275 }
276 }
277
278 // We are not processing messages, or no longer, or this was an
279 // un-actionable translation. We will eventually return false
280 $allDone = false;
281
282 // Prepare to ask the user what to do with this message
283 $actions = $this->getActions();
284 $defaultAction = $action ?: 'import';
285
286 $act = [];
287
288 // Give grep a chance to find the usages:
289 // translate-manage-action-import, translate-manage-action-conflict,
290 // translate-manage-action-ignore, translate-manage-action-fuzzy
291 foreach ( $actions as $action ) {
292 $label = $context->msg( "translate-manage-action-$action" )->escaped();
293 $act[] = Html::rawElement(
294 'label',
295 [],
296 Html::radio(
297 self::escapeNameForPHP( "action-$type-$key" ),
298 $action === $defaultAction,
299 [ 'value' => $action ]
300 ) .
301 "\u{00A0}" .
302 $label
303 );
304 }
305
306 $param = '<code class="mw-tmi-diff">' . htmlspecialchars( $key ) . '</code>';
307 $name = $context->msg( 'translate-manage-import-diff' )
308 ->rawParams( $param, implode( ' ', $act ) )
309 ->escaped();
310
311 $changed[] = self::makeSectionElement( $name, $type, $text );
312 }
313 }
314
315 if ( !$process ) {
316 $collection->filter( MessageCollection::FILTER_HAS_TRANSLATION, MessageCollection::INCLUDE_MATCHING );
317 $keys = $collection->getMessageKeys();
318
319 $diff = array_diff( $keys, array_keys( $messages ) );
320
321 foreach ( $diff as $s ) {
322 $para = '<code class="mw-tmi-deleted">' . htmlspecialchars( $s ) . '</code>';
323 $name = $context->msg( 'translate-manage-import-deleted' )->rawParams( $para )->escaped();
324 $text = Utilities::convertWhiteSpaceToHTML( $collection[$s]->translation() );
325 $changed[] = self::makeSectionElement( $name, 'deleted', $text );
326 }
327 }
328
329 if ( $process || ( $changed === [] && $code !== 'en' ) ) {
330 if ( $changed === [] ) {
331 $output->addWikiMsg( 'translate-manage-nochanges-other' );
332 }
333
334 if ( $changed === [] || !str_starts_with( end( $changed ), '<li>' ) ) {
335 $changed[] = '<ul>';
336 }
337
338 $changed[] = '</ul>';
339
340 $languageName = Utilities::getLanguageName( $code, $context->getLanguage()->getCode() );
341 $message = $context
342 ->msg( 'translate-manage-import-done', $group->getId(), $group->getLabel(), $languageName )
343 ->parse();
344 $changed[] = Html::successBox( $message );
345 $output->addHTML( implode( "\n", $changed ) );
346 } else {
347 // END
348 if ( $changed !== [] ) {
349 if ( $code === 'en' ) {
350 $output->addWikiMsg( 'translate-manage-intro-en' );
351 } else {
352 $lang = Utilities::getLanguageName(
353 $code,
354 $context->getLanguage()->getCode()
355 );
356 $output->addWikiMsg( 'translate-manage-intro-other', $lang );
357 }
358 $output->addHTML( Html::hidden( 'language', $code ) );
359 $output->addHTML( implode( "\n", $changed ) );
360 $output->addHTML( Html::submitButton( $context->msg( 'translate-manage-submit' )->text() ) );
361 } else {
362 $output->addWikiMsg( 'translate-manage-nochanges' );
363 }
364 }
365
366 $output->addHTML( $this->doFooter() );
367
368 return $allDone;
369 }
370
380 private function doAction(
381 string $action,
382 MessageGroup $group,
383 string $key,
384 string $message
385 ): array {
386 global $wgTranslateDocumentationLanguageCode;
387
388 $comment = '';
389 $code = $this->getCode();
390 $title = $this->makeTranslationTitle( $group, $key, $code );
391
392 if ( $action === 'import' || $action === 'conflict' ) {
393 if ( $action === 'import' ) {
394 $comment = wfMessage( 'translate-manage-import-summary' )->inContentLanguage()->plain();
395 } else {
396 $comment = wfMessage( 'translate-manage-conflict-summary' )->inContentLanguage()->plain();
397 $message = MessageHandle::makeFuzzyString( $message );
398 }
399
400 return $this->doImport( $title, $message, $comment );
401 } elseif ( $action === 'ignore' ) {
402 return [ 'translate-manage-import-ignore', $key ];
403 } elseif ( $action === 'fuzzy' && $code !== 'en' &&
404 $code !== $wgTranslateDocumentationLanguageCode
405 ) {
406 $message = MessageHandle::makeFuzzyString( $message );
407
408 return $this->doImport( $title, $message, $comment );
409 } elseif ( $action === 'fuzzy' && $code === 'en' ) {
410 return $this->doFuzzy( $title, $message, $comment );
411 } else {
412 throw new InvalidArgumentException( "Unhandled action $action" );
413 }
414 }
415
416 protected function checkProcessTime(): bool {
417 return (int)wfTimestamp() - $this->time >= self::MAX_PROCESSING_TIME;
418 }
419
421 private function doImport( Title $title, string $message, string $summary ): array {
422 $mwServices = MediaWikiServices::getInstance();
423 $wikiPage = $mwServices->getWikiPageFactory()->newFromTitle( $title );
424 $content = ContentHandler::makeContent( $message, $title );
425
426 $user = $this->context->getUser();
427 $updater = $wikiPage->newPageUpdater( $user )->setContent( SlotRecord::MAIN, $content );
428 if ( $user->authorizeWrite( 'autopatrol', $title ) ) {
429 $updater->setRcPatrolStatus( RecentChange::PRC_AUTOPATROLLED );
430 }
431 $updater->saveRevision( CommentStoreComment::newUnsavedComment( $summary ) );
432 $status = $updater->getStatus();
433 $success = $status->isOK();
434
435 if ( $success ) {
436 return [ 'translate-manage-import-ok',
437 wfEscapeWikiText( $title->getPrefixedText() )
438 ];
439 }
440
441 $statusFormatter = $mwServices
442 ->getFormatterFactory()
443 ->getStatusFormatter( $this->context );
444 $text = "Failed to import new version of page {$title->getPrefixedText()}\n";
445 $text .= $statusFormatter->getWikiText( $status );
446 throw new RuntimeException( $text );
447 }
448
450 private function doFuzzy( Title $title, string $message, string $comment ): array {
451 $services = MediaWikiServices::getInstance();
452
453 if ( !$this->context->getUser()->isAllowed( 'translate-manage' ) ) {
454 return [ 'badaccess-group0' ];
455 }
456
457 // Work on all subpages of base title.
458 $handle = new MessageHandle( $title );
459 $titleText = $handle->getKey();
460
461 $revStore = $services->getRevisionStore();
462 $dbw = $services->getConnectionProvider()->getPrimaryDatabase();
463 $rows = $revStore->newSelectQueryBuilder( $dbw )
464 ->joinPage()
465 ->where( [
466 'page_namespace' => $title->getNamespace(),
467 'page_latest=rev_id',
468 'page_title' . $dbw->buildLike( "$titleText/", $dbw->anyString() ),
469 ] )
470 ->caller( __METHOD__ )
471 ->fetchResultSet();
472
473 $changed = [];
474 $slots = $revStore->getContentBlobsForBatch( $rows, [ SlotRecord::MAIN ] )->getValue();
475
476 foreach ( $rows as $row ) {
477 global $wgTranslateDocumentationLanguageCode;
478
479 $translationTitle = Title::makeTitle( (int)$row->page_namespace, $row->page_title );
480
481 // No fuzzy for English original or documentation language code.
482 if ( $translationTitle->getSubpageText() === 'en' ||
483 $translationTitle->getSubpageText() === $wgTranslateDocumentationLanguageCode
484 ) {
485 // Use imported text, not database text.
486 $text = $message;
487 } elseif ( isset( $slots[$row->rev_id] ) ) {
488 $slot = $slots[$row->rev_id][SlotRecord::MAIN];
489 $text = MessageHandle::makeFuzzyString( $slot->blob_data );
490 } else {
491 $text = MessageHandle::makeFuzzyString(
492 Utilities::getTextFromTextContent(
493 $revStore->newRevisionFromRow( $row )->getContent( SlotRecord::MAIN )
494 )
495 );
496 }
497
498 // Do actual import
499 $changed[] = $this->doImport( $translationTitle, $text, $comment );
500 }
501
502 // Format return text
503 $text = '';
504 foreach ( $changed as $c ) {
505 $key = array_shift( $c );
506 $text .= '* ' . $this->context->msg( $key, $c )->plain() . "\n";
507 }
508
509 return [ 'translate-manage-import-fuzzy', "\n" . $text ];
510 }
511
521 private function makeTranslationTitle( MessageGroup $group, string $key, string $code ): Title {
522 $ns = $group->getNamespace();
523
524 return Title::makeTitleSafe( $ns, "$key/$code" );
525 }
526
537 public static function makeSectionElement(
538 string $legend,
539 string $type,
540 string $content,
541 ?Language $lang = null,
542 array $extraContainerClasses = []
543 ): string {
544 $containerParams = [ 'class' => "mw-tpt-sp-section mw-tpt-sp-section-type-{$type}" ];
545 if ( $extraContainerClasses ) {
546 $containerParams[ 'class' ] .= ' ' . implode( ' ', $extraContainerClasses );
547 }
548 $legendParams = [ 'class' => 'mw-tpt-sp-legend' ];
549 $contentParams = [ 'class' => 'mw-tpt-sp-content' ];
550 if ( $lang ) {
551 $contentParams['dir'] = $lang->getDir();
552 $contentParams['lang'] = $lang->getCode();
553 }
554
555 return Html::rawElement( 'div', $containerParams,
556 Html::rawElement( 'div', $legendParams, $legend ) .
557 Html::rawElement( 'div', $contentParams, $content )
558 );
559 }
560
566 private static function escapeNameForPHP( string $name ): string {
567 $replacements = [
568 '(' => '(OP)',
569 ' ' => '(SP)',
570 "\t" => '(TAB)',
571 '.' => '(DOT)',
572 "'" => '(SQ)',
573 "\"" => '(DQ)',
574 '%' => '(PC)',
575 '&' => '(AMP)',
576 ];
577
578 return strtr( $name, $replacements );
579 }
580}
return[ 'Translate:AggregateGroupManager'=> static function(MediaWikiServices $services):AggregateGroupManager { return new AggregateGroupManager($services->getTitleFactory(), $services->get( 'Translate:MessageGroupMetadata'));}, 'Translate:AggregateGroupMessageGroupFactory'=> static function(MediaWikiServices $services):AggregateGroupMessageGroupFactory { return new AggregateGroupMessageGroupFactory($services->get( 'Translate:MessageGroupMetadata'));}, 'Translate:ConfigHelper'=> static function():ConfigHelper { return new ConfigHelper();}, 'Translate:CsvTranslationImporter'=> static function(MediaWikiServices $services):CsvTranslationImporter { return new CsvTranslationImporter( $services->getWikiPageFactory());}, 'Translate:EntitySearch'=> static function(MediaWikiServices $services):EntitySearch { return new EntitySearch($services->getMainWANObjectCache(), $services->getCollationFactory() ->makeCollation( 'uca-default-u-kn'), MessageGroups::singleton(), $services->getNamespaceInfo(), $services->get( 'Translate:MessageIndex'), $services->getTitleParser(), $services->getTitleFormatter());}, 'Translate:ExternalMessageSourceStateComparator'=> static function(MediaWikiServices $services):ExternalMessageSourceStateComparator { return new ExternalMessageSourceStateComparator(new SimpleStringComparator(), $services->getRevisionLookup(), $services->getPageStore());}, 'Translate:ExternalMessageSourceStateImporter'=> static function(MediaWikiServices $services):ExternalMessageSourceStateImporter { return new ExternalMessageSourceStateImporter($services->get( 'Translate:GroupSynchronizationCache'), $services->getJobQueueGroup(), LoggerFactory::getInstance(LogNames::GROUP_SYNCHRONIZATION), $services->get( 'Translate:MessageIndex'), $services->getTitleFactory(), $services->get( 'Translate:MessageGroupSubscription'), new ServiceOptions(ExternalMessageSourceStateImporter::CONSTRUCTOR_OPTIONS, $services->getMainConfig()));}, 'Translate:FileBasedMessageGroupFactory'=> static function(MediaWikiServices $services):FileBasedMessageGroupFactory { return new FileBasedMessageGroupFactory(new MessageGroupConfigurationParser(), $services->getContentLanguageCode() ->toString(), new ServiceOptions(FileBasedMessageGroupFactory::SERVICE_OPTIONS, $services->getMainConfig()),);}, 'Translate:FileFormatFactory'=> static function(MediaWikiServices $services):FileFormatFactory { return new FileFormatFactory( $services->getObjectFactory());}, 'Translate:GroupSynchronizationCache'=> static function(MediaWikiServices $services):GroupSynchronizationCache { return new GroupSynchronizationCache( $services->get( 'Translate:PersistentCache'));}, 'Translate:HookDefinedMessageGroupFactory'=> static function(MediaWikiServices $services):HookDefinedMessageGroupFactory { return new HookDefinedMessageGroupFactory( $services->get( 'Translate:HookRunner'));}, 'Translate:HookRunner'=> static function(MediaWikiServices $services):HookRunner { return new HookRunner( $services->getHookContainer());}, 'Translate:MessageBundleDependencyPurger'=> static function(MediaWikiServices $services):MessageBundleDependencyPurger { return new MessageBundleDependencyPurger( $services->get( 'Translate:TranslatableBundleFactory'));}, 'Translate:MessageBundleMessageGroupFactory'=> static function(MediaWikiServices $services):MessageBundleMessageGroupFactory { return new MessageBundleMessageGroupFactory($services->get( 'Translate:MessageGroupMetadata'), new ServiceOptions(MessageBundleMessageGroupFactory::SERVICE_OPTIONS, $services->getMainConfig()),);}, 'Translate:MessageBundleStore'=> static function(MediaWikiServices $services):MessageBundleStore { return new MessageBundleStore($services->get( 'Translate:RevTagStore'), $services->getJobQueueGroup(), $services->getLanguageNameUtils(), $services->get( 'Translate:MessageIndex'), $services->get( 'Translate:MessageGroupMetadata'));}, 'Translate:MessageBundleTranslationLoader'=> static function(MediaWikiServices $services):MessageBundleTranslationLoader { return new MessageBundleTranslationLoader( $services->getLanguageFallback());}, 'Translate:MessageGroupMetadata'=> static function(MediaWikiServices $services):MessageGroupMetadata { return new MessageGroupMetadata( $services->getConnectionProvider());}, 'Translate:MessageGroupReviewStore'=> static function(MediaWikiServices $services):MessageGroupReviewStore { return new MessageGroupReviewStore($services->getConnectionProvider(), $services->get( 'Translate:HookRunner'));}, 'Translate:MessageGroupStatsTableFactory'=> static function(MediaWikiServices $services):MessageGroupStatsTableFactory { return new MessageGroupStatsTableFactory($services->get( 'Translate:ProgressStatsTableFactory'), $services->getLinkRenderer(), $services->get( 'Translate:MessageGroupReviewStore'), $services->get( 'Translate:MessageGroupMetadata'), $services->getMainConfig() ->get( 'TranslateWorkflowStates') !==false);}, 'Translate:MessageGroupSubscription'=> static function(MediaWikiServices $services):MessageGroupSubscription { return new MessageGroupSubscription($services->get( 'Translate:MessageGroupSubscriptionStore'), $services->getJobQueueGroup(), $services->getUserIdentityLookup(), LoggerFactory::getInstance(LogNames::GROUP_SUBSCRIPTION), new ServiceOptions(MessageGroupSubscription::CONSTRUCTOR_OPTIONS, $services->getMainConfig()));}, 'Translate:MessageGroupSubscriptionHookHandler'=> static function(MediaWikiServices $services):?MessageGroupSubscriptionHookHandler { if(! $services->getExtensionRegistry() ->isLoaded( 'Echo')) { return null;} return new MessageGroupSubscriptionHookHandler($services->get( 'Translate:MessageGroupSubscription'), $services->getUserFactory());}, 'Translate:MessageGroupSubscriptionStore'=> static function(MediaWikiServices $services):MessageGroupSubscriptionStore { return new MessageGroupSubscriptionStore( $services->getConnectionProvider());}, 'Translate:MessageIndex'=> static function(MediaWikiServices $services):MessageIndex { $params=(array) $services->getMainConfig() ->get( 'TranslateMessageIndex');$class=array_shift( $params);$implementationMap=['HashMessageIndex'=> HashMessageIndex::class, 'CDBMessageIndex'=> CDBMessageIndex::class, 'DatabaseMessageIndex'=> DatabaseMessageIndex::class, 'hash'=> HashMessageIndex::class, 'cdb'=> CDBMessageIndex::class, 'database'=> DatabaseMessageIndex::class,];$messageIndexStoreClass=$implementationMap[$class] ?? $implementationMap['database'];return new MessageIndex(new $messageIndexStoreClass, $services->getMainWANObjectCache(), $services->getJobQueueGroup(), $services->get( 'Translate:HookRunner'), LoggerFactory::getInstance(LogNames::MAIN), $services->getMainObjectStash(), $services->getConnectionProvider(), new ServiceOptions(MessageIndex::SERVICE_OPTIONS, $services->getMainConfig()),);}, 'Translate:MessagePrefixStats'=> static function(MediaWikiServices $services):MessagePrefixStats { return new MessagePrefixStats( $services->getTitleParser());}, 'Translate:ParsingPlaceholderFactory'=> static function():ParsingPlaceholderFactory { return new ParsingPlaceholderFactory();}, 'Translate:PersistentCache'=> static function(MediaWikiServices $services):PersistentCache { return new PersistentDatabaseCache($services->getConnectionProvider(), $services->getJsonCodec());}, 'Translate:ProgressStatsTableFactory'=> static function(MediaWikiServices $services):ProgressStatsTableFactory { return new ProgressStatsTableFactory($services->getLinkRenderer(), $services->get( 'Translate:ConfigHelper'), $services->get( 'Translate:MessageGroupMetadata'));}, 'Translate:RevTagStore'=> static function(MediaWikiServices $services):RevTagStore { return new RevTagStore( $services->getConnectionProvider());}, 'Translate:SubpageListBuilder'=> static function(MediaWikiServices $services):SubpageListBuilder { return new SubpageListBuilder($services->get( 'Translate:TranslatableBundleFactory'), $services->getLinkBatchFactory());}, 'Translate:TranslatableBundleDeleter'=> static function(MediaWikiServices $services):TranslatableBundleDeleter { return new TranslatableBundleDeleter($services->getMainObjectStash(), $services->getJobQueueGroup(), $services->get( 'Translate:SubpageListBuilder'), $services->get( 'Translate:TranslatableBundleFactory'));}, 'Translate:TranslatableBundleExporter'=> static function(MediaWikiServices $services):TranslatableBundleExporter { return new TranslatableBundleExporter($services->get( 'Translate:SubpageListBuilder'), $services->getWikiExporterFactory(), $services->getConnectionProvider());}, 'Translate:TranslatableBundleFactory'=> static function(MediaWikiServices $services):TranslatableBundleFactory { return new TranslatableBundleFactory($services->get( 'Translate:TranslatablePageStore'), $services->get( 'Translate:MessageBundleStore'));}, 'Translate:TranslatableBundleImporter'=> static function(MediaWikiServices $services):TranslatableBundleImporter { return new TranslatableBundleImporter($services->getWikiImporterFactory(), $services->get( 'Translate:TranslatablePageParser'), $services->getRevisionLookup(), $services->getNamespaceInfo(), $services->getTitleFactory(), $services->getFormatterFactory());}, 'Translate:TranslatableBundleMover'=> static function(MediaWikiServices $services):TranslatableBundleMover { return new TranslatableBundleMover($services->getMovePageFactory(), $services->getJobQueueGroup(), $services->getLinkBatchFactory(), $services->get( 'Translate:TranslatableBundleFactory'), $services->get( 'Translate:SubpageListBuilder'), $services->getConnectionProvider(), $services->getObjectCacheFactory(), $services->getMainConfig() ->get( 'TranslatePageMoveLimit'));}, 'Translate:TranslatableBundleStatusStore'=> static function(MediaWikiServices $services):TranslatableBundleStatusStore { return new TranslatableBundleStatusStore($services->getConnectionProvider() ->getPrimaryDatabase(), $services->getCollationFactory() ->makeCollation( 'uca-default-u-kn'), $services->getDBLoadBalancer() ->getMaintenanceConnectionRef(DB_PRIMARY));}, 'Translate:TranslatablePageMarker'=> static function(MediaWikiServices $services):TranslatablePageMarker { return new TranslatablePageMarker($services->getConnectionProvider(), $services->getJobQueueGroup(), $services->getLinkRenderer(), MessageGroups::singleton(), $services->get( 'Translate:MessageIndex'), $services->getTitleFormatter(), $services->getTitleParser(), $services->get( 'Translate:TranslatablePageParser'), $services->get( 'Translate:TranslatablePageStore'), $services->get( 'Translate:TranslatablePageStateStore'), $services->get( 'Translate:TranslationUnitStoreFactory'), $services->get( 'Translate:MessageGroupMetadata'), $services->getWikiPageFactory(), $services->get( 'Translate:TranslatablePageView'), $services->get( 'Translate:MessageGroupSubscription'), $services->getFormatterFactory(), $services->get( 'Translate:HookRunner'),);}, 'Translate:TranslatablePageMessageGroupFactory'=> static function(MediaWikiServices $services):TranslatablePageMessageGroupFactory { return new TranslatablePageMessageGroupFactory(new ServiceOptions(TranslatablePageMessageGroupFactory::SERVICE_OPTIONS, $services->getMainConfig()),);}, 'Translate:TranslatablePageParser'=> static function(MediaWikiServices $services):TranslatablePageParser { return new TranslatablePageParser($services->get( 'Translate:ParsingPlaceholderFactory'));}, 'Translate:TranslatablePageStateStore'=> static function(MediaWikiServices $services):TranslatablePageStateStore { return new TranslatablePageStateStore($services->get( 'Translate:PersistentCache'), $services->getPageStore());}, 'Translate:TranslatablePageStore'=> static function(MediaWikiServices $services):TranslatablePageStore { return new TranslatablePageStore($services->get( 'Translate:MessageIndex'), $services->getJobQueueGroup(), $services->get( 'Translate:RevTagStore'), $services->getConnectionProvider(), $services->get( 'Translate:TranslatableBundleStatusStore'), $services->get( 'Translate:TranslatablePageParser'), $services->get( 'Translate:MessageGroupMetadata'));}, 'Translate:TranslatablePageView'=> static function(MediaWikiServices $services):TranslatablePageView { return new TranslatablePageView($services->getConnectionProvider(), $services->get( 'Translate:TranslatablePageStateStore'), new ServiceOptions(TranslatablePageView::SERVICE_OPTIONS, $services->getMainConfig()));}, 'Translate:TranslateSandbox'=> static function(MediaWikiServices $services):TranslateSandbox { return new TranslateSandbox($services->getUserFactory(), $services->getConnectionProvider(), $services->getPermissionManager(), $services->getAuthManager(), $services->getUserGroupManager(), $services->getActorStore(), $services->getUserOptionsManager(), $services->getJobQueueGroup(), $services->get( 'Translate:HookRunner'), new ServiceOptions(TranslateSandbox::CONSTRUCTOR_OPTIONS, $services->getMainConfig()));}, 'Translate:TranslationStashReader'=> static function(MediaWikiServices $services):TranslationStashReader { return new TranslationStashStorage( $services->getConnectionProvider() ->getPrimaryDatabase());}, 'Translate:TranslationStatsDataProvider'=> static function(MediaWikiServices $services):TranslationStatsDataProvider { return new TranslationStatsDataProvider(new ServiceOptions(TranslationStatsDataProvider::CONSTRUCTOR_OPTIONS, $services->getMainConfig()), $services->getObjectFactory(), $services->getConnectionProvider());}, 'Translate:TranslationUnitStoreFactory'=> static function(MediaWikiServices $services):TranslationUnitStoreFactory { return new TranslationUnitStoreFactory( $services->getDBLoadBalancer());}, 'Translate:TranslatorActivity'=> static function(MediaWikiServices $services):TranslatorActivity { $query=new TranslatorActivityQuery($services->getMainConfig(), $services->getConnectionProvider());return new TranslatorActivity($services->getMainObjectStash(), $query, $services->getJobQueueGroup());}, 'Translate:TtmServerFactory'=> static function(MediaWikiServices $services):TtmServerFactory { $config=$services->getMainConfig();$default=$config->get( 'TranslateTranslationDefaultService');if( $default===false) { $default=null;} return new TtmServerFactory( $config->get( 'TranslateTranslationServices'), $default);}, 'Translate:WorkflowStatesMessageGroupLoader'=> static function(MediaWikiServices $services):WorkflowStatesMessageGroupLoader { return new WorkflowStatesMessageGroupLoader(new ServiceOptions(WorkflowStatesMessageGroupLoader::CONSTRUCTOR_OPTIONS, $services->getMainConfig()),);},]
@phpcs-require-sorted-array
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.
Class for pointing to messages, like Title class is for titles.
static makeSectionElement(string $legend, string $type, string $content, ?Language $lang=null, array $extraContainerClasses=[])
Make section elements.
__construct(Title $title, private readonly IContextSource $context, $group=null, string $code='en')
Essentially random collection of helper functions, similar to GlobalFunctions.php.
Definition Utilities.php:30
Interface for message groups.
Finds external changes for file based message groups.