35 protected $nondefaults = [];
38 private $contentLanguage;
40 private $languageFactory;
42 private $languageNameUtils;
44 public function __construct(
45 Language $contentLanguage,
46 LanguageFactory $languageFactory,
47 LanguageNameUtils $languageNameUtils
49 parent::__construct(
'Translate' );
50 $this->contentLanguage = $contentLanguage;
51 $this->languageFactory = $languageFactory;
52 $this->languageNameUtils = $languageNameUtils;
55 public function doesWrites() {
59 protected function getGroupName() {
70 $out = $this->getOutput();
71 $out->addModuleStyles( [
72 'ext.translate.special.translate.styles',
79 $this->setup( $parameters );
82 if ( $this->getRequest()->getText(
'taction' ) ===
'export' ) {
83 $exportPage = SpecialPage::getTitleFor(
'ExportTranslations' );
84 $out->redirect( $exportPage->getLocalURL( $this->nondefaults ) );
87 $out->addModules(
'ext.translate.special.translate' );
88 $out->addJsConfigVars(
89 'wgTranslateLanguages',
90 Utilities::getLanguageNames( LanguageNameUtils::AUTONYMS )
93 $out->addHTML( Html::openElement(
'div', [
94 'class' =>
'grid ext-translate-container',
97 $out->addHTML( $this->tuxSettingsForm() );
98 $out->addHTML( $this->messageSelector() );
100 $table =
new MessageTable( $this->getContext(), $this->group, $this->options[
'language'] );
101 $output = $table->fullTable();
103 $out->addHTML( $output );
104 $out->addHTML( Html::closeElement(
'div' ) );
107 protected function setup( ?
string $parameters ): void {
108 $request = $this->getRequest();
111 'language' => $this->getLanguage()->getCode(),
112 'group' =>
'!additions',
118 $parameters = $parameters !==
null ? array_map(
'trim', explode(
';', $parameters ) ) : [];
121 foreach ( $parameters as $_ ) {
126 if ( strpos( $_,
'=' ) !==
false ) {
127 [ $key, $value ] = array_map(
'trim', explode(
'=', $_, 2 ) );
133 $pars[$key] = $value;
136 foreach ( $defaults as $v => $t ) {
137 if ( is_bool( $t ) ) {
138 $r = isset( $pars[$v] ) ? (bool)$pars[$v] : $defaults[$v];
139 $r = $request->getBool( $v, $r );
140 } elseif ( is_int( $t ) ) {
141 $r = isset( $pars[$v] ) ? (int)$pars[$v] : $defaults[$v];
142 $r = $request->getInt( $v, $r );
143 } elseif ( is_string( $t ) ) {
144 $r = isset( $pars[$v] ) ? (string)$pars[$v] : $defaults[$v];
145 $r = $request->getText( $v, $r );
148 if ( !isset( $r ) ) {
149 throw new MWException(
'$r was not set' );
152 if ( $defaults[$v] !== $r ) {
153 $nondefaults[$v] = $r;
157 $this->defaults = $defaults;
158 $this->nondefaults = $nondefaults;
159 Hooks::run(
'TranslateGetSpecialTranslateOptions', [ &$defaults, &$nondefaults ] );
161 $this->options = $nondefaults + $defaults;
162 $this->group = MessageGroups::getGroup( $this->options[
'group'] );
163 if ( $this->group ) {
164 $this->options[
'group'] = $this->group->getId();
166 $this->group = MessageGroups::getGroup( $this->defaults[
'group'] );
169 if ( !$this->languageNameUtils->isKnownLanguageTag( $this->options[
'language'] ) ) {
170 $this->options[
'language'] = $this->defaults[
'language'];
173 if ( MessageGroups::isDynamic( $this->group ) ) {
175 $this->group->setLanguage( $this->options[
'language'] );
179 protected function tuxSettingsForm(): string {
180 $nojs = Html::errorBox(
181 $this->msg(
'tux-nojs' )->plain(),
186 $attrs = [
'class' =>
'row tux-editor-header' ];
187 $selectors = $this->tuxGroupSelector() .
188 $this->tuxLanguageSelector() .
189 $this->tuxGroupDescription() .
190 $this->tuxWorkflowSelector() .
191 $this->tuxGroupWarning();
193 return Html::rawElement(
'div', $attrs, $selectors ) . $nojs;
196 protected function messageSelector(): string {
197 $output = Html::openElement(
'div', [
'class' =>
'row tux-messagetable-header hide' ] );
198 $output .= Html::openElement(
'div', [
'class' =>
'nine columns' ] );
199 $output .= Html::openElement(
'ul', [
'class' =>
'row tux-message-selector' ] );
200 $userId = $this->getUser()->getId();
203 'untranslated' =>
'!translated',
204 'outdated' =>
'fuzzy',
205 'translated' =>
'translated',
206 'unproofread' =>
"translated|!reviewer:$userId|!last-translator:$userId",
209 $params = $this->nondefaults;
211 foreach ( $tabs as $tab => $filter ) {
218 $tabClass =
"tux-tab-$tab";
219 $taskParams = [
'filter' => $filter ] + $params;
220 ksort( $taskParams );
221 $href = $this->getPageTitle()->getLocalURL( $taskParams );
222 $link = Html::element(
'a', [
'href' => $href ], $this->msg( $tabClass )->text() );
223 $output .= Html::rawElement(
'li', [
224 'class' =>
'column ' . $tabClass,
225 'data-filter' => $filter,
226 'data-title' => $tab,
236 'optional' => $this->msg(
'tux-message-filter-optional-messages-label' )->text(),
239 $container = Html::openElement(
'ul', [
'class' =>
'column tux-message-selector' ] );
240 foreach ( $options as $optFilter => $optLabel ) {
241 $container .= Html::rawElement(
'li',
242 [
'class' =>
'column' ],
246 "tux-option-$optFilter",
247 isset( $this->nondefaults[$optFilter] ),
248 [
'data-filter' => $optFilter ]
253 $container .= Html::closeElement(
'ul' );
256 $output .= Html::openElement(
'li', [
'class' =>
'column more' ] ) .
259 Html::closeElement(
'li' );
261 $output .= Html::closeElement(
'ul' );
262 $output .= Html::closeElement(
'div' );
263 $output .= Html::openElement(
'div', [
'class' =>
'three columns' ] );
264 $output .= Html::rawElement(
266 [
'class' =>
'tux-message-filter-wrapper' ],
267 Html::element(
'input', [
268 'class' =>
'tux-message-filter-box',
270 'placeholder' => $this->msg(
'tux-message-filter-placeholder' )->text()
275 $output .= Html::closeElement(
'div' ) . Html::closeElement(
'div' );
280 protected function tuxGroupSelector(): string {
281 $groupClass = [
'grouptitle',
'grouplink' ];
283 $groupClass[] =
'tux-breadcrumb__item--aggregate';
287 $output = Html::openElement(
'div', [
288 'class' =>
'eight columns tux-breadcrumb',
289 'data-language' => $this->options[
'language'],
291 Html::element(
'span',
292 [
'class' =>
'grouptitle' ],
293 $this->msg(
'translate-msggroupselector-projects' )->text()
295 Html::element(
'span',
296 [
'class' =>
'grouptitle grouplink tux-breadcrumb__item--aggregate' ],
297 $this->msg(
'translate-msggroupselector-search-all' )->text()
299 Html::element(
'span',
301 'class' => $groupClass,
302 'data-msggroupid' => $this->group->getId(),
304 $this->group->getLabel( $this->getContext() )
306 Html::closeElement(
'div' );
311 protected function tuxLanguageSelector(): string {
312 global $wgTranslateDocumentationLanguageCode;
314 if ( $this->options[
'language'] === $wgTranslateDocumentationLanguageCode ) {
315 $targetLangName = $this->msg(
'translate-documentation-language' )->text();
316 $targetLanguage = $this->contentLanguage;
318 $targetLangName = $this->languageNameUtils->getLanguageName( $this->options[
'language'] );
319 $targetLanguage = $this->languageFactory->getLanguage( $this->options[
'language'] );
322 $label = Html::element(
'span', [], $this->msg(
'tux-languageselector' )->text() );
324 $languageIcon = Html::element(
326 [
'class' =>
'ext-translate-language-icon' ]
329 $targetLanguageName = Html::element(
332 'class' =>
'ext-translate-target-language',
333 'dir' => $targetLanguage->getDir(),
334 'lang' => $targetLanguage->getHtmlCode()
339 $expandIcon = Html::element(
341 [
'class' =>
'ext-translate-language-selector-expand' ]
344 $value = Html::rawElement(
347 'class' =>
'uls mw-ui-button',
349 'title' => $this->msg(
'tux-select-target-language' )->text()
351 $languageIcon . $targetLanguageName . $expandIcon
354 return Html::rawElement(
356 [
'class' =>
'four columns ext-translate-language-selector' ],
361 protected function tuxGroupDescription(): string {
365 [
'class' =>
'twelve columns description' ],
366 $this->getGroupDescription( $this->group )
370 protected function getGroupDescription(
MessageGroup $group ): string {
371 $description = $group->getDescription( $this->getContext() );
372 return $description ===
null ?
373 '' : $this->getOutput()->parseAsInterface( $description );
376 protected function tuxGroupWarning(): string {
377 if ( $this->options[
'group'] ===
'' ) {
378 return Html::warningBox(
379 $this->msg(
'tux-translate-page-no-such-group' )->parse(),
380 'tux-group-warning twelve column'
385 return Html::warningBox(
'',
'tux-group-warning twelve column' );
388 protected function tuxWorkflowSelector(): string {
389 return Html::element(
'div', [
'class' =>
'tux-workflow twelve columns' ] );
396 public static function tabify( Skin $skin, array &$tabs ): bool {
397 $title = $skin->getTitle();
398 if ( !$title->isSpecialPage() ) {
401 [ $alias, $sub ] = MediaWikiServices::getInstance()
402 ->getSpecialPageFactory()->resolveAlias( $title->getText() );
404 $pagesInGroup = [
'Translate',
'LanguageStats',
'MessageGroupStats',
'ExportTranslations' ];
405 if ( !in_array( $alias, $pagesInGroup,
true ) ) {
411 if ( $sub !==
null && trim( $sub ) !==
'' ) {
412 if ( $alias ===
'Translate' || $alias ===
'MessageGroupStats' ) {
413 $params[
'group'] = $sub;
414 } elseif ( $alias ===
'LanguageStats' ) {
416 $params[
'language'] = $sub;
420 $request = $skin->getRequest();
422 $params[
'language'] = $request->getRawVal(
'language' ) ??
'';
423 $params[
'group'] = $request->getRawVal(
'group' ) ??
'';
426 $params = array_filter( $params,
static function (
string $param ) {
427 return $param !==
'';
430 $translate = SpecialPage::getTitleFor(
'Translate' );
431 $languagestats = SpecialPage::getTitleFor(
'LanguageStats' );
432 $messagegroupstats = SpecialPage::getTitleFor(
'MessageGroupStats' );
435 $tabs[
'namespaces'] = [];
437 $tabs[
'namespaces'][
'translate'] = [
438 'text' => wfMessage(
'translate-taction-translate' )->text(),
439 'href' => $translate->getLocalURL( $params ),
440 'class' =>
'tux-tab',
443 if ( $alias ===
'Translate' ) {
444 $tabs[
'namespaces'][
'translate'][
'class'] .=
' selected';
447 $tabs[
'views'][
'lstats'] = [
448 'text' => wfMessage(
'translate-taction-lstats' )->text(),
449 'href' => $languagestats->getLocalURL( $params ),
450 'class' =>
'tux-tab',
452 if ( $alias ===
'LanguageStats' ) {
453 $tabs[
'views'][
'lstats'][
'class'] .=
' selected';
456 $tabs[
'views'][
'mstats'] = [
457 'text' => wfMessage(
'translate-taction-mstats' )->text(),
458 'href' => $messagegroupstats->getLocalURL( $params ),
459 'class' =>
'tux-tab',
462 if ( $alias ===
'MessageGroupStats' ) {
463 $tabs[
'views'][
'mstats'][
'class'] .=
' selected';
466 $tabs[
'views'][
'export'] = [
467 'text' => wfMessage(
'translate-taction-export' )->text(),
468 'href' => SpecialPage::getTitleFor(
'ExportTranslations' )->getLocalURL( $params ),
469 'class' =>
'tux-tab',
return[ 'Translate:ConfigHelper'=> static function():ConfigHelper { return new ConfigHelper();}, 'Translate:CsvTranslationImporter'=> static function(MediaWikiServices $services):CsvTranslationImporter { return new CsvTranslationImporter( $services->getWikiPageFactory());}, 'Translate:EntitySearch'=> static function(MediaWikiServices $services):EntitySearch { return new EntitySearch($services->getMainWANObjectCache(), $services->getCollationFactory() ->makeCollation( 'uca-default-u-kn'), MessageGroups::singleton(), $services->getNamespaceInfo(), $services->get( 'Translate:MessageIndex'), $services->getTitleParser(), $services->getTitleFormatter());}, 'Translate:ExternalMessageSourceStateImporter'=> static function(MediaWikiServices $services):ExternalMessageSourceStateImporter { return new ExternalMessageSourceStateImporter($services->getMainConfig(), $services->get( 'Translate:GroupSynchronizationCache'), $services->getJobQueueGroup(), LoggerFactory::getInstance( 'Translate.GroupSynchronization'), $services->get( 'Translate:MessageIndex'));}, 'Translate:GroupSynchronizationCache'=> static function(MediaWikiServices $services):GroupSynchronizationCache { return new GroupSynchronizationCache( $services->get( 'Translate:PersistentCache'));}, 'Translate:MessageBundleStore'=> static function(MediaWikiServices $services):MessageBundleStore { return new MessageBundleStore(new RevTagStore(), $services->getJobQueueGroup(), $services->getLanguageNameUtils(), $services->get( 'Translate:MessageIndex'));}, 'Translate:MessageGroupReview'=> static function(MediaWikiServices $services):MessageGroupReview { return new MessageGroupReview($services->getDBLoadBalancer(), $services->getHookContainer());}, 'Translate:MessageGroupStatsTableFactory'=> static function(MediaWikiServices $services):MessageGroupStatsTableFactory { return new MessageGroupStatsTableFactory($services->get( 'Translate:ProgressStatsTableFactory'), $services->getDBLoadBalancer(), $services->getLinkRenderer(), $services->getMainConfig() ->get( 'TranslateWorkflowStates') !==false);}, 'Translate:MessageIndex'=> static function(MediaWikiServices $services):MessageIndex { $params=$services->getMainConfig() ->get( 'TranslateMessageIndex');if(is_string( $params)) { $params=(array) $params;} $class=array_shift( $params);return new $class( $params);}, 'Translate:MessagePrefixStats'=> static function(MediaWikiServices $services):MessagePrefixStats { return new MessagePrefixStats( $services->getTitleParser());}, 'Translate:ParsingPlaceholderFactory'=> static function():ParsingPlaceholderFactory { return new ParsingPlaceholderFactory();}, 'Translate:PersistentCache'=> static function(MediaWikiServices $services):PersistentCache { return new PersistentDatabaseCache($services->getDBLoadBalancer(), $services->getJsonCodec());}, 'Translate:ProgressStatsTableFactory'=> static function(MediaWikiServices $services):ProgressStatsTableFactory { return new ProgressStatsTableFactory($services->getLinkRenderer(), $services->get( 'Translate:ConfigHelper'));}, 'Translate:SubpageListBuilder'=> static function(MediaWikiServices $services):SubpageListBuilder { return new SubpageListBuilder($services->get( 'Translate:TranslatableBundleFactory'), $services->getLinkBatchFactory());}, 'Translate:TranslatableBundleFactory'=> static function(MediaWikiServices $services):TranslatableBundleFactory { return new TranslatableBundleFactory($services->get( 'Translate:TranslatablePageStore'), $services->get( 'Translate:MessageBundleStore'));}, 'Translate:TranslatableBundleMover'=> static function(MediaWikiServices $services):TranslatableBundleMover { return new TranslatableBundleMover($services->getMovePageFactory(), $services->getJobQueueGroup(), $services->getLinkBatchFactory(), $services->get( 'Translate:TranslatableBundleFactory'), $services->get( 'Translate:SubpageListBuilder'), $services->getMainConfig() ->get( 'TranslatePageMoveLimit'));}, 'Translate:TranslatableBundleStatusStore'=> static function(MediaWikiServices $services):TranslatableBundleStatusStore { return new TranslatableBundleStatusStore($services->getDBLoadBalancer() ->getConnection(DB_PRIMARY), $services->getCollationFactory() ->makeCollation( 'uca-default-u-kn'), $services->getDBLoadBalancer() ->getMaintenanceConnectionRef(DB_PRIMARY));}, 'Translate:TranslatablePageParser'=> static function(MediaWikiServices $services):TranslatablePageParser { return new TranslatablePageParser($services->get( 'Translate:ParsingPlaceholderFactory'));}, 'Translate:TranslatablePageStore'=> static function(MediaWikiServices $services):TranslatablePageStore { return new TranslatablePageStore($services->get( 'Translate:MessageIndex'), $services->getJobQueueGroup(), new RevTagStore(), $services->getDBLoadBalancer(), $services->get( 'Translate:TranslatableBundleStatusStore'));}, 'Translate:TranslationStashReader'=> static function(MediaWikiServices $services):TranslationStashReader { $db=$services->getDBLoadBalancer() ->getConnectionRef(DB_REPLICA);return new TranslationStashStorage( $db);}, 'Translate:TranslationStatsDataProvider'=> static function(MediaWikiServices $services):TranslationStatsDataProvider { return new TranslationStatsDataProvider(new ServiceOptions(TranslationStatsDataProvider::CONSTRUCTOR_OPTIONS, $services->getMainConfig()), $services->getObjectFactory());}, 'Translate:TranslationUnitStoreFactory'=> static function(MediaWikiServices $services):TranslationUnitStoreFactory { return new TranslationUnitStoreFactory( $services->getDBLoadBalancer());}, 'Translate:TranslatorActivity'=> static function(MediaWikiServices $services):TranslatorActivity { $query=new TranslatorActivityQuery($services->getMainConfig(), $services->getDBLoadBalancer());return new TranslatorActivity($services->getMainObjectStash(), $query, $services->getJobQueueGroup());}, 'Translate:TtmServerFactory'=> static function(MediaWikiServices $services):TtmServerFactory { $config=$services->getMainConfig();$default=$config->get( 'TranslateTranslationDefaultService');if( $default===false) { $default=null;} return new TtmServerFactory( $config->get( 'TranslateTranslationServices'), $default);}]
@phpcs-require-sorted-array