67 global $wgHooks, $wgTranslateYamlLibrary;
73 if ( !defined(
'TRANSLATE_FUZZY' ) ) {
74 define(
'TRANSLATE_FUZZY',
'!!FUZZY!!' );
77 if ( $wgTranslateYamlLibrary ===
null ) {
78 $wgTranslateYamlLibrary = function_exists(
'yaml_parse' ) ?
'phpyaml' :
'spyc';
81 $wgHooks[
'PageSaveComplete'][] =
'TranslateEditAddons::onSaveComplete';
84 global $wgEnablePageTranslation;
85 if ( $wgEnablePageTranslation ) {
87 global $wgSpecialPages, $wgAvailableRights;
88 $wgSpecialPages[
'PageTranslation'] = [
89 'class' => PageTranslationSpecialPage::class,
93 'Translate:TranslationUnitStoreFactory',
94 'Translate:TranslatablePageParser',
99 $wgSpecialPages[
'PageTranslationDeletePage'] = [
100 'class' => DeleteTranslatableBundleSpecialPage::class,
104 'Translate:TranslatableBundleFactory',
105 'Translate:SubpageListBuilder',
111 $wgAvailableRights[] =
'pagetranslation';
113 $wgSpecialPages[
'PageMigration'] = MigrateTranslatablePageSpecialPage::class;
114 $wgSpecialPages[
'PagePreparation'] = PrepareTranslatablePageSpecialPage::class;
116 global $wgActionFilteredLogs, $wgLogActionsHandlers, $wgLogTypes;
125 $wgLogTypes[] =
'pagetranslation';
126 $wgLogActionsHandlers[
'pagetranslation/mark'] = TranslatableBundleLogFormatter::class;
127 $wgLogActionsHandlers[
'pagetranslation/unmark'] = TranslatableBundleLogFormatter::class;
128 $wgLogActionsHandlers[
'pagetranslation/moveok'] = TranslatableBundleLogFormatter::class;
129 $wgLogActionsHandlers[
'pagetranslation/movenok'] = TranslatableBundleLogFormatter::class;
130 $wgLogActionsHandlers[
'pagetranslation/deletelok'] = TranslatableBundleLogFormatter::class;
131 $wgLogActionsHandlers[
'pagetranslation/deletefok'] = TranslatableBundleLogFormatter::class;
132 $wgLogActionsHandlers[
'pagetranslation/deletelnok'] = TranslatableBundleLogFormatter::class;
133 $wgLogActionsHandlers[
'pagetranslation/deletefnok'] = TranslatableBundleLogFormatter::class;
134 $wgLogActionsHandlers[
'pagetranslation/encourage'] = TranslatableBundleLogFormatter::class;
135 $wgLogActionsHandlers[
'pagetranslation/discourage'] = TranslatableBundleLogFormatter::class;
136 $wgLogActionsHandlers[
'pagetranslation/prioritylanguages'] = TranslatableBundleLogFormatter::class;
137 $wgLogActionsHandlers[
'pagetranslation/associate'] = TranslatableBundleLogFormatter::class;
138 $wgLogActionsHandlers[
'pagetranslation/dissociate'] = TranslatableBundleLogFormatter::class;
139 $wgActionFilteredLogs[
'pagetranslation'] = [
140 'mark' => [
'mark' ],
141 'unmark' => [
'unmark' ],
142 'move' => [
'moveok',
'movenok' ],
143 'delete' => [
'deletefok',
'deletefnok',
'deletelok',
'deletelnok' ],
144 'encourage' => [
'encourage' ],
145 'discourage' => [
'discourage' ],
146 'prioritylanguages' => [
'prioritylanguages' ],
147 'aggregategroups' => [
'associate',
'dissociate' ],
150 $wgLogTypes[] =
'messagebundle';
151 $wgLogActionsHandlers[
'messagebundle/moveok'] = TranslatableBundleLogFormatter::class;
152 $wgLogActionsHandlers[
'messagebundle/movenok'] = TranslatableBundleLogFormatter::class;
153 $wgLogActionsHandlers[
'messagebundle/deletefok'] = TranslatableBundleLogFormatter::class;
154 $wgLogActionsHandlers[
'messagebundle/deletefnok'] = TranslatableBundleLogFormatter::class;
155 $wgActionFilteredLogs[
'messagebundle'] = [
156 'move' => [
'moveok',
'movenok' ],
157 'delete' => [
'deletefok',
'deletefnok' ],
160 global $wgJobClasses;
161 $wgJobClasses[
'RenderTranslationPageJob'] = RenderTranslationPageJob::class;
163 $wgJobClasses[
'TranslateRenderJob'] = RenderTranslationPageJob::class;
165 $wgJobClasses[
'TranslatableBundleMoveJob'] = MoveTranslatableBundleJob::class;
166 $wgJobClasses[
'MoveTranslatableBundleJob'] = MoveTranslatableBundleJob::class;
168 $wgJobClasses[
'TranslatableBundleDeleteJob'] = DeleteTranslatableBundleJob::class;
169 $wgJobClasses[
'DeleteTranslatableBundleJob'] = DeleteTranslatableBundleJob::class;
171 $wgJobClasses[
'UpdateTranslatablePageJob'] = UpdateTranslatablePageJob::class;
173 $wgJobClasses[
'TranslationsUpdateJob'] = UpdateTranslatablePageJob::class;
176 global $wgNamespacesWithSubpages, $wgNamespaceProtection;
177 global $wgTranslateMessageNamespaces;
179 $wgNamespacesWithSubpages[NS_TRANSLATIONS] =
true;
180 $wgNamespacesWithSubpages[NS_TRANSLATIONS_TALK] =
true;
183 $wgNamespaceProtection[NS_TRANSLATIONS] = [
'translate' ];
184 $wgTranslateMessageNamespaces[] = NS_TRANSLATIONS;
189 $wgHooks[
'BeforePageDisplay'][] = [ Hooks::class,
'onBeforePageDisplay' ];
192 $wgHooks[
'VisualEditorBeforeEditor'][] = [ Hooks::class,
'onVisualEditorBeforeEditor' ];
195 $wgHooks[
'MultiContentSave'][] = [ Hooks::class,
'tpSyntaxCheck' ];
196 $wgHooks[
'EditFilterMergedContent'][] =
197 [ Hooks::class,
'tpSyntaxCheckForEditContent' ];
200 $wgHooks[
'PageSaveComplete'][] = [ Hooks::class,
'addTranstagAfterSave' ];
202 $wgHooks[
'RevisionRecordInserted'][] = [ Hooks::class,
'updateTranstagOnNullRevisions' ];
205 $wgHooks[
'ParserFirstCallInit'][] =
'TranslateHooks::setupParserHooks';
206 $wgHooks[
'LanguageLinks'][] = [ Hooks::class,
'addLanguageLinks' ];
207 $wgHooks[
'SkinTemplateGetLanguageLink'][] = [ Hooks::class,
'formatLanguageLink' ];
210 $wgHooks[
'ParserBeforeInternalParse'][] = [ Hooks::class,
'renderTagPage' ];
212 $wgHooks[
'ParserBeforePreprocess'][] = [ Hooks::class,
'preprocessTagPage' ];
213 $wgHooks[
'ParserOutputPostCacheTransform'][] =
214 [ Hooks::class,
'onParserOutputPostCacheTransform' ];
216 $wgHooks[
'BeforeParserFetchTemplateRevisionRecord'][] =
217 [ Hooks::class,
'fetchTranslatableTemplateAndTitle' ];
220 $wgHooks[
'PageContentLanguage'][] = [ Hooks::class,
'onPageContentLanguage' ];
223 $wgHooks[
'getUserPermissionsErrorsExpensive'][] =
224 [ Hooks::class,
'onGetUserPermissionsErrorsExpensive' ];
226 $wgHooks[
'getUserPermissionsErrorsExpensive'][] =
227 [ Hooks::class,
'preventDirectEditing' ];
230 $wgHooks[
'ArticleViewHeader'][] = [ Hooks::class,
'translatablePageHeader' ];
233 $wgHooks[
'TitleGetEditNotices'][] = [ Hooks::class,
'onTitleGetEditNotices' ];
236 $wgHooks[
'SpecialPage_initList'][] = [ Hooks::class,
'replaceMovePage' ];
238 $wgHooks[
'getUserPermissionsErrorsExpensive'][] =
239 [ Hooks::class,
'lockedPagesCheck' ];
241 $wgHooks[
'ArticleConfirmDelete'][] = [ Hooks::class,
'disableDelete' ];
244 $wgHooks[
'SkinSubPageSubtitle'][] = [ Hooks::class,
'replaceSubtitle' ];
247 $wgHooks[
'SkinTemplateNavigation::Universal'][] = [ Hooks::class,
'translateTab' ];
250 $wgHooks[
'PageMoveComplete'][] = [ Hooks::class,
'onMovePageTranslationUnits' ];
253 $wgHooks[
'ArticleDeleteComplete'][] = [ Hooks::class,
'onDeleteTranslationUnit' ];
256 global $wgTranslateUseSandbox;
257 if ( $wgTranslateUseSandbox ) {
258 global $wgSpecialPages, $wgAvailableRights, $wgDefaultUserOptions;
260 $wgSpecialPages[
'ManageTranslatorSandbox'] = [
261 'class' => ManageTranslatorSandboxSpecialPage::class,
263 'Translate:TranslationStashReader',
268 return new ServiceOptions(
269 ManageTranslatorSandboxSpecialPage::CONSTRUCTOR_OPTIONS,
270 MediaWikiServices::getInstance()->getMainConfig()
275 $wgSpecialPages[
'TranslationStash'] = [
276 'class' => TranslationStashSpecialPage::class,
279 'Translate:TranslationStashReader',
284 return new ServiceOptions(
285 TranslationStashSpecialPage::CONSTRUCTOR_OPTIONS,
286 MediaWikiServices::getInstance()->getMainConfig()
291 $wgDefaultUserOptions[
'translate-sandbox'] =
'';
293 $wgAvailableRights[] =
'translate-sandboxmanage';
295 $wgHooks[
'GetPreferences'][] =
'TranslateSandbox::onGetPreferences';
296 $wgHooks[
'UserGetRights'][] =
'TranslateSandbox::enforcePermissions';
297 $wgHooks[
'ApiCheckCanExecute'][] =
'TranslateSandbox::onApiCheckCanExecute';
299 global $wgLogTypes, $wgLogActionsHandlers;
301 $wgLogTypes[] =
'translatorsandbox';
303 $wgLogActionsHandlers[
'translatorsandbox/promoted'] =
'TranslateLogFormatter';
304 $wgLogActionsHandlers[
'translatorsandbox/rejected'] =
'TranslateLogFormatter';
308 $wgLogActionsHandlers[
'newusers/tsbpromoted'] =
'LogFormatter';
310 global $wgJobClasses;
311 $wgJobClasses[
'TranslateSandboxEmailJob'] =
'TranslateSandboxEmailJob';
313 global $wgAPIModules;
314 $wgAPIModules[
'translationstash'] = [
315 'class' => TranslationStashActionApi::class,
321 $wgAPIModules[
'translatesandbox'] = [
322 'class' => TranslatorSandboxActionApi::class,
326 'UserOptionsManager',
332 return new ServiceOptions(
333 TranslatorSandboxActionApi::CONSTRUCTOR_OPTIONS,
334 MediaWikiServices::getInstance()->getMainConfig()
341 global $wgNamespaceRobotPolicies;
342 $wgNamespaceRobotPolicies[NS_TRANSLATIONS] =
'noindex';
345 global $wgTranslateTranslationDefaultService,
346 $wgTranslateTranslationServices;
347 if ( $wgTranslateTranslationDefaultService ===
true ) {
348 $wgTranslateTranslationDefaultService =
'TTMServer';
349 if ( !isset( $wgTranslateTranslationServices[
'TTMServer'] ) ) {
350 $wgTranslateTranslationServices[
'TTMServer'] = [
353 'type' =>
'ttmserver',
359 $wgHooks[
'SidebarBeforeOutput'][] =
'TranslateToolbox::toolboxAllTranslations';
458 $dir = __DIR__ .
'/sql';
459 $dbType = $updater->getDB()->getType();
461 if ( $dbType ===
'mysql' || $dbType ===
'sqlite' ) {
462 $updater->addExtensionTable(
463 'translate_sections',
464 "{$dir}/{$dbType}/translate_sections.sql"
466 $updater->addExtensionTable(
468 "{$dir}/{$dbType}/revtag.sql"
470 $updater->addExtensionTable(
471 'translate_groupstats',
472 "{$dir}/{$dbType}/translate_groupstats.sql"
474 $updater->addExtensionTable(
476 "{$dir}/{$dbType}/translate_reviews.sql"
478 $updater->addExtensionTable(
479 'translate_groupreviews',
480 "{$dir}/{$dbType}/translate_groupreviews.sql"
482 $updater->addExtensionTable(
484 "{$dir}/{$dbType}/translate_tm.sql"
486 $updater->addExtensionTable(
487 'translate_metadata',
488 "{$dir}/{$dbType}/translate_metadata.sql"
490 $updater->addExtensionTable(
491 'translate_messageindex',
492 "{$dir}/{$dbType}/translate_messageindex.sql"
494 $updater->addExtensionTable(
496 "{$dir}/{$dbType}/translate_stash.sql"
500 $updater->addExtensionUpdate( [
503 'trr_user_page_revision',
506 "$dir/translate_reviews-patch-01-primary-key.sql",
510 $updater->addExtensionTable(
512 "{$dir}/{$dbType}/translate_cache.sql"
515 if ( $dbType ===
'mysql' ) {
517 $updater->modifyExtensionField(
520 "{$dir}/{$dbType}/translate_cache-alter-varbinary.sql"
523 } elseif ( $dbType ===
'postgres' ) {
524 $updater->addExtensionTable(
525 'translate_sections',
526 "{$dir}/{$dbType}/tables-generated.sql"
528 $updater->addExtensionUpdate( [
529 'changeField',
'translate_cache',
'tc_exptime',
'TIMESTAMPTZ',
'th_timestamp::timestamp with time zone'
534 $updater->dropExtensionIndex(
535 'translate_messageindex',
537 "{$dir}/{$dbType}/patch-translate_messageindex-unique-to-pk.sql"
539 $updater->dropExtensionIndex(
542 "{$dir}/{$dbType}/patch-translate_tmt-unique-to-pk.sql"
544 $updater->dropExtensionIndex(
546 'rt_type_page_revision',
547 "{$dir}/{$dbType}/patch-revtag-unique-to-pk.sql"
637 SpecialSearch $search,
643 if ( $profile !==
'translation' ) {
648 $href = SpecialPage::getTitleFor(
'SearchTranslations' )
649 ->getFullUrl( [
'query' => $term ] );
650 $form = Html::successBox(
651 $search->msg(
'translate-searchprofile-note', $href )->parse(),
658 if ( !$search->getSearchEngine()->supports(
'title-suffix-filter' ) ) {
663 foreach ( $opts as $key => $value ) {
664 $hidden .= Html::hidden( $key, $value );
667 $context = $search->getContext();
668 $code = $context->getLanguage()->getCode();
669 $selected = $context->getRequest()->getVal(
'languagefilter' );
671 $languages = TranslateUtils::getLanguageNames( $code );
674 $selector =
new XmlSelect(
'languagefilter',
'languagefilter' );
675 $selector->setDefault( $selected );
676 $selector->addOption( wfMessage(
'translate-search-nofilter' )->text(),
'-' );
677 foreach ( $languages as $code => $name ) {
678 $selector->addOption(
"$code - $name", $code );
681 $selector = $selector->getHTML();
684 wfMessage(
'translate-search-languagefilter' )->text(),
687 $params = [
'id' =>
'mw-searchoptions' ];
689 $form = Xml::fieldset(
false,
false, $params ) .
690 $hidden . $label . $selector .
691 Html::closeElement(
'fieldset' );
758 public static function addConfig( array &$vars, OutputPage $out ) {
759 $title = $out->getTitle();
760 [ $alias, ] = MediaWikiServices::getInstance()
761 ->getSpecialPageFactory()->resolveAlias( $title->getText() );
763 if ( $title->isSpecialPage()
764 && ( $alias ===
'Translate'
765 || $alias ===
'TranslationStash'
766 || $alias ===
'SearchTranslations' )
768 global $wgTranslateDocumentationLanguageCode, $wgTranslatePermissionUrl,
769 $wgTranslateUseSandbox;
770 $vars[
'TranslateRight'] = $out->getUser()->isAllowed(
'translate' );
771 $vars[
'TranslateMessageReviewRight'] =
772 $out->getUser()->isAllowed(
'translate-messagereview' );
773 $vars[
'DeleteRight'] = $out->getUser()->isAllowed(
'delete' );
774 $vars[
'TranslateManageRight'] = $out->getUser()->isAllowed(
'translate-manage' );
775 $vars[
'wgTranslateDocumentationLanguageCode'] = $wgTranslateDocumentationLanguageCode;
776 $vars[
'wgTranslatePermissionUrl'] = $wgTranslatePermissionUrl;
777 $vars[
'wgTranslateUseSandbox'] = $wgTranslateUseSandbox;
928 Status $status, $summary, User $user
930 if ( !$content instanceof TextContent ) {
935 $text = $content->getText();
936 $title = $context->getTitle();
939 if ( !$handle->isValid() ) {
944 if ( $user->isAllowed(
'translate-manage' ) || $user->equals( FuzzyBot::getUser() ) ) {
949 if ( $handle->isMessageNamespace() && !$handle->isDoc() ) {
950 $group = $handle->getGroup();
952 if ( is_callable( [ $group,
'getMessageContent' ] ) ) {
954 $definition = $group->getMessageContent( $handle );
956 $definition = $group->getMessage( $handle->getKey(), $group->getSourceLanguage() );
959 $message =
new FatMessage( $handle->getKey(), $definition );
960 $message->setTranslation( $text );
962 $messageValidator = $group->getValidator();
963 if ( !$messageValidator ) {
967 $validationResponse = $messageValidator->validateMessage( $message, $handle->getCode() );
968 if ( $validationResponse->hasErrors() ) {
969 $status->fatal(
new ApiRawMessage(
970 $context->msg(
'translate-syntax-error' )->parse(),
971 'translate-validation-failed',
974 'errors' => $validationResponse->getDescriptiveErrors( $context ),
975 'warnings' => $validationResponse->getDescriptiveWarnings( $context )