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