Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
TranslateEditAddons.php
1<?php
2declare( strict_types = 1 );
3
4namespace MediaWiki\Extension\Translate\TranslatorInterface;
5
6use DeferredUpdates;
11use MediaWiki\Logger\LoggerFactory;
12use MediaWiki\MediaWikiServices;
13use MediaWiki\Revision\RevisionRecord;
14use MediaWiki\User\UserIdentity;
18use ParserOptions;
19use TextContent;
20use Title;
21use TTMServer;
22use User;
23use WikiPage;
24
44 public static function disallowLangTranslations(
45 Title $title,
46 User $user,
47 string $action,
48 &$result
49 ): bool {
50 if ( $action !== 'edit' ) {
51 return true;
52 }
53
54 $handle = new MessageHandle( $title );
55 if ( !$handle->isValid() ) {
56 return true;
57 }
58
59 if ( $user->isAllowed( 'translate-manage' ) ) {
60 return true;
61 }
62
63 $group = $handle->getGroup();
64 $languages = $group->getTranslatableLanguages();
65 $langCode = $handle->getCode();
66 if ( $languages !== null && $langCode && !isset( $languages[$langCode] ) ) {
67 $result = [ 'translate-language-disabled' ];
68 return false;
69 }
70
71 $groupId = $group->getId();
72 $checks = [
73 $groupId,
74 strtok( $groupId, '-' ),
75 '*'
76 ];
77
78 $disabledLanguages = Services::getInstance()->getConfigHelper()->getDisabledTargetLanguages();
79 foreach ( $checks as $check ) {
80 if ( isset( $disabledLanguages[$check][$langCode] ) ) {
81 $reason = $disabledLanguages[$check][$langCode];
82 $result = [ 'translate-page-disabled', $reason ];
83 return false;
84 }
85 }
86
87 return true;
88 }
89
94 public static function onSaveComplete(
95 WikiPage $wikiPage,
96 UserIdentity $userIdentity,
97 string $summary,
98 int $flags,
99 RevisionRecord $revisionRecord
100 ): void {
101 global $wgEnablePageTranslation;
102
103 $content = $wikiPage->getContent();
104
105 if ( !$content instanceof TextContent ) {
106 // Screw it, not interested
107 return;
108 }
109
110 $text = $content->getText();
111 $title = $wikiPage->getTitle();
112 $handle = new MessageHandle( $title );
113
114 if ( !$handle->isValid() ) {
115 return;
116 }
117
118 // Update it.
119 $revId = $revisionRecord->getId();
120
121 $fuzzy = self::checkNeedsFuzzy( $handle, $text );
122 self::updateFuzzyTag( $title, $revId, $fuzzy );
123
124 $group = $handle->getGroup();
125 // Update translation stats - source language should always be up to date
126 if ( $handle->getCode() !== $group->getSourceLanguage() ) {
127 // This will update in-process cache immediately, but the value is saved
128 // to the database in a deferred update. See MessageGroupStats::queueUpdates.
129 // In case an error happens before that, the stats may be stale, but that
130 // would be fixed by the next update or purge.
131 MessageGroupStats::clear( $handle );
132 }
133
134 // This job asks for stats, however the updated stats are written in a deferred update.
135 // To make it less likely that the job would be executed before the updated stats are
136 // written, create the job inside a deferred update too.
137 DeferredUpdates::addCallableUpdate(
138 static function () use ( $handle ) {
140 }
141 );
142 $mwServices = MediaWikiServices::getInstance();
143 $user = $mwServices->getUserFactory()
144 ->newFromId( $userIdentity->getId() );
145
146 if ( !$fuzzy ) {
147 Services::getInstance()->getHookRunner()
148 ->onTranslate_newTranslation( $handle, $revId, $text, $user );
149 }
150
151 TTMServer::onChange( $handle );
152
153 if ( $wgEnablePageTranslation && $handle->isPageTranslation() ) {
154 // Updates for translatable pages only
155 $minor = (bool)( $flags & EDIT_MINOR );
156 PageTranslationHooks::onSectionSave( $wikiPage, $user, $content,
157 $summary, $minor, $flags, $handle );
158 }
159 }
160
162 private static function checkNeedsFuzzy( MessageHandle $handle, string $text ): bool {
163 // Docs are exempt for checks
164 if ( $handle->isDoc() ) {
165 return false;
166 }
167
168 // Check for explicit tag.
169 if ( MessageHandle::hasFuzzyString( $text ) ) {
170 return true;
171 }
172
173 // Not all groups have validators
174 $group = $handle->getGroup();
175 $validator = $group->getValidator();
176
177 // no validator set
178 if ( !$validator ) {
179 return false;
180 }
181
182 $code = $handle->getCode();
183 $key = $handle->getKey();
184 $en = $group->getMessage( $key, $group->getSourceLanguage() );
185 $message = new FatMessage( $key, $en );
186 // Take the contents from edit field as a translation.
187 $message->setTranslation( $text );
188 if ( $message->definition() === null ) {
189 // This should NOT happen, but add a check since it seems to be happening
190 // See: https://phabricator.wikimedia.org/T255669
191 LoggerFactory::getInstance( 'Translate' )->warning(
192 'Message definition is empty! Title: {title}, group: {group}, key: {key}',
193 [
194 'title' => $handle->getTitle()->getPrefixedText(),
195 'group' => $group->getId(),
196 'key' => $key
197 ]
198 );
199 return false;
200 }
201
202 $validationResult = $validator->quickValidate( $message, $code );
203 return $validationResult->hasIssues();
204 }
205
211 private static function updateFuzzyTag( Title $title, int $revision, bool $fuzzy ): void {
212 $dbw = MediaWikiServices::getInstance()->getDBLoadBalancer()->getConnection( DB_PRIMARY );
213
214 $conds = [
215 'rt_page' => $title->getArticleID(),
216 'rt_type' => RevTagStore::FUZZY_TAG,
217 'rt_revision' => $revision
218 ];
219
220 // Replace the existing fuzzy tag, if any
221 if ( $fuzzy ) {
222 $index = array_keys( $conds );
223 $dbw->replace( 'revtag', [ $index ], $conds, __METHOD__ );
224 } else {
225 $dbw->delete( 'revtag', $conds, __METHOD__ );
226 }
227 }
228
235 public static function updateTransverTag(
236 MessageHandle $handle,
237 int $revision,
238 string $text,
239 User $user
240 ): bool {
241 if ( $user->isAllowed( 'bot' ) ) {
242 return false;
243 }
244
245 $group = $handle->getGroup();
246
247 $title = $handle->getTitle();
248 $name = $handle->getKey() . '/' . $group->getSourceLanguage();
249 $definitionTitle = Title::makeTitleSafe( $title->getNamespace(), $name );
250 if ( !$definitionTitle || !$definitionTitle->exists() ) {
251 return true;
252 }
253
254 $definitionRevision = $definitionTitle->getLatestRevID();
255 $dbw = MediaWikiServices::getInstance()
256 ->getDBLoadBalancer()
257 ->getConnection( DB_PRIMARY );
258
259 $conds = [
260 'rt_page' => $title->getArticleID(),
261 'rt_type' => RevTagStore::TRANSVER_PROP,
262 'rt_revision' => $revision,
263 'rt_value' => $definitionRevision,
264 ];
265 $index = [ 'rt_type', 'rt_page', 'rt_revision' ];
266 $dbw->replace( 'revtag', [ $index ], $conds, __METHOD__ );
267
268 return true;
269 }
270
272 public static function disablePreSaveTransform(
273 WikiPage $wikiPage,
274 ParserOptions $popts
275 ): void {
276 global $wgTranslateUsePreSaveTransform;
277
278 if ( !$wgTranslateUsePreSaveTransform ) {
279 $handle = new MessageHandle( $wikiPage->getTitle() );
280 if ( $handle->isMessageNamespace() && !$handle->isDoc() ) {
281 $popts->setPreSaveTransform( false );
282 }
283 }
284 }
285}
Class to manage revision tags for translatable bundles.
Message object where you can directly set the translation.
static onSectionSave(WikiPage $wikiPage, User $user, TextContent $content, $summary, $minor, $flags, MessageHandle $handle)
This is triggered after an edit to translation unit page.
Definition Hooks.php:381
Minimal service container.
Definition Services.php:44
Various editing enhancements to the edit page interface.
static disablePreSaveTransform(WikiPage $wikiPage, ParserOptions $popts)
Hook: ArticlePrepareTextForEdit.
static onSaveComplete(WikiPage $wikiPage, UserIdentity $userIdentity, string $summary, int $flags, RevisionRecord $revisionRecord)
Runs message checks, adds tp:transver tags and updates statistics.
static disallowLangTranslations(Title $title, User $user, string $action, &$result)
Prevent translations to non-translatable languages for the group Hook: getUserPermissionsErrorsExpens...
static updateTransverTag(MessageHandle $handle, int $revision, string $text, User $user)
Adds tag which identifies the revision of source message at that time.
Logic for handling automatic message group state changes.
static onChange(MessageHandle $handle)
Hook: TranslateEventTranslationReview and also on translation changes.
This class abstract MessageGroup statistics calculation and storing.
Class for pointing to messages, like Title class is for titles.
Some general static methods for instantiating TTMServer and helpers.
Definition TTMServer.php:19