Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
TranslatablePage.php
1<?php
2declare( strict_types = 1 );
3
4namespace MediaWiki\Extension\Translate\PageTranslation;
5
6use IDBAccessObject;
7use LogicException;
15use MediaWiki\Languages\LanguageNameUtils;
16use MediaWiki\Linker\LinkTarget;
17use MediaWiki\MediaWikiServices;
18use MediaWiki\Page\PageIdentity;
19use MediaWiki\Page\PageReference;
20use MediaWiki\Revision\RevisionRecord;
21use MediaWiki\Revision\SlotRecord;
22use MediaWiki\SpecialPage\SpecialPage;
23use MediaWiki\Title\Title;
24use RuntimeException;
25use TextContent;
26use Wikimedia\Rdbms\Database;
27use Wikimedia\Rdbms\IResultWrapper;
28use Wikimedia\Rdbms\SelectQueryBuilder;
30
42 public const METADATA_KEYS = [
43 'maxid',
44 'priorityforce',
45 'prioritylangs',
46 'priorityreason',
47 'transclusion',
48 'version'
49 ];
51 public const DISPLAY_TITLE_UNIT_ID = 'Page display title';
52
53 protected PageIdentity $title;
54 protected RevTagStore $revTagStore;
56 protected $text;
58 protected $revision;
60 protected $source;
62 protected $pageDisplayTitle;
64 private $targetLanguage;
65
66 protected function __construct( PageIdentity $title ) {
67 $this->title = $title;
68 $this->revTagStore = Services::getInstance()->getRevTagStore();
69 }
70
76 public static function newFromText( Title $title, string $text ): self {
77 $obj = new self( $title );
78 $obj->text = $text;
79 $obj->source = 'text';
80
81 return $obj;
82 }
83
89 public static function newFromRevision( PageIdentity $title, int $revision ): self {
90 $rev = MediaWikiServices::getInstance()
91 ->getRevisionLookup()
92 ->getRevisionByTitle( $title, $revision );
93 if ( $rev === null ) {
94 throw new RuntimeException( 'Revision is null' );
95 }
96
97 $obj = new self( $title );
98 $obj->source = 'revision';
99 $obj->revision = $revision;
100
101 return $obj;
102 }
103
108 public static function newFromTitle( PageIdentity $title ): self {
109 $obj = new self( $title );
110 $obj->source = 'title';
111
112 return $obj;
113 }
114
116 public function getTitle(): Title {
117 return Title::castFromPageIdentity( $this->title );
118 }
119
120 public function getPageIdentity(): PageIdentity {
121 return $this->title;
122 }
123
125 public function getText(): string {
126 if ( $this->text !== null ) {
127 return $this->text;
128 }
129
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"
135 );
136 }
137 $this->revision = $revision;
138 }
139
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;
148
149 if ( !is_string( $text ) ) {
150 throw new RuntimeException( "Failed to load text for {$this->getPageIdentity()}" );
151 }
152
153 $this->text = $text;
154
155 return $this->text;
156 }
157
162 public function getRevision(): ?int {
163 return $this->revision;
164 }
165
171 public function getSourceLanguageCode(): string {
172 return $this->getTitle()->getPageLanguage()->getCode();
173 }
174
176 public function getMessageGroupId(): string {
177 return self::getMessageGroupIdFromTitle( $this->getPageIdentity() );
178 }
179
181 public static function getMessageGroupIdFromTitle( PageReference $page ): string {
182 return 'page-' . MediaWikiServices::getInstance()->getTitleFormatter()->getPrefixedText( $page );
183 }
184
190 $groupId = $this->getMessageGroupId();
191 $group = MessageGroups::getGroup( $groupId );
192 if ( !$group || $group instanceof WikiPageMessageGroup ) {
193 return $group;
194 }
195
196 throw new RuntimeException(
197 "Expected $groupId to be of type WikiPageMessageGroup; got " .
198 get_class( $group )
199 );
200 }
201
203 public function hasPageDisplayTitle(): bool {
204 // Cached value
205 if ( $this->pageDisplayTitle !== null ) {
206 return $this->pageDisplayTitle;
207 }
208
209 // Check if title section exists in list of sections
210 $factory = Services::getInstance()->getTranslationUnitStoreFactory();
211 $store = $factory->getReader( $this->getPageIdentity() );
212 $this->pageDisplayTitle = in_array( self::DISPLAY_TITLE_UNIT_ID, $store->getNames() );
213
214 return $this->pageDisplayTitle;
215 }
216
218 public function getPageDisplayTitle( string $languageCode ): ?string {
219 // Return null if title not marked for translation
220 if ( !$this->hasPageDisplayTitle() ) {
221 return null;
222 }
223
224 // Display title from DB
225 $section = str_replace( ' ', '_', self::DISPLAY_TITLE_UNIT_ID );
226 $page = MediaWikiServices::getInstance()->getTitleFormatter()->getPrefixedDBkey( $this->getPageIdentity() );
227
228 try {
229 $group = $this->getMessageGroup();
230 } catch ( RuntimeException $e ) {
231 return null;
232 }
233
234 // Sanity check, seems to happen during moves
235 if ( !$group ) {
236 return null;
237 }
238
239 return $group->getMessage( "$page/$section", $languageCode, IDBAccessObject::READ_NORMAL );
240 }
241
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 );
246
247 return $text;
248 }
249
250 public static function getTranslationPageFromTitle( Title $title ): ?TranslationPage {
251 $self = self::isTranslationPage( $title );
252 return $self ? $self->getTranslationPage( $self->targetLanguage ) : null;
253 }
254
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();
265
266 return new TranslationPage(
267 $parserOutput,
268 $this->getMessageGroup(),
269 $languageFactory->getLanguage( $targetLanguage ),
270 $languageFactory->getLanguage( $this->getSourceLanguageCode() ),
271 $config->get( 'TranslateKeepOutdatedTranslations' ),
272 $wrapUntranslated,
273 $this->getTitle()
274 );
275 }
276
278 public function addMarkedTag( int $revision, array $value = null ) {
279 $this->revTagStore->replaceTag( $this->getPageIdentity(), RevTagStore::TP_MARK_TAG, $revision, $value );
280 self::clearSourcePageCache();
281 }
282
284 public function addReadyTag( int $revision ): void {
285 $this->revTagStore->replaceTag( $this->getPageIdentity(), RevTagStore::TP_READY_TAG, $revision );
286 if ( !self::isSourcePage( $this->getPageIdentity() ) ) {
287 self::clearSourcePageCache();
288 }
289 }
290
292 public function getMarkedTag(): ?int {
293 return $this->revTagStore->getLatestRevisionWithTag( $this->getPageIdentity(), RevTagStore::TP_MARK_TAG );
294 }
295
297 public function getReadyTag(): ?int {
298 return $this->revTagStore->getLatestRevisionWithTag( $this->getPageIdentity(), RevTagStore::TP_READY_TAG );
299 }
300
306 public function getTranslationUrl( $code = false ): string {
307 $params = [
308 'group' => $this->getMessageGroupId(),
309 'action' => 'page',
310 'filter' => '',
311 'language' => $code,
312 ];
313
314 $translate = SpecialPage::getTitleFor( 'Translate' );
315
316 return $translate->getLocalURL( $params );
317 }
318
319 public function getMarkedRevs(): IResultWrapper {
320 $db = Utilities::getSafeReadDB();
321
322 return $db->newSelectQueryBuilder()
323 ->select( [ 'rt_revision', 'rt_value' ] )
324 ->from( 'revtag' )
325 ->where( [
326 'rt_page' => $this->getPageIdentity()->getId(),
327 'rt_type' => RevTagStore::TP_MARK_TAG,
328 ] )
329 ->orderBy( 'rt_revision', SelectQueryBuilder::SORT_DESC )
330 ->caller( __METHOD__ )
331 ->fetchResultSet();
332 }
333
335 public function getTranslationPages(): array {
336 $mwServices = MediaWikiServices::getInstance();
337
338 $messageGroup = $this->getMessageGroup();
339 $knownLanguageCodes = $messageGroup ? $messageGroup->getTranslatableLanguages() : null;
340 $knownLanguageCodes ??= Utilities::getLanguageNames( LanguageNameUtils::AUTONYMS );
341
342 $prefixedDbTitleKey = $this->getPageIdentity()->getDBkey() . '/';
343 $baseNamespace = $this->getPageIdentity()->getNamespace();
344
345 // Build a link batch query for all translation pages
346 $linkBatch = $mwServices->getLinkBatchFactory()->newLinkBatch();
347 foreach ( array_keys( $knownLanguageCodes ) as $code ) {
348 $linkBatch->add( $baseNamespace, $prefixedDbTitleKey . $code );
349 }
350
351 $translationPages = [];
352 foreach ( $linkBatch->getPageIdentities() as $pageIdentity ) {
353 if ( $pageIdentity->exists() ) {
354 $translationPages[] = Title::castFromPageIdentity( $pageIdentity );
355 }
356 }
357
358 return $translationPages;
359 }
360
362 public function getTranslationUnitPages( ?string $code = null ): array {
363 return $this->getTranslationUnitPagesByTitle( $this->title, $code );
364 }
365
366 public function getTranslationPercentages(): array {
367 // Calculate percentages for the available translations
368 try {
369 $group = $this->getMessageGroup();
370 } catch ( RuntimeException $e ) {
371 return [];
372 }
373
374 if ( !$group ) {
375 return [];
376 }
377
378 $titles = $this->getTranslationPages();
379 $temp = MessageGroupStats::forGroup( $this->getMessageGroupId() );
380 $stats = [];
381
382 foreach ( $titles as $t ) {
383 $handle = new MessageHandle( $t );
384 $code = $handle->getCode();
385
386 // Sometimes we want to display 0.00 for pages for which translation
387 // hasn't started yet.
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 );
394 }
395 }
396
397 // Content language is always up-to-date
398 $stats[$this->getSourceLanguageCode()] = 1.00;
399
400 return $stats;
401 }
402
403 public function getTransRev( string $suffix ) {
404 $title = Title::makeTitle( NS_TRANSLATIONS, $suffix );
405
406 $db = Utilities::getSafeReadDB();
407
408 return $db->newSelectQueryBuilder()
409 ->select( 'rt_value' )
410 ->from( 'revtag' )
411 ->where( [
412 'rt_page' => $title->getArticleID(),
413 'rt_type' => RevTagStore::TRANSVER_PROP,
414 ] )
415 ->orderBy( 'rt_revision', SelectQueryBuilder::SORT_DESC )
416 ->caller( __METHOD__ )
417 ->fetchField();
418 }
419
420 public function supportsTransclusion(): ?bool {
421 $transclusion = Services::getInstance()
422 ->getMessageGroupMetadata()
423 ->get( $this->getMessageGroupId(), 'transclusion' );
424 if ( $transclusion === false ) {
425 return null;
426 }
427
428 return $transclusion === '1';
429 }
430
431 public function getRevisionRecordWithFallback(): ?RevisionRecord {
432 $title = $this->getTitle();
433 $store = MediaWikiServices::getInstance()->getRevisionStore();
434 $revRecord = $store->getRevisionByTitle( $title->getSubpage( $this->targetLanguage ) );
435 if ( $revRecord ) {
436 return $revRecord;
437 }
438
439 // Fetch the source fallback
440 return $store->getRevisionByTitle( $title->getSubpage( $this->getSourceLanguageCode() ) );
441 }
442
444 public function isMoveable(): bool {
445 return $this->getMarkedTag() !== null;
446 }
447
449 public function isDeletable(): bool {
450 return $this->getMarkedTag() !== null;
451 }
452
454 public static function isTranslationPage( Title $title ) {
455 $handle = new MessageHandle( $title );
456 if ( !Utilities::isTranslationPage( $handle ) ) {
457 return false;
458 }
459
460 $languageCode = $handle->getCode();
461 $newTitle = $handle->getTitleForBase();
462
463 $page = self::newFromTitle( $newTitle );
464
465 if ( $page->getMarkedTag() === null ) {
466 return false;
467 }
468
469 $page->targetLanguage = $languageCode;
470
471 return $page;
472 }
473
475 public static function parseTranslationUnit( LinkTarget $translationUnit ): array {
476 // Format is Translations:SourcePageNamespace:SourcePageName/SectionName/LanguageCode.
477 // We will drop the namespace immediately here.
478 $parts = explode( '/', $translationUnit->getText() );
479
480 // LanguageCode and SectionName are guaranteed to not have '/'.
481 $language = array_pop( $parts );
482 $section = array_pop( $parts );
483 $sourcepage = implode( '/', $parts );
484
485 return [
486 'sourcepage' => $sourcepage,
487 'section' => $section,
488 'language' => $language
489 ];
490 }
491
492 public static function isSourcePage( PageIdentity $page ): bool {
493 if ( !$page->exists() ) {
494 // No point in loading all translatable pages if the page
495 // doesn’t exist. This also avoids PreconditionExceptions
496 // if $page is a Title pointing to a non-proper page like
497 // a special page.
498 return false;
499 }
500
501 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
502 $cacheKey = $cache->makeKey( 'pagetranslation', 'sourcepages' );
503
504 $translatablePageIds = $cache->getWithSetCallback(
505 $cacheKey,
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 );
510
511 return RevTagStore::getTranslatableBundleIds(
512 RevTagStore::TP_MARK_TAG, RevTagStore::TP_READY_TAG
513 );
514 },
515 [
516 'checkKeys' => [ $cacheKey ],
517 'pcTTL' => $cache::TTL_PROC_SHORT,
518 'pcGroup' => __CLASS__ . ':1',
519 'version' => 2,
520 ]
521 );
522
523 return isset( $translatablePageIds[$page->getId()] );
524 }
525
527 public static function clearSourcePageCache(): void {
528 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
529 $cache->touchCheckKey( $cache->makeKey( 'pagetranslation', 'sourcepages' ) );
530 }
531
532 public static function determineStatus(
533 ?int $readyRevisionId,
534 ?int $markRevisionId,
535 int $latestRevisionId
537 $status = null;
538 if ( $markRevisionId === null ) {
539 // Never marked, check that the latest version is ready
540 if ( $readyRevisionId === $latestRevisionId ) {
541 $status = TranslatablePageStatus::PROPOSED;
542 } else {
543 // Otherwise, ignore such pages
544 return null;
545 }
546 } elseif ( $readyRevisionId === $latestRevisionId ) {
547 if ( $markRevisionId === $readyRevisionId ) {
548 // Marked and latest version is fine
549 $status = TranslatablePageStatus::ACTIVE;
550 } else {
551 $status = TranslatablePageStatus::OUTDATED;
552 }
553 } else {
554 // Marked but latest version is not fine
555 $status = TranslatablePageStatus::BROKEN;
556 }
557
558 return new TranslatablePageStatus( $status );
559 }
560}
561
562class_alias( TranslatablePage::class, 'TranslatablePage' );
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
Factory class for accessing message groups individually by id or all of them as a list.
Class to manage revision tags for translatable bundles.
Translatable bundle represents a message group where its translatable content is defined on a wiki pa...
Class for pointing to messages, like Title class is for titles.
Stores and validates possible statuses for TranslatablePage.
Mixed bag of methods related to translatable pages.
static newFromRevision(PageIdentity $title, int $revision)
Constructs a translatable page from given revision.
addReadyTag(int $revision)
Adds a tag which indicates that this page source is ready for marking for translation.
static newFromTitle(PageIdentity $title)
Constructs a translatable page from title.
getMarkedTag()
Returns the latest revision which has marked tag, if any.
getText()
Returns the text for this translatable page.
hasPageDisplayTitle()
Check whether title is marked for translation.
static getMessageGroupIdFromTitle(PageReference $page)
Constructs MessageGroup id for any title.
getReadyTag()
Returns the latest revision which has ready tag, if any.
addMarkedTag(int $revision, array $value=null)
Adds a tag which indicates that this page is suitable for translation.
getRevision()
Revision is null if object was constructed using newFromText.
getSourceLanguageCode()
Returns the source language of this translatable page.
getTranslationUrl( $code=false)
Produces a link to translation view of a translation page.
getMessageGroup()
Returns MessageGroup used for translating this page.
const METADATA_KEYS
List of keys in the metadata table that need to be handled for moves and deletions @phpcs-require-sor...
static parseTranslationUnit(LinkTarget $translationUnit)
Helper to guess translation page from translation unit.
static newFromText(Title $title, string $text)
Constructs a translatable page from given text.
getPageDisplayTitle(string $languageCode)
Get translated page title.
Minimal service container.
Definition Services.php:58
This class aims to provide efficient mechanism for fetching translation completion stats.
Essentially random collection of helper functions, similar to GlobalFunctions.php.
Definition Utilities.php:31
Wraps the translatable page sections into a message group.