Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 69 |
|
0.00% |
0 / 4 |
CRAP | |
0.00% |
0 / 1 |
UpdateMessageBundleJob | |
0.00% |
0 / 69 |
|
0.00% |
0 / 4 |
90 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
newJob | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
2 | |||
run | |
0.00% |
0 / 56 |
|
0.00% |
0 / 1 |
20 | |||
shouldFuzzy | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
12 |
1 | <?php |
2 | declare( strict_types = 1 ); |
3 | |
4 | namespace MediaWiki\Extension\Translate\MessageBundleTranslation; |
5 | |
6 | use Job; |
7 | use MediaWiki\Extension\Translate\MessageGroupProcessing\MessageGroups; |
8 | use MediaWiki\Extension\Translate\Services; |
9 | use MediaWiki\Logger\LoggerFactory; |
10 | use MediaWiki\MediaWikiServices; |
11 | use MediaWiki\Title\Title; |
12 | use MessageGroupStats; |
13 | use MessageIndexRebuildJob; |
14 | use MessageUpdateJob; |
15 | |
16 | /** |
17 | * @author Niklas Laxström |
18 | * @license GPL-2.0-or-later |
19 | * @since 2021.12 |
20 | */ |
21 | class UpdateMessageBundleJob extends Job { |
22 | /** @inheritDoc */ |
23 | public function __construct( Title $title, $params = [] ) { |
24 | parent::__construct( 'UpdateMessageBundle', $title, $params ); |
25 | } |
26 | |
27 | public static function newJob( Title $bundlePageTitle, int $revisionId, ?int $previousRevisionId ): self { |
28 | return new self( |
29 | $bundlePageTitle, |
30 | [ |
31 | 'revisionId' => $revisionId, |
32 | 'previousRevisionId' => $previousRevisionId, |
33 | ] |
34 | ); |
35 | } |
36 | |
37 | /** @inheritDoc */ |
38 | public function run(): bool { |
39 | $mwInstance = MediaWikiServices::getInstance(); |
40 | $lb = $mwInstance->getDBLoadBalancerFactory(); |
41 | $jobQueue = $mwInstance->getJobQueueGroup(); |
42 | $logger = LoggerFactory::getInstance( 'Translate.MessageBundle' ); |
43 | $messageIndex = Services::getInstance()->getMessageIndex(); |
44 | |
45 | $logger->info( 'UpdateMessageBundleJob: Starting job for: ' . $this->getTitle()->getPrefixedText() ); |
46 | |
47 | // Not sure if this is necessary, but it should ensure that this job, which was created |
48 | // when a revision was saved, can read that revision from the replica. In addition, this |
49 | // may potentially do a bunch of more writes that could cause more replication lag. |
50 | if ( !$lb->waitForReplication() ) { |
51 | $logger->warning( 'UpdateMessageBundleJob: Continuing despite replication lag' ); |
52 | } |
53 | |
54 | // Setup |
55 | $bundlePageTitle = $this->getTitle(); |
56 | $name = $bundlePageTitle->getPrefixedText(); |
57 | $pageId = $bundlePageTitle->getId(); |
58 | $groupId = MessageBundleMessageGroup::getGroupId( $name ); |
59 | $params = $this->getParams(); |
60 | // We don't care about the group description or label, so no need to pass it through |
61 | $group = new MessageBundleMessageGroup( |
62 | $groupId, $name, $pageId, $params['revisionId'], null, null |
63 | ); |
64 | $messages = $group->getDefinitions(); |
65 | $previousMessages = []; |
66 | if ( $params['previousRevisionId'] ) { |
67 | $groupPreviousVersion = new MessageBundleMessageGroup( |
68 | $groupId, $name, $pageId, $params['previousRevisionId'], null, null |
69 | ); |
70 | $previousMessages = $groupPreviousVersion->getDefinitions(); |
71 | } |
72 | |
73 | // Fill in the front-cache. Ideally this should be done right away, but hopefully |
74 | // this is okay since we only trigger message group cache rebuild later in this job. |
75 | // It's possible that some other change triggers it earlier and makes the new group |
76 | // available before this step is complete. |
77 | $newKeys = array_diff( array_keys( $messages ), array_keys( $previousMessages ) ); |
78 | $messageIndex->storeInterim( $group, $newKeys ); |
79 | |
80 | // Create jobs that will update the '/' source language pages. These pages should |
81 | // exist so that the editor can show differences for changed messages. Also compare |
82 | // against previous version (if any) to determine whether to mark translations as |
83 | // outdated. There is no support for renames. |
84 | $jobs = []; |
85 | $namespace = $group->getNamespace(); |
86 | $code = $group->getSourceLanguage(); |
87 | foreach ( $messages as $key => $value ) { |
88 | $title = Title::makeTitle( $namespace, "$key/$code" ); |
89 | $fuzzy = $this->shouldFuzzy( $previousMessages, $newKeys, $key, $value ); |
90 | $jobs[] = MessageUpdateJob::newJob( $title, $value, $fuzzy ); |
91 | } |
92 | $jobQueue->push( $jobs ); |
93 | $logger->info( |
94 | 'UpdateMessageBundleJob: Added {number} MessageUpdateJobs to the job queue for: {title}', |
95 | [ |
96 | 'number' => count( $jobs ), |
97 | 'title' => $name |
98 | ] |
99 | ); |
100 | |
101 | // This is somewhat slow, so it has been postponed until now, but it's needed to |
102 | // make the group available for the message index rebuild. |
103 | MessageGroups::singleton()->recache(); |
104 | |
105 | $logger->info( |
106 | 'UpdateMessageBundleJob: {title}: Recaching message groups', |
107 | [ 'title' => $name ] |
108 | ); |
109 | |
110 | // Schedule message index update. Thanks to front caching, it is okay if this takes |
111 | // a while (and on large wikis it does take a while!). Running it as a separate job |
112 | // also allows de-duplication. |
113 | $job = MessageIndexRebuildJob::newJob(); |
114 | $jobQueue->push( $job ); |
115 | $logger->info( |
116 | 'UpdateMessageBundleJob: {title}: Queue MessageIndexRebuildJob', |
117 | [ 'title' => $name ] |
118 | ); |
119 | |
120 | // Refresh or fill translations statistics. If this a new group, this prevents |
121 | // calculating the stats on the fly during read requests. If an existing group, this |
122 | // makes sure that the statistics are up-to-date. |
123 | MessageGroupStats::forGroup( |
124 | $groupId, |
125 | MessageGroupStats::FLAG_NO_CACHE | MessageGroupStats::FLAG_IMMEDIATE_WRITES |
126 | ); |
127 | |
128 | return true; |
129 | } |
130 | |
131 | private function shouldFuzzy( |
132 | ?array $previousMessages, |
133 | array $newKeys, |
134 | string $key, |
135 | string $value |
136 | ): bool { |
137 | // Mark new keys as fuzzy |
138 | if ( in_array( $key, $newKeys ) ) { |
139 | return true; |
140 | } |
141 | |
142 | $previousValue = $previousMessages[$key] ?? null; |
143 | return $previousMessages !== null && $previousValue !== $value; |
144 | } |
145 | } |