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;
19use RuntimeException;
21use Wikimedia\ObjectCache\WANObjectCache;
22
35 private $groups;
37 private $groupLoaders;
38 private WANObjectCache $cache;
45 private const CACHE_VERSION = 4;
46
48 protected function init(): void {
49 if ( is_array( $this->groups ) ) {
50 return; // groups already initialized
51 }
52
53 $groups = [];
54 foreach ( $this->getGroupLoaders() as $loader ) {
55 $groups += $loader->getGroups();
56 }
57 $this->initGroupsFromDefinitions( $groups );
58 }
59
65 protected function initGroupsFromDefinitions( array $groups ): void {
66 foreach ( $groups as $id => $mixed ) {
67 if ( !is_object( $mixed ) ) {
68 $groups[$id] = $mixed( $id );
69 }
70 }
71
72 $this->groups = $groups;
73 }
74
76 public function recache(): void {
77 $cache = $this->getCache();
78 $cache->touchCheckKey( $this->getCacheKey() );
79
80 $groups = [];
81 foreach ( $this->getGroupLoaders() as $loader ) {
82 if ( $loader instanceof CachedMessageGroupLoader ) {
83 $groups += $loader->recache();
84 } else {
85 $groups += $loader->getGroups();
86 }
87 }
88 $this->initGroupsFromDefinitions( $groups );
89 }
90
97 public function clearProcessCache(): void {
98 $this->groups = null;
99 $this->groupLoaders = null;
100 }
101
102 protected function getCache(): WANObjectCache {
103 return $this->cache ?? MediaWikiServices::getInstance()->getMainWANObjectCache();
104 }
105
107 public function setCache( WANObjectCache $cache ) {
108 $this->cache = $cache;
109 }
110
112 public function getCacheKey(): string {
113 return $this->getCache()->makeKey( 'translate-groups', 'v' . self::CACHE_VERSION );
114 }
115
121 protected function getGroupLoaders(): array {
122 if ( $this->groupLoaders !== null ) {
123 return $this->groupLoaders;
124 }
125
126 $services = Services::getInstance();
127 $cache = $this->getCache();
128 $dbProvider = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
129
130 $groupLoaderInstances = $this->groupLoaders = [];
131
132 // Initialize the dependencies
133 $deps = [
134 'database' => Utilities::getSafeReadDB(),
135 'cache' => $cache
136 ];
137
138 $services->getHookRunner()->onTranslateInitGroupLoaders( $groupLoaderInstances, $deps );
139
140 foreach ( $groupLoaderInstances as $loader ) {
141 if ( !$loader instanceof MessageGroupLoader ) {
142 throw new InvalidArgumentException(
143 "MessageGroupLoader - $loader must implement the " .
144 'MessageGroupLoader interface.'
145 );
146 }
147
148 $this->groupLoaders[] = $loader;
149 }
150
151 $factories = [
152 $services->getAggregateGroupMessageGroupFactory(),
153 $services->getFileBasedMessageGroupFactory(),
154 $services->getHookDefinedMessageGroupFactory(),
155 $services->getMessageBundleMessageGroupFactory(),
156 $services->getTranslatablePageMessageGroupFactory()
157 ];
158
159 foreach ( $factories as $factory ) {
160 $this->groupLoaders[] = new CachedMessageGroupFactoryLoader(
161 $cache,
162 $dbProvider,
163 $factory
164 );
165 }
166
167 // TODO: To decide if we should load this like we load other message group factories.
168 // See Ie95f25e18816f48d1d663ae392ecf5f9bbcf0b56 for related discussion
169 $this->groupLoaders[] = $services->getWorkflowStatesMessageGroupLoader();
170
171 return $this->groupLoaders;
172 }
173
180 public static function getGroup( string $id ): ?MessageGroup {
181 $groups = self::singleton()->getGroups();
182 $id = self::normalizeId( $id );
183
184 if ( isset( $groups[$id] ) ) {
185 return $groups[$id];
186 }
187
188 if ( str_starts_with( $id, '!' ) ) {
189 $dynamic = self::getDynamicGroups();
190 if ( isset( $dynamic[$id] ) ) {
191 return new $dynamic[$id];
192 }
193 }
194
195 return null;
196 }
197
199 public static function normalizeId( ?string $id ): string {
200 /* Translatable pages use spaces, but MW occasionally likes to
201 * normalize spaces to underscores */
202 $id = (string)$id;
203
204 if ( str_starts_with( $id, 'page-' ) ) {
205 $id = strtr( $id, '_', ' ' );
206 }
207
208 global $wgTranslateGroupAliases;
209 if ( isset( $wgTranslateGroupAliases[$id] ) ) {
210 $id = $wgTranslateGroupAliases[$id];
211 }
212
213 return $id;
214 }
215
216 public static function exists( string $id ): bool {
217 return (bool)self::getGroup( $id );
218 }
219
221 public static function labelExists( string $name ): bool {
222 $groups = self::getAllGroups();
223 foreach ( $groups as $group ) {
224 if ( $group instanceof AggregateMessageGroup ) {
225 if ( $group->getLabel() === $name ) {
226 return true;
227 }
228 }
229
230 }
231
232 return false;
233 }
234
239 public static function getAllGroups(): array {
240 return self::singleton()->getGroups();
241 }
242
251 public static function getPriority( MessageGroup|string $group ): string {
252 if ( $group instanceof MessageGroup ) {
253 $id = $group->getId();
254 } else {
255 $id = self::normalizeId( $group );
256 }
257
258 return Services::getInstance()->getMessageGroupReviewStore()->getGroupPriority( $id ) ?? '';
259 }
260
267 public static function setPriority( MessageGroup|string $group, string $priority = '' ): void {
268 if ( $group instanceof MessageGroup ) {
269 $id = $group->getId();
270 } else {
271 $id = self::normalizeId( $group );
272 }
273
274 $priority = $priority === '' ? null : $priority;
275 Services::getInstance()->getMessageGroupReviewStore()->setGroupPriority( $id, $priority );
276 }
277
278 public static function isDynamic( MessageGroup $group ): bool {
279 $id = $group->getId();
280
281 return ( $id[0] ?? null ) === '!';
282 }
283
288 public static function getSharedGroups( MessageGroup $group ): array {
289 // Take the first message, get a handle for it and check
290 // if that message belongs to other groups. Those are the
291 // parent aggregate groups. Ideally we loop over all keys,
292 // but this should be enough.
293 $keys = array_keys( $group->getDefinitions() );
294 $title = Title::makeTitle( $group->getNamespace(), $keys[0] );
295 $handle = new MessageHandle( $title );
296 $ids = $handle->getGroupIds();
297 foreach ( $ids as $index => $id ) {
298 if ( $id === $group->getId() ) {
299 unset( $ids[$index] );
300 break;
301 }
302 }
303
304 return $ids;
305 }
306
311 public static function getParentGroups( MessageGroup $targetGroup ): array {
312 $ids = self::getSharedGroups( $targetGroup );
313 if ( $ids === [] ) {
314 return [];
315 }
316
317 $targetId = $targetGroup->getId();
318
319 /* Get the group structure. We will be using this to find which
320 * of our candidates are top-level groups. Prefilter it to only
321 * contain aggregate groups. */
322 $structure = self::getGroupStructure();
323 foreach ( $structure as $index => $group ) {
324 if ( $group instanceof MessageGroup ) {
325 unset( $structure[$index] );
326 } else {
327 $structure[$index] = array_shift( $group );
328 }
329 }
330
331 /* Now that we have all related groups, use them to find all paths
332 * from top-level groups to target group with any number of subgroups
333 * in between. */
334 $paths = [];
335
336 /* This function recursively finds paths to the target group */
337 $pathFinder = static function ( &$paths, $group, $targetId, $prefix = '' )
338 use ( &$pathFinder ) {
339 if ( $group instanceof AggregateMessageGroup ) {
340 foreach ( $group->getGroups() as $subgroup ) {
341 $subId = $subgroup->getId();
342 if ( $subId === $targetId ) {
343 $paths[] = $prefix;
344 continue;
345 }
346
347 $pathFinder( $paths, $subgroup, $targetId, "$prefix|$subId" );
348 }
349 }
350 };
351
352 // Iterate over the top-level groups only
353 foreach ( $ids as $id ) {
354 // First, find a top level groups
355 $group = self::getGroup( $id );
356
357 // Quick escape for leaf groups
358 if ( !$group instanceof AggregateMessageGroup ) {
359 continue;
360 }
361
362 foreach ( $structure as $rootGroup ) {
364 if ( $rootGroup->getId() === $group->getId() ) {
365 // Yay we found a top-level group
366 $pathFinder( $paths, $rootGroup, $targetId, $id );
367 break; // No we have one or more paths appended into $paths
368 }
369 }
370 }
371
372 // And finally explode the strings
373 return array_map( static function ( string $pathString ): array {
374 return explode( '|', $pathString );
375 }, $paths );
376 }
377
378 public static function singleton(): self {
379 static $instance;
380 if ( !$instance instanceof self ) {
381 $instance = new self();
382 }
383
384 return $instance;
385 }
386
392 public function getGroups(): array {
393 $this->init();
394
395 return $this->groups;
396 }
397
406 public static function getGroupsById( array $ids, bool $skipMeta = false ): array {
407 $groups = [];
408 foreach ( $ids as $id ) {
409 $group = self::getGroup( $id );
410
411 if ( $group !== null ) {
412 if ( $skipMeta && $group->isMeta() ) {
413 continue;
414 } else {
415 $groups[$id] = $group;
416 }
417 } else {
418 wfDebug( __METHOD__ . ": Invalid message group id: $id\n" );
419 }
420 }
421
422 return $groups;
423 }
424
432 public static function expandWildcards( $ids ): array {
433 $all = [];
434
435 $ids = (array)$ids;
436 foreach ( $ids as $index => $id ) {
437 // Fast path, no wildcards
438 if ( strcspn( $id, '*?' ) === strlen( $id ) ) {
439 $g = self::getGroup( $id );
440 if ( $g ) {
441 $all[] = $g->getId();
442 }
443 unset( $ids[$index] );
444 }
445 }
446
447 if ( $ids === [] ) {
448 return $all;
449 }
450
451 // Slow path for the ones with wildcards
452 $matcher = new StringMatcher( '', $ids );
453 foreach ( self::getAllGroups() as $id => $_ ) {
454 if ( $matcher->matches( $id ) ) {
455 $all[] = $id;
456 }
457 }
458
459 return $all;
460 }
461
467 public static function getDynamicGroups(): array {
468 return [
469 '!recent' => RecentMessageGroup::class,
470 '!additions' => RecentAdditionsMessageGroup::class,
471 '!sandbox' => SandboxMessageGroup::class,
472 ];
473 }
474
482 public static function getGroupsByType( string $type ): array {
483 $groups = self::getAllGroups();
484 foreach ( $groups as $id => $group ) {
485 if ( !$group instanceof $type ) {
486 unset( $groups[$id] );
487 }
488 }
489
490 // @phan-suppress-next-line PhanTypeMismatchReturn
491 return $groups;
492 }
493
503 public static function getGroupStructure(): array {
504 $groups = self::getAllGroups();
505
506 // Determine the top level groups of the tree
507 $tree = $groups;
508 foreach ( $groups as $id => $o ) {
509 if ( !$o->exists() ) {
510 unset( $groups[$id], $tree[$id] );
511 continue;
512 }
513
514 if ( $o instanceof AggregateMessageGroup ) {
515 foreach ( $o->getGroups() as $sid => $so ) {
516 unset( $tree[$sid] );
517 }
518 }
519 }
520
521 uasort( $tree, [ self::class, 'groupLabelSort' ] );
522
523 /* Now we have two things left in $tree array:
524 * - solitaries: top-level non-aggregate message groups
525 * - top-level aggregate message groups */
526 foreach ( $tree as $index => $group ) {
527 if ( $group instanceof AggregateMessageGroup ) {
528 $tree[$index] = self::subGroups( $group );
529 }
530 }
531
532 /* Essentially we are done now. Cyclic groups can cause part of the
533 * groups not be included at all, because they have all unset each
534 * other in the first loop. So now we check if there are groups left
535 * over. */
536 $used = [];
537 array_walk_recursive(
538 $tree,
539 static function ( MessageGroup $group ) use ( &$used ) {
540 $used[$group->getId()] = true;
541 }
542 );
543 $unused = array_diff_key( $groups, $used );
544 if ( $unused ) {
545 foreach ( $unused as $index => $group ) {
546 if ( !$group instanceof AggregateMessageGroup ) {
547 unset( $unused[$index] );
548 }
549 }
550
551 // Only list the aggregate groups, other groups cannot cause cycles
552 $participants = implode( ', ', array_keys( $unused ) );
553 throw new RuntimeException( "Found cyclic aggregate message groups: $participants" );
554 }
555
556 return $tree;
557 }
558
560 public static function groupLabelSort( MessageGroup $a, MessageGroup $b ): int {
561 $al = $a->getLabel();
562 $bl = $b->getLabel();
563
564 return strcasecmp( $al, $bl );
565 }
566
576 public static function subGroups(
577 AggregateMessageGroup $parent,
578 array &$childIds = [],
579 string $fname = 'caller'
580 ): array {
581 static $recursionGuard = [];
582
583 $pid = $parent->getId();
584 if ( isset( $recursionGuard[$pid] ) ) {
585 $tid = $pid;
586 $path = [ $tid ];
587 do {
588 $tid = $recursionGuard[$tid];
589 $path[] = $tid;
590 // Until we have gone full cycle
591 } while ( $tid !== $pid );
592 $path = implode( ' > ', $path );
593 throw new RuntimeException( "Found cyclic aggregate message groups: $path" );
594 }
595
596 // We don't care about the ids.
597 $tree = array_values( $parent->getGroups() );
598 usort( $tree, [ self::class, 'groupLabelSort' ] );
599 // Expand aggregate groups (if any left) after sorting to form a tree
600 foreach ( $tree as $index => $group ) {
601 if ( $group instanceof AggregateMessageGroup ) {
602 $sid = $group->getId();
603 $recursionGuard[$pid] = $sid;
604 $tree[$index] = self::subGroups( $group, $childIds, __METHOD__ );
605 unset( $recursionGuard[$pid] );
606
607 $childIds[$sid] = 1;
608 }
609 }
610
611 // Parent group must be first item in the array
612 array_unshift( $tree, $parent );
613
614 if ( $fname !== __METHOD__ ) {
615 // Move the IDs from the keys to the value for final return
616 $childIds = array_values( $childIds );
617 }
618
619 return $tree;
620 }
621
627 public static function haveSingleSourceLanguage( array $groups ): string {
628 $seen = '';
629
630 foreach ( $groups as $group ) {
631 $language = $group->getSourceLanguage();
632 if ( $seen === '' ) {
633 $seen = $language;
634 } elseif ( $language !== $seen ) {
635 return '';
636 }
637 }
638
639 return $seen;
640 }
641
650 public static function isTranslatableMessage( MessageHandle $handle, string $targetLanguage ): bool {
651 static $cache = [];
652
653 if ( !$handle->isValid() ) {
654 return false;
655 }
656
657 $group = $handle->getGroup();
658 $groupId = $group->getId();
659 $cacheKey = "$groupId:$targetLanguage";
660
661 if ( !isset( $cache[$cacheKey] ) ) {
662 $supportedLanguages = Utilities::getLanguageNames( 'en' );
663 $inclusionList = $group->getTranslatableLanguages() ?? $supportedLanguages;
664
665 $included = isset( $inclusionList[$targetLanguage] );
666 $excluded = Services::getInstance()->getMessageGroupMetadata()->isExcluded( $groupId, $targetLanguage );
667
668 $cache[$cacheKey] = [
669 'relevant' => $included && !$excluded,
670 'tags' => [],
671 ];
672
673 $groupTags = $group->getTags();
674 foreach ( [ 'ignored', 'optional' ] as $tag ) {
675 if ( isset( $groupTags[$tag] ) ) {
676 foreach ( $groupTags[$tag] as $key ) {
677 // TODO: ucfirst should not be here
678 $cache[$cacheKey]['tags'][ucfirst( $key )] = true;
679 }
680 }
681 }
682 }
683
684 return $cache[$cacheKey]['relevant'] &&
685 !isset( $cache[$cacheKey]['tags'][ucfirst( $handle->getKey() )] );
686 }
687
689 public function overrideGroupsForTesting( array $groups ): void {
690 $this->groups = $groups;
691 }
692}
693
694class_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->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
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 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 setPriority(MessageGroup|string $group, string $priority='')
Sets the message group priority.
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 getPriority(MessageGroup|string $group)
We want to de-emphasize time sensitive groups like news for 2009.
static groupLabelSort(MessageGroup $a, MessageGroup $b)
Sorts groups by label value.
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).