51 public const DISPLAY_TITLE_UNIT_ID =
'Page display title';
53 protected PageIdentity $title;
62 protected $pageDisplayTitle;
64 private $targetLanguage;
66 protected function __construct( PageIdentity $title ) {
67 $this->title = $title;
68 $this->revTagStore = Services::getInstance()->getRevTagStore();
76 public static function newFromText( Title $title,
string $text ): self {
77 $obj = new self( $title );
79 $obj->source =
'text';
89 public static function newFromRevision( PageIdentity $title,
int $revision ): self {
90 $rev = MediaWikiServices::getInstance()
92 ->getRevisionByTitle( $title, $revision );
93 if ( $rev ===
null ) {
94 throw new RuntimeException(
'Revision is null' );
97 $obj =
new self( $title );
98 $obj->source =
'revision';
99 $obj->revision = $revision;
109 $obj = new self( $title );
110 $obj->source =
'title';
117 return Title::castFromPageIdentity( $this->title );
120 public function getPageIdentity(): PageIdentity {
126 if ( $this->text !== null ) {
130 if ( $this->source ===
'title' ) {
131 $revision = $this->getMarkedTag();
132 if ( !is_int( $revision ) ) {
133 throw new LogicException(
134 "Trying to load a text for {$this->getPageIdentity()} which is not marked for translation"
137 $this->revision = $revision;
140 $flags = Utilities::shouldReadFromPrimary()
141 ? IDBAccessObject::READ_LATEST
142 : IDBAccessObject::READ_NORMAL;
143 $rev = MediaWikiServices::getInstance()
144 ->getRevisionLookup()
145 ->getRevisionByTitle( $this->getPageIdentity(), $this->revision, $flags );
146 $content = $rev->getContent( SlotRecord::MAIN );
147 $text = ( $content instanceof TextContent ) ? $content->getText() :
null;
149 if ( !is_string( $text ) ) {
150 throw new RuntimeException(
"Failed to load text for {$this->getPageIdentity()}" );
172 return $this->getTitle()->getPageLanguage()->getCode();
177 return self::getMessageGroupIdFromTitle( $this->getPageIdentity() );
182 return 'page-' . MediaWikiServices::getInstance()->getTitleFormatter()->getPrefixedText( $page );
190 $groupId = $this->getMessageGroupId();
191 $group = MessageGroups::getGroup( $groupId );
196 throw new RuntimeException(
197 "Expected $groupId to be of type WikiPageMessageGroup; got " .
205 if ( $this->pageDisplayTitle !== null ) {
206 return $this->pageDisplayTitle;
210 $factory = Services::getInstance()->getTranslationUnitStoreFactory();
211 $store = $factory->getReader( $this->getPageIdentity() );
212 $this->pageDisplayTitle = in_array( self::DISPLAY_TITLE_UNIT_ID, $store->getNames() );
214 return $this->pageDisplayTitle;
220 if ( !$this->hasPageDisplayTitle() ) {
225 $section = str_replace(
' ',
'_', self::DISPLAY_TITLE_UNIT_ID );
226 $page = MediaWikiServices::getInstance()->getTitleFormatter()->getPrefixedDBkey( $this->getPageIdentity() );
229 $group = $this->getMessageGroup();
230 }
catch ( RuntimeException $e ) {
239 return $group->getMessage(
"$page/$section", $languageCode, IDBAccessObject::READ_NORMAL );
242 public function getStrippedSourcePageText(): string {
243 $parser = Services::getInstance()->getTranslatablePageParser();
244 $text = $parser->cleanupTags( $this->getText() );
245 $text = preg_replace(
'~<languages\s*/>\n?~s',
'', $text );
250 public static function getTranslationPageFromTitle( Title $title ): ?TranslationPage {
251 $self = self::isTranslationPage( $title );
252 return $self ? $self->getTranslationPage( $self->targetLanguage ) :
null;
255 public function getTranslationPage(
string $targetLanguage ): TranslationPage {
256 $mwServices = MediaWikiServices::getInstance();
257 $config = $mwServices->getMainConfig();
258 $services = Services::getInstance();
259 $parser = $services->getTranslatablePageParser();
260 $parserOutput = $parser->parse( $this->getText() );
261 $pageVersion = (int)$services->getMessageGroupMetadata()
262 ->get( $this->getMessageGroupId(),
'version' );
263 $wrapUntranslated = $pageVersion >= 2;
264 $languageFactory = $mwServices->getLanguageFactory();
266 return new TranslationPage(
268 $this->getMessageGroup(),
269 $languageFactory->getLanguage( $targetLanguage ),
270 $languageFactory->getLanguage( $this->getSourceLanguageCode() ),
271 $config->get(
'TranslateKeepOutdatedTranslations' ),
279 $this->revTagStore->replaceTag( $this->getPageIdentity(), RevTagStore::TP_MARK_TAG, $revision, $value );
280 self::clearSourcePageCache();
285 $this->revTagStore->replaceTag( $this->getPageIdentity(),
RevTagStore::TP_READY_TAG, $revision );
286 if ( !self::isSourcePage( $this->getPageIdentity() ) ) {
287 self::clearSourcePageCache();
293 return $this->revTagStore->getLatestRevisionWithTag( $this->getPageIdentity(),
RevTagStore::TP_MARK_TAG );
298 return $this->revTagStore->getLatestRevisionWithTag( $this->getPageIdentity(),
RevTagStore::TP_READY_TAG );
308 'group' => $this->getMessageGroupId(),
314 $translate = SpecialPage::getTitleFor(
'Translate' );
316 return $translate->getLocalURL( $params );
319 public function getMarkedRevs(): IResultWrapper {
322 return $db->newSelectQueryBuilder()
323 ->select( [
'rt_revision',
'rt_value' ] )
326 'rt_page' => $this->getPageIdentity()->getId(),
327 'rt_type' => RevTagStore::TP_MARK_TAG,
329 ->orderBy(
'rt_revision', SelectQueryBuilder::SORT_DESC )
330 ->caller( __METHOD__ )
336 $mwServices = MediaWikiServices::getInstance();
338 $messageGroup = $this->getMessageGroup();
339 $knownLanguageCodes = $messageGroup ? $messageGroup->getTranslatableLanguages() :
null;
340 $knownLanguageCodes ??= Utilities::getLanguageNames( LanguageNameUtils::AUTONYMS );
342 $prefixedDbTitleKey = $this->getPageIdentity()->getDBkey() .
'/';
343 $baseNamespace = $this->getPageIdentity()->getNamespace();
346 $linkBatch = $mwServices->getLinkBatchFactory()->newLinkBatch();
347 foreach ( array_keys( $knownLanguageCodes ) as $code ) {
348 $linkBatch->add( $baseNamespace, $prefixedDbTitleKey . $code );
351 $translationPages = [];
352 foreach ( $linkBatch->getPageIdentities() as $pageIdentity ) {
353 if ( $pageIdentity->exists() ) {
354 $translationPages[] = Title::castFromPageIdentity( $pageIdentity );
358 return $translationPages;
363 return $this->getTranslationUnitPagesByTitle( $this->title, $code );
366 public function getTranslationPercentages(): array {
369 $group = $this->getMessageGroup();
370 }
catch ( RuntimeException $e ) {
378 $titles = $this->getTranslationPages();
379 $temp = MessageGroupStats::forGroup( $this->getMessageGroupId() );
382 foreach ( $titles as $t ) {
383 $handle =
new MessageHandle( $t );
384 $code = $handle->getCode();
388 $stats[$code] = 0.00;
389 if ( ( $temp[$code][MessageGroupStats::TOTAL] ?? 0 ) > 0 ) {
390 $total = $temp[$code][MessageGroupStats::TOTAL];
391 $translated = $temp[$code][MessageGroupStats::TRANSLATED];
392 $percentage = $translated / $total;
393 $stats[$code] = sprintf(
'%.2f', $percentage );
398 $stats[$this->getSourceLanguageCode()] = 1.00;
403 public function getTransRev(
string $suffix ) {
404 $title = Title::makeTitle( NS_TRANSLATIONS, $suffix );
406 $db = Utilities::getSafeReadDB();
408 return $db->newSelectQueryBuilder()
409 ->select(
'rt_value' )
412 'rt_page' => $title->getArticleID(),
413 'rt_type' => RevTagStore::TRANSVER_PROP,
415 ->orderBy(
'rt_revision', SelectQueryBuilder::SORT_DESC )
416 ->caller( __METHOD__ )
420 public function supportsTransclusion(): ?bool {
421 $transclusion = Services::getInstance()
422 ->getMessageGroupMetadata()
423 ->get( $this->getMessageGroupId(),
'transclusion' );
424 if ( $transclusion ===
false ) {
428 return $transclusion ===
'1';
431 public function getRevisionRecordWithFallback(): ?RevisionRecord {
432 $title = $this->getTitle();
433 $store = MediaWikiServices::getInstance()->getRevisionStore();
434 $revRecord = $store->getRevisionByTitle( $title->getSubpage( $this->targetLanguage ) );
440 return $store->getRevisionByTitle( $title->getSubpage( $this->getSourceLanguageCode() ) );
445 return $this->getMarkedTag() !== null;
450 return $this->getMarkedTag() !== null;
456 if ( !Utilities::isTranslationPage( $handle ) ) {
460 $languageCode = $handle->getCode();
461 $newTitle = $handle->getTitleForBase();
463 $page = self::newFromTitle( $newTitle );
465 if ( $page->getMarkedTag() ===
null ) {
469 $page->targetLanguage = $languageCode;
478 $parts = explode(
'/', $translationUnit->getText() );
481 $language = array_pop( $parts );
482 $section = array_pop( $parts );
483 $sourcepage = implode(
'/', $parts );
486 'sourcepage' => $sourcepage,
487 'section' => $section,
488 'language' => $language
492 public static function isSourcePage( PageIdentity $page ): bool {
493 if ( !$page->exists() ) {
501 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
502 $cacheKey = $cache->makeKey(
'pagetranslation',
'sourcepages' );
504 $translatablePageIds = $cache->getWithSetCallback(
506 $cache::TTL_HOUR * 2,
507 static function ( $oldValue, &$ttl, array &$setOpts ) {
508 $dbr = MediaWikiServices::getInstance()->getDBLoadBalancer()->getConnection( DB_REPLICA );
509 $setOpts += Database::getCacheSetOptions( $dbr );
511 return RevTagStore::getTranslatableBundleIds(
512 RevTagStore::TP_MARK_TAG, RevTagStore::TP_READY_TAG
516 'checkKeys' => [ $cacheKey ],
517 'pcTTL' => $cache::TTL_PROC_SHORT,
518 'pcGroup' => __CLASS__ .
':1',
523 return isset( $translatablePageIds[$page->getId()] );
528 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
529 $cache->touchCheckKey( $cache->makeKey(
'pagetranslation',
'sourcepages' ) );
532 public static function determineStatus(
533 ?
int $readyRevisionId,
534 ?
int $markRevisionId,
535 int $latestRevisionId
538 if ( $markRevisionId ===
null ) {
540 if ( $readyRevisionId === $latestRevisionId ) {
541 $status = TranslatablePageStatus::PROPOSED;
546 } elseif ( $readyRevisionId === $latestRevisionId ) {
547 if ( $markRevisionId === $readyRevisionId ) {
549 $status = TranslatablePageStatus::ACTIVE;
551 $status = TranslatablePageStatus::OUTDATED;
555 $status = TranslatablePageStatus::BROKEN;
558 return new TranslatablePageStatus( $status );
return[ 'Translate:AggregateGroupManager'=> static function(MediaWikiServices $services):AggregateGroupManager { return new AggregateGroupManager( $services->getTitleFactory());}, '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( 'Translate.GroupSynchronization'), $services->get( 'Translate:MessageIndex'), $services->getTitleFactory(), new ServiceOptions(ExternalMessageSourceStateImporter::CONSTRUCTOR_OPTIONS, $services->getMainConfig()));}, 'Translate:FileBasedMessageGroupFactory'=> static function(MediaWikiServices $services):FileBasedMessageGroupFactory { return new FileBasedMessageGroupFactory(new MessageGroupConfigurationParser(), 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: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->getDBLoadBalancer());}, 'Translate:MessageGroupReviewStore'=> static function(MediaWikiServices $services):MessageGroupReviewStore { return new MessageGroupReviewStore($services->getDBLoadBalancer(), $services->get( 'Translate:HookRunner'));}, 'Translate:MessageGroupStatsTableFactory'=> static function(MediaWikiServices $services):MessageGroupStatsTableFactory { return new MessageGroupStatsTableFactory($services->get( 'Translate:ProgressStatsTableFactory'), $services->getDBLoadBalancer(), $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( 'Translate.MessageGroupSubscription'), new ServiceOptions(MessageGroupSubscription::CONSTRUCTOR_OPTIONS, $services->getMainConfig()));}, 'Translate:MessageGroupSubscriptionHookHandler'=> static function(MediaWikiServices $services):MessageGroupSubscriptionHookHandler { return new MessageGroupSubscriptionHookHandler($services->get( 'Translate:MessageGroupSubscription'), $services->getUserFactory());}, 'Translate:MessageGroupSubscriptionStore'=> static function(MediaWikiServices $services):MessageGroupSubscriptionStore { return new MessageGroupSubscriptionStore( $services->getDBLoadBalancerFactory());}, '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( 'Translate'), $services->getMainObjectStash(), $services->getDBLoadBalancerFactory(), $services->get( 'Translate:MessageGroupSubscription'), 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->getDBLoadBalancer(), $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->getDBLoadBalancer());}, '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->getDBLoadBalancer());}, '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());}, '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->getDBLoadBalancerFactory(), $services->getMainConfig() ->get( 'TranslatePageMoveLimit'));}, 'Translate:TranslatableBundleStatusStore'=> static function(MediaWikiServices $services):TranslatableBundleStatusStore { return new TranslatableBundleStatusStore($services->getDBLoadBalancer() ->getConnection(DB_PRIMARY), $services->getCollationFactory() ->makeCollation( 'uca-default-u-kn'), $services->getDBLoadBalancer() ->getMaintenanceConnectionRef(DB_PRIMARY));}, 'Translate:TranslatablePageMarker'=> static function(MediaWikiServices $services):TranslatablePageMarker { return new TranslatablePageMarker($services->getDBLoadBalancer(), $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'));}, '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->getDBLoadBalancer(), $services->get( 'Translate:TranslatableBundleStatusStore'), $services->get( 'Translate:TranslatablePageParser'), $services->get( 'Translate:MessageGroupMetadata'));}, 'Translate:TranslatablePageView'=> static function(MediaWikiServices $services):TranslatablePageView { return new TranslatablePageView($services->getDBLoadBalancerFactory(), $services->get( 'Translate:TranslatablePageStateStore'), new ServiceOptions(TranslatablePageView::SERVICE_OPTIONS, $services->getMainConfig()));}, 'Translate:TranslateSandbox'=> static function(MediaWikiServices $services):TranslateSandbox { return new TranslateSandbox($services->getUserFactory(), $services->getDBLoadBalancer(), $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 { $db=$services->getDBLoadBalancer() ->getConnection(DB_REPLICA);return new TranslationStashStorage( $db);}, 'Translate:TranslationStatsDataProvider'=> static function(MediaWikiServices $services):TranslationStatsDataProvider { return new TranslationStatsDataProvider(new ServiceOptions(TranslationStatsDataProvider::CONSTRUCTOR_OPTIONS, $services->getMainConfig()), $services->getObjectFactory(), $services->getDBLoadBalancer());}, '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);}]
@phpcs-require-sorted-array