Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
UpdateTranslatablePageJob.php
1<?php
2declare( strict_types = 1 );
3
4namespace MediaWiki\Extension\Translate\PageTranslation;
5
8use MediaWiki\MediaWikiServices;
12use RunnableJob;
13use Title;
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();
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.
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[] = MessageUpdateJob::newJob( $title, $unit->getTextWithVariables(), $fuzzy );
143 }
144
145 return $jobs;
146 }
147
152 public static function getRenderJobs( TranslatablePage $page ): array {
153 $jobs = [];
154
155 $jobTitles = $page->getTranslationPages();
156 // Ensure that we create the source language page when page is marked for translation.
157 $jobTitles[] = $page->getTitle()->getSubpage( $page->getSourceLanguageCode() );
158 // In some cases translation page may be missing even though translations exist. One such case
159 // is when FuzzyBot makes edits, which supresses render jobs. There may also be bugs with the
160 // render jobs failing. Add jobs based on message group stats to create self-healing process.
161 $stats = MessageGroupStats::forGroup( $page->getMessageGroupId() );
162 foreach ( $stats as $languageCode => $languageStats ) {
163 if ( $languageStats[MessageGroupStats::TRANSLATED] > 0 ) {
164 $jobTitles[] = $page->getTitle()->getSubpage( $languageCode );
165 }
166 }
167
168 // These jobs can be deduplicated by the job queue as well, but it's simple to do it here ourselves.
169 // Titles have __toString method that returns the prefixed text so array_unique should work.
170 $jobTitles = array_unique( $jobTitles );
171 foreach ( $jobTitles as $t ) {
172 $jobs[] = RenderTranslationPageJob::newJob( $t );
173 }
174
175 return $jobs;
176 }
177
178}
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 newFromPage(TranslatablePage $page, array $sections=[])
Create a job that updates a translation page.
static getRenderJobs(TranslatablePage $page)
Creates jobs needed to create or update all translation pages.
This class abstract MessageGroup statistics calculation and storing.
static forGroup( $id, $flags=0)
Returns stats for all languages in given group.
Job for rebuilding message index.
Job for updating translation pages when translation or message definition changes.
static newJob(Title $target, string $content, $fuzzy=false)
Create a normal message update job without a rename process.