Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
TranslatablePage.php
1<?php
2declare( strict_types = 1 );
3
4namespace MediaWiki\Extension\Translate\PageTranslation;
5
6use LogicException;
7use MediaWiki\Content\TextContent;
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 Wikimedia\Rdbms\Database;
26use Wikimedia\Rdbms\IDBAccessObject;
28
40 public const METADATA_KEYS = [
41 'maxid',
42 'priorityforce',
43 'prioritylangs',
44 'priorityreason',
45 'transclusion',
46 'version'
47 ];
49 public const DISPLAY_TITLE_UNIT_ID = 'Page display title';
50
51 protected PageIdentity $title;
52 protected RevTagStore $revTagStore;
54 protected $text;
56 protected $revision;
58 protected $source;
60 protected $pageDisplayTitle;
62 private $targetLanguage;
63
64 protected function __construct( PageIdentity $title ) {
65 $this->title = $title;
66 $this->revTagStore = Services::getInstance()->getRevTagStore();
67 }
68
74 public static function newFromText( Title $title, string $text ): self {
75 $obj = new self( $title );
76 $obj->text = $text;
77 $obj->source = 'text';
78
79 return $obj;
80 }
81
87 public static function newFromRevision( PageIdentity $title, int $revision ): self {
88 $rev = MediaWikiServices::getInstance()
89 ->getRevisionLookup()
90 ->getRevisionByTitle( $title, $revision );
91 if ( $rev === null ) {
92 throw new RuntimeException( 'Revision is null' );
93 }
94
95 $obj = new self( $title );
96 $obj->source = 'revision';
97 $obj->revision = $revision;
98
99 return $obj;
100 }
101
106 public static function newFromTitle( PageIdentity $title ): self {
107 $obj = new self( $title );
108 $obj->source = 'title';
109
110 return $obj;
111 }
112
114 public function getTitle(): Title {
115 return Title::newFromPageIdentity( $this->title );
116 }
117
118 public function getPageIdentity(): PageIdentity {
119 return $this->title;
120 }
121
123 public function getText(): string {
124 if ( $this->text !== null ) {
125 return $this->text;
126 }
127
128 if ( $this->source === 'title' ) {
129 $revision = $this->getMarkedTag();
130 if ( !is_int( $revision ) ) {
131 throw new LogicException(
132 "Trying to load a text for {$this->getPageIdentity()} which is not marked for translation"
133 );
134 }
135 $this->revision = $revision;
136 }
137
138 $flags = Utilities::shouldReadFromPrimary()
139 ? IDBAccessObject::READ_LATEST
140 : IDBAccessObject::READ_NORMAL;
141 $rev = MediaWikiServices::getInstance()
142 ->getRevisionLookup()
143 ->getRevisionByTitle( $this->getPageIdentity(), $this->revision, $flags );
144 $content = $rev->getContent( SlotRecord::MAIN );
145 $text = ( $content instanceof TextContent ) ? $content->getText() : null;
146
147 if ( !is_string( $text ) ) {
148 throw new RuntimeException( "Failed to load text for {$this->getPageIdentity()}" );
149 }
150
151 $this->text = $text;
152
153 return $this->text;
154 }
155
160 public function getRevision(): ?int {
161 return $this->revision;
162 }
163
169 public function getSourceLanguageCode(): string {
170 return $this->getTitle()->getPageLanguage()->getCode();
171 }
172
174 public function getMessageGroupId(): string {
175 return self::getMessageGroupIdFromTitle( $this->getPageIdentity() );
176 }
177
179 public static function getMessageGroupIdFromTitle( PageReference $page ): string {
180 return 'page-' . MediaWikiServices::getInstance()->getTitleFormatter()->getPrefixedText( $page );
181 }
182
188 $groupId = $this->getMessageGroupId();
189 $group = MessageGroups::getGroup( $groupId );
190 if ( !$group || $group instanceof WikiPageMessageGroup ) {
191 return $group;
192 }
193
194 throw new RuntimeException(
195 "Expected $groupId to be of type WikiPageMessageGroup; got " .
196 get_class( $group )
197 );
198 }
199
201 public function hasPageDisplayTitle(): bool {
202 // Cached value
203 if ( $this->pageDisplayTitle !== null ) {
204 return $this->pageDisplayTitle;
205 }
206
207 // Check if title section exists in list of sections
208 $factory = Services::getInstance()->getTranslationUnitStoreFactory();
209 $store = $factory->getReader( $this->getPageIdentity() );
210 $this->pageDisplayTitle = in_array( self::DISPLAY_TITLE_UNIT_ID, $store->getNames() );
211
212 return $this->pageDisplayTitle;
213 }
214
216 public function getPageDisplayTitle( string $languageCode ): ?string {
217 // Return null if title not marked for translation
218 if ( !$this->hasPageDisplayTitle() ) {
219 return null;
220 }
221
222 // Display title from DB
223 $section = str_replace( ' ', '_', self::DISPLAY_TITLE_UNIT_ID );
224 $page = MediaWikiServices::getInstance()->getTitleFormatter()->getPrefixedDBkey( $this->getPageIdentity() );
225
226 try {
227 $group = $this->getMessageGroup();
228 } catch ( RuntimeException ) {
229 return null;
230 }
231
232 // Sanity check, seems to happen during moves
233 if ( !$group ) {
234 return null;
235 }
236
237 return $group->getMessage( "$page/$section", $languageCode, IDBAccessObject::READ_NORMAL );
238 }
239
240 public function getStrippedSourcePageText(): string {
241 $parser = Services::getInstance()->getTranslatablePageParser();
242 $text = $parser->cleanupTags( $this->getText() );
243 $text = preg_replace( '~<languages\s*/>\n?~s', '', $text );
244
245 return $text;
246 }
247
248 public static function getTranslationPageFromTitle( Title $title ): ?TranslationPage {
249 $self = self::isTranslationPage( $title );
250 return $self ? $self->getTranslationPage( $self->targetLanguage ) : null;
251 }
252
253 public function getTranslationPage( string $targetLanguage ): TranslationPage {
254 $mwServices = MediaWikiServices::getInstance();
255 $config = $mwServices->getMainConfig();
256 $services = Services::getInstance();
257 $parser = $services->getTranslatablePageParser();
258 $parserOutput = $parser->parse( $this->getText() );
259 $pageVersion = (int)$services->getMessageGroupMetadata()
260 ->get( $this->getMessageGroupId(), 'version' );
261 $wrapUntranslated = $pageVersion >= 2;
262 $languageFactory = $mwServices->getLanguageFactory();
263
264 return new TranslationPage(
265 $parserOutput,
266 $this->getMessageGroup(),
267 $languageFactory->getLanguage( $targetLanguage ),
268 $languageFactory->getLanguage( $this->getSourceLanguageCode() ),
269 $config->get( 'TranslateKeepOutdatedTranslations' ),
270 $wrapUntranslated,
271 $this->getTitle()
272 );
273 }
274
276 public function addMarkedTag( int $revision, ?array $value = null ) {
277 $this->revTagStore->replaceTag( $this->getPageIdentity(), RevTagStore::TP_MARK_TAG, $revision, $value );
278 self::clearSourcePageCache();
279 }
280
282 public function addReadyTag( int $revision ): void {
283 $this->revTagStore->replaceTag( $this->getPageIdentity(), RevTagStore::TP_READY_TAG, $revision );
284 if ( !self::isSourcePage( $this->getPageIdentity() ) ) {
285 self::clearSourcePageCache();
286 }
287 }
288
290 public function getMarkedTag(): ?int {
291 return $this->revTagStore->getLatestRevisionWithTag( $this->getPageIdentity(), RevTagStore::TP_MARK_TAG );
292 }
293
295 public function getReadyTag(): ?int {
296 return $this->revTagStore->getLatestRevisionWithTag( $this->getPageIdentity(), RevTagStore::TP_READY_TAG );
297 }
298
304 public function getTranslationUrl( $code = false ): string {
305 $params = [
306 'group' => $this->getMessageGroupId(),
307 'action' => 'page',
308 'filter' => '',
309 'language' => $code,
310 ];
311
312 $translate = SpecialPage::getTitleFor( 'Translate' );
313
314 return $translate->getLocalURL( $params );
315 }
316
318 public function getTranslationPages(): array {
319 $mwServices = MediaWikiServices::getInstance();
320
321 $messageGroup = $this->getMessageGroup();
322 $knownLanguageCodes = $messageGroup ? $messageGroup->getTranslatableLanguages() : null;
323 $knownLanguageCodes ??= Utilities::getLanguageNames( LanguageNameUtils::AUTONYMS );
324
325 $prefixedDbTitleKey = $this->getPageIdentity()->getDBkey() . '/';
326 $baseNamespace = $this->getPageIdentity()->getNamespace();
327
328 // Build a link batch query for all translation pages
329 $linkBatch = $mwServices->getLinkBatchFactory()->newLinkBatch();
330 foreach ( array_keys( $knownLanguageCodes ) as $code ) {
331 $linkBatch->add( $baseNamespace, $prefixedDbTitleKey . $code );
332 }
333
334 $translationPages = [];
335 foreach ( $linkBatch->getPageIdentities() as $pageIdentity ) {
336 if ( $pageIdentity->exists() ) {
337 $translationPages[] = Title::newFromPageIdentity( $pageIdentity );
338 }
339 }
340
341 return $translationPages;
342 }
343
345 public function getTranslationUnitPages( ?string $code = null ): array {
346 return $this->getTranslationUnitPagesByTitle( $this->title, $code );
347 }
348
349 public function getTranslationPercentages(): array {
350 // Calculate percentages for the available translations
351 try {
352 $group = $this->getMessageGroup();
353 } catch ( RuntimeException ) {
354 return [];
355 }
356
357 if ( !$group ) {
358 return [];
359 }
360
361 $titles = $this->getTranslationPages();
362 $temp = MessageGroupStats::forGroup( $this->getMessageGroupId(), MessageGroupStats::FLAG_CACHE_ONLY );
363 $stats = [];
364
365 foreach ( $titles as $t ) {
366 $handle = new MessageHandle( $t );
367 $code = $handle->getCode();
368
369 // Sometimes we want to display 0.00 for pages for which translation
370 // hasn't started yet.
371 $stats[$code] = 0.00;
372 if ( ( $temp[$code][MessageGroupStats::TOTAL] ?? 0 ) > 0 ) {
373 $total = $temp[$code][MessageGroupStats::TOTAL];
374 $translated = $temp[$code][MessageGroupStats::TRANSLATED];
375 $percentage = $translated / $total;
376 $stats[$code] = sprintf( '%.2f', $percentage );
377 }
378 }
379
380 // Content language is always up-to-date
381 $stats[$this->getSourceLanguageCode()] = 1.00;
382
383 return $stats;
384 }
385
386 public function supportsTransclusion(): ?bool {
387 $transclusion = Services::getInstance()
388 ->getMessageGroupMetadata()
389 ->get( $this->getMessageGroupId(), 'transclusion' );
390 if ( $transclusion === false ) {
391 return null;
392 }
393
394 return $transclusion === '1';
395 }
396
397 public function getRevisionRecordWithFallback(): ?RevisionRecord {
398 $title = $this->getTitle();
399 $store = MediaWikiServices::getInstance()->getRevisionStore();
400 $revRecord = $store->getRevisionByTitle( $title->getSubpage( $this->targetLanguage ) );
401 if ( $revRecord ) {
402 return $revRecord;
403 }
404
405 // Fetch the source fallback
406 return $store->getRevisionByTitle( $title->getSubpage( $this->getSourceLanguageCode() ) );
407 }
408
410 public function isMoveable(): bool {
411 return $this->getMarkedTag() !== null;
412 }
413
415 public function isDeletable(): bool {
416 return $this->getMarkedTag() !== null;
417 }
418
420 public static function isTranslationPage( Title $title ) {
421 $handle = new MessageHandle( $title );
422 if ( !Utilities::isTranslationPage( $handle ) ) {
423 return false;
424 }
425
426 $languageCode = $handle->getCode();
427 $newTitle = $handle->getTitleForBase();
428
429 $page = self::newFromTitle( $newTitle );
430
431 if ( $page->getMarkedTag() === null ) {
432 return false;
433 }
434
435 $page->targetLanguage = $languageCode;
436
437 return $page;
438 }
439
441 public static function parseTranslationUnit( LinkTarget $translationUnit ): array {
442 // Format is Translations:SourcePageNamespace:SourcePageName/SectionName/LanguageCode.
443 // We will drop the namespace immediately here.
444 $parts = explode( '/', $translationUnit->getText() );
445
446 // LanguageCode and SectionName are guaranteed to not have '/'.
447 $language = array_pop( $parts );
448 $section = array_pop( $parts );
449 $sourcepage = implode( '/', $parts );
450
451 return [
452 'sourcepage' => $sourcepage,
453 'section' => $section,
454 'language' => $language
455 ];
456 }
457
458 public static function isSourcePage( PageIdentity $page ): bool {
459 if ( !$page->exists() ) {
460 // No point in loading all translatable pages if the page
461 // doesn’t exist. This also avoids PreconditionExceptions
462 // if $page is a Title pointing to a non-proper page like
463 // a special page.
464 return false;
465 }
466
467 $localCache = MediaWikiServices::getInstance()->getLocalServerObjectCache();
468 $localKey = $localCache->makeKey( 'pagetranslation', 'sourcepages', 'local' );
469 // Store the value in the local cache for a short duration to reduce the number of
470 // times we hit the WAN cache. See: T366455
471 $translatablePageIds = $localCache->getWithSetCallback(
472 $localKey,
473 $localCache::TTL_SECOND * 8,
474 static function () {
475 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
476 $cacheKey = $cache->makeKey( 'pagetranslation', 'sourcepages' );
477
478 return $cache->getWithSetCallback(
479 $cacheKey,
480 $cache::TTL_HOUR * 2,
481 [ TranslatablePage::class, 'getCacheValue' ],
482 [
483 'checkKeys' => [ $cacheKey ],
484 'pcTTL' => $cache::TTL_PROC_SHORT,
485 'pcGroup' => __CLASS__ . ':1',
486 'version' => 3,
487 ]
488 );
489 },
490 $localCache::READ_LATEST
491 );
492
493 return str_contains( $translatablePageIds, ( ',' . $page->getId() . ',' ) );
494 }
495
497 public static function clearSourcePageCache(): void {
498 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
499 $cache->touchCheckKey( $cache->makeKey( 'pagetranslation', 'sourcepages' ) );
500 }
501
502 public static function determineStatus(
503 ?int $readyRevisionId,
504 ?int $markRevisionId,
505 int $latestRevisionId
507 $status = null;
508 if ( $markRevisionId === null ) {
509 // Never marked, check that the latest version is ready
510 if ( $readyRevisionId === $latestRevisionId ) {
511 $status = TranslatablePageStatus::PROPOSED;
512 } else {
513 // Otherwise, ignore such pages
514 return null;
515 }
516 } elseif ( $readyRevisionId === $latestRevisionId ) {
517 if ( $markRevisionId === $readyRevisionId ) {
518 // Marked and latest version is fine
519 $status = TranslatablePageStatus::ACTIVE;
520 } else {
521 $status = TranslatablePageStatus::OUTDATED;
522 }
523 } else {
524 // Marked but latest version is not fine
525 $status = TranslatablePageStatus::BROKEN;
526 }
527
528 return new TranslatablePageStatus( $status );
529 }
530
539 public static function getCacheValue( $oldValue, &$ttl, array &$setOpts ): string {
540 $dbr = MediaWikiServices::getInstance()->getConnectionProvider()->getReplicaDatabase();
541 $setOpts += Database::getCacheSetOptions( $dbr );
542
543 $ids = RevTagStore::getTranslatableBundleIds(
544 RevTagStore::TP_MARK_TAG,
545 RevTagStore::TP_READY_TAG
546 );
547
548 // Adding a comma at the end and beginning so that we can check for page ID
549 // existence with the "," delimiters
550 return ',' . implode( ',', $ids ) . ',';
551 }
552}
553
554class_alias( TranslatablePage::class, 'TranslatablePage' );
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
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.
static getCacheValue( $oldValue, &$ttl, array &$setOpts)
Get list of translatable page ids to be stored in the cache.
getReadyTag()
Returns the latest revision which has ready tag, if any.
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.
addMarkedTag(int $revision, ?array $value=null)
Adds a tag which indicates that this page is suitable for translation.
getPageDisplayTitle(string $languageCode)
Get translated page title.
Minimal service container.
Definition Services.php:60
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:29
Wraps the translatable page sections into a message group.