Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
MessageGroups.php
1<?php
2declare( strict_types = 1 );
3
4namespace MediaWiki\Extension\Translate\MessageGroupProcessing;
5
8use InvalidArgumentException;
13use MediaWiki\MediaWikiServices;
14use MediaWiki\Title\Title;
15use MessageGroup;
17use RuntimeException;
18use Wikimedia\ObjectCache\WANObjectCache;
19
32 private $groups;
34 private $groupLoaders;
35 private WANObjectCache $cache;
42 private const CACHE_VERSION = 4;
43
45 protected function init(): void {
46 if ( is_array( $this->groups ) ) {
47 return; // groups already initialized
48 }
49
50 $groups = [];
51 foreach ( $this->getGroupLoaders() as $loader ) {
52 $groups += $loader->getGroups();
53 }
54 $this->initGroupsFromDefinitions( $groups );
55 }
56
62 protected function initGroupsFromDefinitions( array $groups ): void {
63 foreach ( $groups as $id => $mixed ) {
64 if ( !is_object( $mixed ) ) {
65 $groups[$id] = $mixed( $id );
66 }
67 }
68
69 $this->groups = $groups;
70 }
71
73 public function recache(): void {
74 $cache = $this->getCache();
75 $cache->touchCheckKey( $this->getCacheKey() );
76
77 $groups = [];
78 foreach ( $this->getGroupLoaders() as $loader ) {
79 if ( $loader instanceof CachedMessageGroupLoader ) {
80 $groups += $loader->recache();
81 } else {
82 $groups += $loader->getGroups();
83 }
84 }
85 $this->initGroupsFromDefinitions( $groups );
86 }
87
94 public function clearProcessCache(): void {
95 $this->groups = null;
96 $this->groupLoaders = null;
97 }
98
99 protected function getCache(): WANObjectCache {
100 return $this->cache ?? MediaWikiServices::getInstance()->getMainWANObjectCache();
101 }
102
104 public function setCache( WANObjectCache $cache ) {
105 $this->cache = $cache;
106 }
107
109 public function getCacheKey(): string {
110 return $this->getCache()->makeKey( 'translate-groups', 'v' . self::CACHE_VERSION );
111 }
112
118 protected function getGroupLoaders(): array {
119 if ( $this->groupLoaders !== null ) {
120 return $this->groupLoaders;
121 }
122
123 $services = Services::getInstance();
124 $cache = $this->getCache();
125 $dbProvider = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
126
127 $groupLoaderInstances = $this->groupLoaders = [];
128
129 // Initialize the dependencies
130 $deps = [
131 'database' => Utilities::getSafeReadDB(),
132 'cache' => $cache
133 ];
134
135 $services->getHookRunner()->onTranslateInitGroupLoaders( $groupLoaderInstances, $deps );
136
137 foreach ( $groupLoaderInstances as $loader ) {
138 if ( !$loader instanceof MessageGroupLoader ) {
139 throw new InvalidArgumentException(
140 "MessageGroupLoader - $loader must implement the " .
141 'MessageGroupLoader interface.'
142 );
143 }
144
145 $this->groupLoaders[] = $loader;
146 }
147
148 $factories = [
149 $services->getAggregateGroupMessageGroupFactory(),
150 $services->getFileBasedMessageGroupFactory(),
151 $services->getHookDefinedMessageGroupFactory(),
152 $services->getMessageBundleMessageGroupFactory(),
153 $services->getTranslatablePageMessageGroupFactory()
154 ];
155
156 foreach ( $factories as $factory ) {
157 $this->groupLoaders[] = new CachedMessageGroupFactoryLoader(
158 $cache,
159 $dbProvider,
160 $factory
161 );
162 }
163
164 // TODO: To decide if we should load this like we load other message group factories.
165 // See Ie95f25e18816f48d1d663ae392ecf5f9bbcf0b56 for related discussion
166 $this->groupLoaders[] = $services->getWorkflowStatesMessageGroupLoader();
167
168 return $this->groupLoaders;
169 }
170
177 public static function getGroup( string $id ): ?MessageGroup {
178 $groups = self::singleton()->getGroups();
179 $id = self::normalizeId( $id );
180
181 if ( isset( $groups[$id] ) ) {
182 return $groups[$id];
183 }
184
185 if ( $id !== '' && $id[0] === '!' ) {
186 $dynamic = self::getDynamicGroups();
187 if ( isset( $dynamic[$id] ) ) {
188 return new $dynamic[$id];
189 }
190 }
191
192 return null;
193 }
194
196 public static function normalizeId( ?string $id ): string {
197 /* Translatable pages use spaces, but MW occasionally likes to
198 * normalize spaces to underscores */
199 $id = (string)$id;
200
201 if ( str_starts_with( $id, 'page-' ) ) {
202 $id = strtr( $id, '_', ' ' );
203 }
204
205 global $wgTranslateGroupAliases;
206 if ( isset( $wgTranslateGroupAliases[$id] ) ) {
207 $id = $wgTranslateGroupAliases[$id];
208 }
209
210 return $id;
211 }
212
213 public static function exists( string $id ): bool {
214 return (bool)self::getGroup( $id );
215 }
216
218 public static function labelExists( string $name ): bool {
219 $groups = self::getAllGroups();
220 foreach ( $groups as $group ) {
221 if ( $group instanceof AggregateMessageGroup ) {
222 if ( $group->getLabel() === $name ) {
223 return true;
224 }
225 }
226
227 }
228
229 return false;
230 }
231
236 public static function getAllGroups(): array {
237 return self::singleton()->getGroups();
238 }
239
248 public static function getPriority( $group ): string {
249 if ( $group instanceof MessageGroup ) {
250 $id = $group->getId();
251 } else {
252 $id = self::normalizeId( $group );
253 }
254
255 return Services::getInstance()->getMessageGroupReviewStore()->getGroupPriority( $id ) ?? '';
256 }
257
264 public static function setPriority( $group, string $priority = '' ): void {
265 if ( $group instanceof MessageGroup ) {
266 $id = $group->getId();
267 } else {
268 $id = self::normalizeId( $group );
269 }
270
271 $priority = $priority === '' ? null : $priority;
272 Services::getInstance()->getMessageGroupReviewStore()->setGroupPriority( $id, $priority );
273 }
274
275 public static function isDynamic( MessageGroup $group ): bool {
276 $id = $group->getId();
277
278 return ( $id[0] ?? null ) === '!';
279 }
280
285 public static function getSharedGroups( MessageGroup $group ): array {
286 // Take the first message, get a handle for it and check
287 // if that message belongs to other groups. Those are the
288 // parent aggregate groups. Ideally we loop over all keys,
289 // but this should be enough.
290 $keys = array_keys( $group->getDefinitions() );
291 $title = Title::makeTitle( $group->getNamespace(), $keys[0] );
292 $handle = new MessageHandle( $title );
293 $ids = $handle->getGroupIds();
294 foreach ( $ids as $index => $id ) {
295 if ( $id === $group->getId() ) {
296 unset( $ids[$index] );
297 break;
298 }
299 }
300
301 return $ids;
302 }
303
308 public static function getParentGroups( MessageGroup $targetGroup ): array {
309 $ids = self::getSharedGroups( $targetGroup );
310 if ( $ids === [] ) {
311 return [];
312 }
313
314 $targetId = $targetGroup->getId();
315
316 /* Get the group structure. We will be using this to find which
317 * of our candidates are top-level groups. Prefilter it to only
318 * contain aggregate groups. */
319 $structure = self::getGroupStructure();
320 foreach ( $structure as $index => $group ) {
321 if ( $group instanceof MessageGroup ) {
322 unset( $structure[$index] );
323 } else {
324 $structure[$index] = array_shift( $group );
325 }
326 }
327
328 /* Now that we have all related groups, use them to find all paths
329 * from top-level groups to target group with any number of subgroups
330 * in between. */
331 $paths = [];
332
333 /* This function recursively finds paths to the target group */
334 $pathFinder = static function ( &$paths, $group, $targetId, $prefix = '' )
335 use ( &$pathFinder ) {
336 if ( $group instanceof AggregateMessageGroup ) {
337 foreach ( $group->getGroups() as $subgroup ) {
338 $subId = $subgroup->getId();
339 if ( $subId === $targetId ) {
340 $paths[] = $prefix;
341 continue;
342 }
343
344 $pathFinder( $paths, $subgroup, $targetId, "$prefix|$subId" );
345 }
346 }
347 };
348
349 // Iterate over the top-level groups only
350 foreach ( $ids as $id ) {
351 // First, find a top level groups
352 $group = self::getGroup( $id );
353
354 // Quick escape for leaf groups
355 if ( !$group instanceof AggregateMessageGroup ) {
356 continue;
357 }
358
359 foreach ( $structure as $rootGroup ) {
361 if ( $rootGroup->getId() === $group->getId() ) {
362 // Yay we found a top-level group
363 $pathFinder( $paths, $rootGroup, $targetId, $id );
364 break; // No we have one or more paths appended into $paths
365 }
366 }
367 }
368
369 // And finally explode the strings
370 return array_map( static function ( string $pathString ): array {
371 return explode( '|', $pathString );
372 }, $paths );
373 }
374
375 public static function singleton(): self {
376 static $instance;
377 if ( !$instance instanceof self ) {
378 $instance = new self();
379 }
380
381 return $instance;
382 }
383
389 public function getGroups(): array {
390 $this->init();
391
392 return $this->groups;
393 }
394
403 public static function getGroupsById( array $ids, bool $skipMeta = false ): array {
404 $groups = [];
405 foreach ( $ids as $id ) {
406 $group = self::getGroup( $id );
407
408 if ( $group !== null ) {
409 if ( $skipMeta && $group->isMeta() ) {
410 continue;
411 } else {
412 $groups[$id] = $group;
413 }
414 } else {
415 wfDebug( __METHOD__ . ": Invalid message group id: $id\n" );
416 }
417 }
418
419 return $groups;
420 }
421
429 public static function expandWildcards( $ids ): array {
430 $all = [];
431
432 $ids = (array)$ids;
433 foreach ( $ids as $index => $id ) {
434 // Fast path, no wildcards
435 if ( strcspn( $id, '*?' ) === strlen( $id ) ) {
436 $g = self::getGroup( $id );
437 if ( $g ) {
438 $all[] = $g->getId();
439 }
440 unset( $ids[$index] );
441 }
442 }
443
444 if ( $ids === [] ) {
445 return $all;
446 }
447
448 // Slow path for the ones with wildcards
449 $matcher = new StringMatcher( '', $ids );
450 foreach ( self::getAllGroups() as $id => $_ ) {
451 if ( $matcher->matches( $id ) ) {
452 $all[] = $id;
453 }
454 }
455
456 return $all;
457 }
458
460 public static function getDynamicGroups(): array {
461 return [
462 '!recent' => 'RecentMessageGroup',
463 '!additions' => 'RecentAdditionsMessageGroup',
464 '!sandbox' => 'SandboxMessageGroup',
465 ];
466 }
467
475 public static function getGroupsByType( string $type ): array {
476 $groups = self::getAllGroups();
477 foreach ( $groups as $id => $group ) {
478 if ( !$group instanceof $type ) {
479 unset( $groups[$id] );
480 }
481 }
482
483 // @phan-suppress-next-line PhanTypeMismatchReturn
484 return $groups;
485 }
486
496 public static function getGroupStructure(): array {
497 $groups = self::getAllGroups();
498
499 // Determine the top level groups of the tree
500 $tree = $groups;
501 foreach ( $groups as $id => $o ) {
502 if ( !$o->exists() ) {
503 unset( $groups[$id], $tree[$id] );
504 continue;
505 }
506
507 if ( $o instanceof AggregateMessageGroup ) {
508 foreach ( $o->getGroups() as $sid => $so ) {
509 unset( $tree[$sid] );
510 }
511 }
512 }
513
514 uasort( $tree, [ self::class, 'groupLabelSort' ] );
515
516 /* Now we have two things left in $tree array:
517 * - solitaries: top-level non-aggregate message groups
518 * - top-level aggregate message groups */
519 foreach ( $tree as $index => $group ) {
520 if ( $group instanceof AggregateMessageGroup ) {
521 $tree[$index] = self::subGroups( $group );
522 }
523 }
524
525 /* Essentially we are done now. Cyclic groups can cause part of the
526 * groups not be included at all, because they have all unset each
527 * other in the first loop. So now we check if there are groups left
528 * over. */
529 $used = [];
530 array_walk_recursive(
531 $tree,
532 static function ( MessageGroup $group ) use ( &$used ) {
533 $used[$group->getId()] = true;
534 }
535 );
536 $unused = array_diff_key( $groups, $used );
537 if ( $unused ) {
538 foreach ( $unused as $index => $group ) {
539 if ( !$group instanceof AggregateMessageGroup ) {
540 unset( $unused[$index] );
541 }
542 }
543
544 // Only list the aggregate groups, other groups cannot cause cycles
545 $participants = implode( ', ', array_keys( $unused ) );
546 throw new RuntimeException( "Found cyclic aggregate message groups: $participants" );
547 }
548
549 return $tree;
550 }
551
553 public static function groupLabelSort( MessageGroup $a, MessageGroup $b ): int {
554 $al = $a->getLabel();
555 $bl = $b->getLabel();
556
557 return strcasecmp( $al, $bl );
558 }
559
569 public static function subGroups(
570 AggregateMessageGroup $parent,
571 array &$childIds = [],
572 string $fname = 'caller'
573 ): array {
574 static $recursionGuard = [];
575
576 $pid = $parent->getId();
577 if ( isset( $recursionGuard[$pid] ) ) {
578 $tid = $pid;
579 $path = [ $tid ];
580 do {
581 $tid = $recursionGuard[$tid];
582 $path[] = $tid;
583 // Until we have gone full cycle
584 } while ( $tid !== $pid );
585 $path = implode( ' > ', $path );
586 throw new RuntimeException( "Found cyclic aggregate message groups: $path" );
587 }
588
589 // We don't care about the ids.
590 $tree = array_values( $parent->getGroups() );
591 usort( $tree, [ self::class, 'groupLabelSort' ] );
592 // Expand aggregate groups (if any left) after sorting to form a tree
593 foreach ( $tree as $index => $group ) {
594 if ( $group instanceof AggregateMessageGroup ) {
595 $sid = $group->getId();
596 $recursionGuard[$pid] = $sid;
597 $tree[$index] = self::subGroups( $group, $childIds, __METHOD__ );
598 unset( $recursionGuard[$pid] );
599
600 $childIds[$sid] = 1;
601 }
602 }
603
604 // Parent group must be first item in the array
605 array_unshift( $tree, $parent );
606
607 if ( $fname !== __METHOD__ ) {
608 // Move the IDs from the keys to the value for final return
609 $childIds = array_values( $childIds );
610 }
611
612 return $tree;
613 }
614
620 public static function haveSingleSourceLanguage( array $groups ): string {
621 $seen = '';
622
623 foreach ( $groups as $group ) {
624 $language = $group->getSourceLanguage();
625 if ( $seen === '' ) {
626 $seen = $language;
627 } elseif ( $language !== $seen ) {
628 return '';
629 }
630 }
631
632 return $seen;
633 }
634
643 public static function isTranslatableMessage( MessageHandle $handle, string $targetLanguage ): bool {
644 static $cache = [];
645
646 if ( !$handle->isValid() ) {
647 return false;
648 }
649
650 $group = $handle->getGroup();
651 $groupId = $group->getId();
652 $cacheKey = "$groupId:$targetLanguage";
653
654 if ( !isset( $cache[$cacheKey] ) ) {
655 $supportedLanguages = Utilities::getLanguageNames( 'en' );
656 $inclusionList = $group->getTranslatableLanguages() ?? $supportedLanguages;
657
658 $included = isset( $inclusionList[$targetLanguage] );
659 $excluded = Services::getInstance()->getMessageGroupMetadata()->isExcluded( $groupId, $targetLanguage );
660
661 $cache[$cacheKey] = [
662 'relevant' => $included && !$excluded,
663 'tags' => [],
664 ];
665
666 $groupTags = $group->getTags();
667 foreach ( [ 'ignored', 'optional' ] as $tag ) {
668 if ( isset( $groupTags[$tag] ) ) {
669 foreach ( $groupTags[$tag] as $key ) {
670 // TODO: ucfirst should not be here
671 $cache[$cacheKey]['tags'][ucfirst( $key )] = true;
672 }
673 }
674 }
675 }
676
677 return $cache[$cacheKey]['relevant'] &&
678 !isset( $cache[$cacheKey]['tags'][ucfirst( $handle->getKey() )] );
679 }
680
682 public function overrideGroupsForTesting( array $groups ): void {
683 $this->groups = $groups;
684 }
685}
686
687class_alias( MessageGroups::class, 'MessageGroups' );
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->getDBLoadBalancer());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
Groups multiple message groups together as one group.
getGroups()
Returns a list of message groups that this group consists of.
Factory class for accessing message groups individually by id or all of them as a list.
static getGroup(string $id)
Fetch a message group by id.
static getPriority( $group)
We want to de-emphasize time sensitive groups like news for 2009.
static getDynamicGroups()
Contents on these groups changes on a whim.
static getGroupsByType(string $type)
Get only groups of specific type (class).
static getSharedGroups(MessageGroup $group)
Returns a list of message groups that share (certain) messages with this group.
static getGroupsById(array $ids, bool $skipMeta=false)
Get message groups for corresponding message group ids.
static isTranslatableMessage(MessageHandle $handle, string $targetLanguage)
Filters out messages that should not be translated under normal conditions.
static getGroupStructure()
Returns a tree of message groups.
static normalizeId(?string $id)
Fixes the id and resolves aliases.
setCache(WANObjectCache $cache)
Override cache, for example during tests.
getGroups()
Get all enabled non-dynamic message groups.
static subGroups(AggregateMessageGroup $parent, array &$childIds=[], string $fname='caller')
Like getGroupStructure but start from one root which must be an AggregateMessageGroup.
static haveSingleSourceLanguage(array $groups)
Checks whether all the message groups have the same source language.
static labelExists(string $name)
Check if a particular aggregate group label exists.
static expandWildcards( $ids)
If the list of message group ids contains wildcards, this function will match them against the list o...
static groupLabelSort(MessageGroup $a, MessageGroup $b)
Sorts groups by label value.
static setPriority( $group, string $priority='')
Sets the message group priority.
initGroupsFromDefinitions(array $groups)
Expand process cached groups to objects.
recache()
Clear message group caches and populate message groups from uncached data.
static getParentGroups(MessageGroup $targetGroup)
Returns a list of parent message groups.
Class for pointing to messages, like Title class is for titles.
getKey()
Returns the identified or guessed message key.
getGroup()
Get the primary MessageGroup this message belongs to.
isValid()
Checks if the handle corresponds to a known message.
The versatile default implementation of StringMangler interface.
Minimal service container.
Definition Services.php:60
Essentially random collection of helper functions, similar to GlobalFunctions.php.
Definition Utilities.php:29
Interface for MessageGroupFactories that use caching.
Interface for message group loaders.
Interface for message groups.
getTranslatableLanguages()
Get all the translatable languages for a group, considering the inclusion and exclusion list.
getTags( $type=null)
Returns message tags.
getNamespace()
Returns the namespace where messages are placed.
getId()
Returns the unique identifier for this group.
getLabel(?IContextSource $context=null)
Returns the human readable label (as plain text).