Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
MessageBundleStore.php
1<?php
2declare( strict_types = 1 );
3
4namespace MediaWiki\Extension\Translate\MessageBundleTranslation;
5
6use InvalidArgumentException;
7use JobQueueGroup;
12use MediaWiki\Languages\LanguageNameUtils;
13use MediaWiki\Revision\RevisionRecord;
14use Message;
15use MessageIndex;
16use RequestContext;
17use SpecialPageLanguage;
18use Title;
20
29 private $revTagStore;
31 private $jobQueue;
33 private $languageNameUtils;
35 private $messageIndex;
36 private const METADATA_KEYS_DB = [
37 'priorityforce',
38 'prioritylangs'
39 ];
40
41 public function __construct(
42 RevTagStore $revTagStore,
43 JobQueueGroup $jobQueue,
44 LanguageNameUtils $languageNameUtils,
45 MessageIndex $messageIndex
46 ) {
47 $this->revTagStore = $revTagStore;
48 $this->jobQueue = $jobQueue;
49 $this->languageNameUtils = $languageNameUtils;
50 $this->messageIndex = $messageIndex;
51 }
52
53 public function move( Title $oldName, Title $newName ): void {
54 $oldBundle = new MessageBundle( $oldName );
55 $newBundle = new MessageBundle( $newName );
56
57 TranslateMetadata::moveMetadata(
58 $oldBundle->getMessageGroupId(),
59 $newBundle->getMessageGroupId(),
60 self::METADATA_KEYS_DB
61 );
62
63 MessageBundle::clearSourcePageCache();
64
65 // Re-render the bundles to get everything in sync
66 MessageGroups::singleton()->recache();
67 // Update message index now so that, when after this job the MoveTranslationUnits hook
68 // runs in deferred updates, it will not run MessageIndexRebuildJob (T175834).
69 $this->messageIndex->rebuild();
70 }
71
72 public function handleNullRevisionInsert( TranslatableBundle $bundle, RevisionRecord $revision ): void {
73 if ( !$bundle instanceof MessageBundle ) {
74 throw new InvalidArgumentException(
75 'Expected $bundle to be of type MessageBundle, got ' . get_class( $bundle )
76 );
77 }
78
79 $this->revTagStore->replaceTag( $bundle->getTitle(), RevTagStore::MB_VALID_TAG, $revision->getId() );
80 MessageBundle::clearSourcePageCache();
81 }
82
83 public function delete( Title $title ): void {
84 $this->revTagStore->removeTags( $title, RevTagStore::MB_VALID_TAG );
85
86 $bundle = new MessageBundle( $title );
87 TranslateMetadata::clearMetadata( $bundle->getMessageGroupId(), self::METADATA_KEYS_DB );
88
89 MessageBundle::clearSourcePageCache();
90
91 MessageGroups::singleton()->recache();
92 $this->messageIndex->rebuild();
93 }
94
95 public function validate( Title $pageTitle, MessageBundleContent $content ): void {
96 $content->validate();
97 // Verify that the language code is valid
98 $metadata = $content->getMetadata();
99 $sourceLanguageCode = $metadata->getSourceLanguageCode();
100 if ( $sourceLanguageCode ) {
101 if ( !$this->languageNameUtils->isKnownLanguageTag( $sourceLanguageCode ) ) {
102 throw new MalformedBundle(
103 'translate-messagebundle-error-invalid-sourcelanguage', [ $sourceLanguageCode ]
104 );
105 }
106
107 $revisionId = $this->revTagStore->getLatestRevisionWithTag( $pageTitle, RevTagStore::MB_VALID_TAG );
108 // If request wants the source language to be changed after creation, then throw an exception
109 if ( $revisionId !== null && $sourceLanguageCode !== $pageTitle->getPageLanguage()->getCode() ) {
110 throw new MalformedBundle( 'translate-messagebundle-sourcelanguage-changed' );
111 }
112
113 }
114
115 $priorityLanguageCodes = $metadata->getPriorityLanguages();
116 if ( $priorityLanguageCodes ) {
117 $invalidLanguageCodes = [];
118 foreach ( $priorityLanguageCodes as $languageCode ) {
119 if ( !is_string( $languageCode ) ) {
120 throw new MalformedBundle( 'translate-messagebundle-error-invalid-prioritylanguage-format' );
121 }
122
123 if ( !$this->languageNameUtils->isKnownLanguageTag( $languageCode ) ) {
124 $invalidLanguageCodes[] = $languageCode;
125 }
126 }
127
128 if ( $invalidLanguageCodes ) {
129 throw new MalformedBundle(
130 'translate-messagebundle-error-invalid-prioritylanguage',
131 [ Message::listParam( $invalidLanguageCodes ), count( $invalidLanguageCodes ) ]
132 );
133 }
134 }
135 }
136
137 public function save(
138 Title $pageTitle,
139 RevisionRecord $revisionRecord,
140 MessageBundleContent $content
141 ): void {
142 // Validate the content before saving
143 $this->validate( $pageTitle, $content );
144
145 $previousRevisionId = $this->revTagStore->getLatestRevisionWithTag( $pageTitle, RevTagStore::MB_VALID_TAG );
146 if ( $previousRevisionId !== null ) {
147 $this->revTagStore->removeTags( $pageTitle, RevTagStore::MB_VALID_TAG );
148 }
149
150 if ( $content->isValid() ) {
151 // Bundle is valid and contains translatable messages
152 $this->revTagStore->replaceTag( $pageTitle, RevTagStore::MB_VALID_TAG, $revisionRecord->getId() );
153 MessageBundle::clearSourcePageCache();
154
155 // Defer most of the heavy work to the job queue
156 $job = UpdateMessageBundleJob::newJob( $pageTitle, $revisionRecord->getId(), $previousRevisionId );
157
158 $this->jobQueue->push( $job );
159
160 // A new message bundle, set the source language.
161 $definedLanguageCode = $content->getMetadata()->getSourceLanguageCode();
162 $pageLanguageCode = $pageTitle->getPageLanguage()->getCode();
163 if ( $previousRevisionId === null ) {
164 if ( $definedLanguageCode !== $pageLanguageCode ) {
165 $context = RequestContext::getMain();
166 SpecialPageLanguage::changePageLanguage(
167 $context,
168 $pageTitle,
169 $definedLanguageCode,
170 wfMessage( 'translate-messagebundle-change-sourcelanguage' )->inContentLanguage()
171 );
172 }
173 }
174
175 // Save the metadata
176 $messageBundle = new MessageBundle( $pageTitle );
177 $groupId = $messageBundle->getMessageGroupId();
178
179 $metadata = $content->getMetadata();
180 $priorityForce = $metadata->areOnlyPriorityLanguagesAllowed() ? 'on' : false;
181 $priorityLanguages = $metadata->getPriorityLanguages();
182 $priorityLanguages = $priorityLanguages ? implode( ',', $priorityLanguages ) : false;
183
184 TranslateMetadata::set( $groupId, 'prioritylangs', $priorityLanguages );
185 TranslateMetadata::set( $groupId, 'priorityforce', $priorityForce );
186
187 $description = $metadata->getDescription();
188 TranslateMetadata::set( $groupId, 'description', $description ?? false );
189 }
190
191 // What should we do if there are no messages? Use the previous version? Remove the group?
192 // Currently, the bundle is removed from translation.
193 }
194}
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...
getTitle()
Return the title of the page where the translatable bundle is defined.
getMessageGroupId()
Return the message group id for the bundle Note that the message group id may refer to a message grou...
Creates a database of keys in all groups, so that namespace and key can be used to get the groups the...