Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
UpdateTranslatablePageJob.php
1<?php
2declare( strict_types = 1 );
3
4namespace MediaWiki\Extension\Translate\PageTranslation;
5
7use 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}
Mixed bag of methods related to translatable pages.
static newFromTitle(Title $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.
Factory class for accessing message groups individually by id or all of them as an list.
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.