Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
UpdateTranslatablePageJob.php
1<?php
2declare( strict_types = 1 );
3
4namespace MediaWiki\Extension\Translate\PageTranslation;
5
11use MediaWiki\MediaWikiServices;
12use MediaWiki\Title\Title;
13use RunnableJob;
14
21 public function __construct( Title $title, array $params = [] ) {
22 parent::__construct( 'UpdateTranslatablePageJob', $title, $params );
23 }
24
34 public static function newFromPage( TranslatablePage $page, array $sections = [] ): self {
35 $params = [];
36 $params[ 'sections' ] = [];
37 foreach ( $sections as $section ) {
38 $params[ 'sections' ][] = $section->serializeToArray();
39 }
40
41 return new self( $page->getTitle(), $params );
42 }
43
44 public function run(): bool {
45 // WARNING: Nothing here must not depend on message index being up to date.
46 // For performance reasons, message index rebuild is run a separate job after
47 // everything else is updated.
48
49 // START: This section does not care about replication lag
50 $this->logInfo( 'Starting UpdateTranslatablePageJob' );
51
52 $sections = $this->params[ 'sections' ];
53 foreach ( $sections as $index => $section ) {
54 // Old jobs stored sections as objects because they were serialized and
55 // unserialized transparently. That is no longer supported, so we
56 // convert manually to primitive types first (to an PHP array).
57 if ( is_array( $section ) ) {
58 $sections[ $index ] = TranslationUnit::unserializeFromArray( $section );
59 }
60 }
61
66 $page = TranslatablePage::newFromTitle( $this->title );
67 $unitJobs = self::getTranslationUnitJobs( $page, $sections );
68 foreach ( $unitJobs as $job ) {
69 $job->run();
70 }
71
72 $this->logInfo(
73 'Finished running ' . count( $unitJobs ) . ' MessageUpdate jobs for '
74 . count( $sections ) . ' sections'
75 );
76 // END: This section does not care about replication lag
77 $mwServices = MediaWikiServices::getInstance();
78 $lb = $mwServices->getDBLoadBalancerFactory();
79 if ( !$lb->waitForReplication() ) {
80 $this->logWarning( 'Continuing despite replication lag' );
81 }
82
83 // Ensure we are using the latest group definitions. This is needed so
84 // that in long running scripts we do see the page which was just
85 // marked for translation. Otherwise getMessageGroup in the next line
86 // returns null. There is no need to regenerate the global cache.
87 MessageGroups::singleton()->clearProcessCache();
88 // Ensure fresh definitions for stats
89 $page->getMessageGroup()->clearCaches();
90
91 $this->logInfo( 'Cleared caches' );
92
93 // Refresh translations statistics, we want these to be up to date for the
94 // RenderJobs, for displaying up to date statistics on the translation pages.
95 $id = $page->getMessageGroupId();
96 MessageGroupStats::forGroup(
97 $id,
98 MessageGroupStats::FLAG_NO_CACHE | MessageGroupStats::FLAG_IMMEDIATE_WRITES
99 );
100 $this->logInfo( 'Updated the message group stats' );
101
102 // Try to avoid stale statistics on the base page
103 $wikiPage = $mwServices->getWikiPageFactory()->newFromTitle( $page->getTitle() );
104 $wikiPage->doPurge();
105 $this->logInfo( 'Finished purging' );
106
107 // These can be run independently and in parallel if possible
108 $jobQueueGroup = $mwServices->getJobQueueGroup();
109 $renderJobs = self::getRenderJobs( $page );
110 $jobQueueGroup->push( $renderJobs );
111 $this->logInfo( 'Added ' . count( $renderJobs ) . ' RenderJobs to the queue' );
112
113 // Schedule message index update. Thanks to front caching, it is okay if this takes
114 // a while (and on large wikis it does take a while!). Running it as a separate job
115 // also allows de-duplication in case multiple translatable pages are being marked
116 // for translation in a short period of time.
117 $job = RebuildMessageIndexJob::newJob();
118 $jobQueueGroup->push( $job );
119
120 $this->logInfo( 'Finished UpdateTranslatablePageJob' );
121
122 return true;
123 }
124
131 private static function getTranslationUnitJobs( TranslatablePage $page, array $units ): array {
132 $jobs = [];
133
134 $code = $page->getSourceLanguageCode();
135 $prefix = $page->getTitle()->getPrefixedText();
136
137 foreach ( $units as $unit ) {
138 $unitName = $unit->id;
139 $title = Title::makeTitle( NS_TRANSLATIONS, "$prefix/$unitName/$code" );
140
141 $fuzzy = $unit->type === 'changed';
142 $jobs[] = UpdateMessageJob::newJob( $title, $unit->getTextWithVariables(), $fuzzy );
143 }
144
145 return $jobs;
146 }
147
152 public static function getRenderJobs( TranslatablePage $page, bool $nonPrioritizedJobs = false ): array {
153 $documentationLanguageCode = MediaWikiServices::getInstance()
154 ->getMainConfig()
155 ->get( 'TranslateDocumentationLanguageCode' );
156
157 $jobs = [];
158
159 $jobTitles = $page->getTranslationPages();
160 // Ensure that we create the source language page when page is marked for translation.
161 $jobTitles[] = $page->getTitle()->getSubpage( $page->getSourceLanguageCode() );
162 // In some cases translation page may be missing even though translations exist. One such case
163 // is when FuzzyBot makes edits, which suppresses render jobs. There may also be bugs with the
164 // render jobs failing. Add jobs based on message group stats to create self-healing process.
165 $stats = MessageGroupStats::forGroup( $page->getMessageGroupId() );
166 foreach ( $stats as $languageCode => $languageStats ) {
167 if ( $languageStats[MessageGroupStats::TRANSLATED] > 0 && $languageCode !== $documentationLanguageCode ) {
168 $jobTitles[] = $page->getTitle()->getSubpage( $languageCode );
169 }
170 }
171
172 // These jobs can be deduplicated by the job queue as well, but it's simple to do it here ourselves.
173 // Titles have __toString method that returns the prefixed text so array_unique should work.
174 $jobTitles = array_unique( $jobTitles );
175 foreach ( $jobTitles as $t ) {
176 if ( $nonPrioritizedJobs ) {
177 $jobs[] = RenderTranslationPageJob::newNonPrioritizedJob( $t );
178 } else {
179 $jobs[] = RenderTranslationPageJob::newJob( $t );
180 }
181
182 }
183
184 return $jobs;
185 }
186
187}
Factory class for accessing message groups individually by id or all of them as a list.
Mixed bag of methods related to translatable pages.
static newFromTitle(PageIdentity $title)
Constructs a translatable page from title.
getSourceLanguageCode()
Returns the source language of this translatable page.
getMessageGroup()
Returns MessageGroup used for translating this page.
Job for updating translation units and translation pages when a translatable page is marked for trans...
static getRenderJobs(TranslatablePage $page, bool $nonPrioritizedJobs=false)
Creates jobs needed to create or update all translation pages.
static newFromPage(TranslatablePage $page, array $sections=[])
Create a job that updates a translation page.
This class aims to provide efficient mechanism for fetching translation completion stats.
Job for updating translation pages when translation or message definition changes.