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(
'wgTranslateLanguages', TranslateUtils::getLanguageNames(
null ) );
90 $out->addHTML( Html::openElement(
'div', [
91 'class' =>
'grid ext-translate-container',
94 $out->addHTML( $this->tuxSettingsForm() );
95 $out->addHTML( $this->messageSelector() );
97 $table =
new MessageTable( $this->getContext(), $this->group, $this->options[
'language'] );
98 $output = $table->fullTable();
100 $out->addHTML( $output );
101 $out->addHTML( Html::closeElement(
'div' ) );
104 protected function setup( ?
string $parameters ): void {
105 $request = $this->getRequest();
108 'language' => $this->getLanguage()->getCode(),
109 'group' =>
'!additions',
115 $parameters = $parameters !==
null ? array_map(
'trim', explode(
';', $parameters ) ) : [];
118 foreach ( $parameters as $_ ) {
123 if ( strpos( $_,
'=' ) !==
false ) {
124 [ $key, $value ] = array_map(
'trim', explode(
'=', $_, 2 ) );
130 $pars[$key] = $value;
133 foreach ( $defaults as $v => $t ) {
134 if ( is_bool( $t ) ) {
135 $r = isset( $pars[$v] ) ? (bool)$pars[$v] : $defaults[$v];
136 $r = $request->getBool( $v, $r );
137 } elseif ( is_int( $t ) ) {
138 $r = isset( $pars[$v] ) ? (int)$pars[$v] : $defaults[$v];
139 $r = $request->getInt( $v, $r );
140 } elseif ( is_string( $t ) ) {
141 $r = isset( $pars[$v] ) ? (string)$pars[$v] : $defaults[$v];
142 $r = $request->getText( $v, $r );
145 if ( !isset( $r ) ) {
146 throw new MWException(
'$r was not set' );
149 if ( $defaults[$v] !== $r ) {
150 $nondefaults[$v] = $r;
154 $this->defaults = $defaults;
155 $this->nondefaults = $nondefaults;
156 Hooks::run(
'TranslateGetSpecialTranslateOptions', [ &$defaults, &$nondefaults ] );
158 $this->options = $nondefaults + $defaults;
160 if ( $this->group ) {
161 $this->options[
'group'] = $this->group->getId();
166 if ( !Language::isKnownLanguageTag( $this->options[
'language'] ) ) {
167 $this->options[
'language'] = $this->defaults[
'language'];
170 if ( MessageGroups::isDynamic( $this->group ) ) {
172 $this->group->setLanguage( $this->options[
'language'] );
176 protected function tuxSettingsForm(): string {
177 $nojs = Html::errorBox(
178 $this->msg(
'tux-nojs' )->plain(),
183 $attrs = [
'class' =>
'row tux-editor-header' ];
184 $selectors = $this->tuxGroupSelector() .
185 $this->tuxLanguageSelector() .
186 $this->tuxGroupDescription() .
187 $this->tuxWorkflowSelector() .
188 $this->tuxGroupWarning();
190 return Html::rawElement(
'div', $attrs, $selectors ) . $nojs;
193 protected function messageSelector(): string {
194 $output = Html::openElement(
'div', [
'class' =>
'row tux-messagetable-header hide' ] );
195 $output .= Html::openElement(
'div', [
'class' =>
'nine columns' ] );
196 $output .= Html::openElement(
'ul', [
'class' =>
'row tux-message-selector' ] );
197 $userId = $this->getUser()->getId();
200 'untranslated' =>
'!translated',
201 'outdated' =>
'fuzzy',
202 'translated' =>
'translated',
203 'unproofread' =>
"translated|!reviewer:$userId|!last-translator:$userId",
206 $params = $this->nondefaults;
208 foreach ( $tabs as $tab => $filter ) {
215 $tabClass =
"tux-tab-$tab";
216 $taskParams = [
'filter' => $filter ] + $params;
217 ksort( $taskParams );
218 $href = $this->getPageTitle()->getLocalURL( $taskParams );
219 $link = Html::element(
'a', [
'href' => $href ], $this->msg( $tabClass )->text() );
220 $output .= Html::rawElement(
'li', [
221 'class' =>
'column ' . $tabClass,
222 'data-filter' => $filter,
223 'data-title' => $tab,
233 'optional' => $this->msg(
'tux-message-filter-optional-messages-label' )->text(),
236 $container = Html::openElement(
'ul', [
'class' =>
'column tux-message-selector' ] );
237 foreach ( $options as $optFilter => $optLabel ) {
238 $container .= Html::rawElement(
'li',
239 [
'class' =>
'column' ],
243 "tux-option-$optFilter",
244 isset( $this->nondefaults[$optFilter] ),
245 [
'data-filter' => $optFilter ]
250 $container .= Html::closeElement(
'ul' );
253 $output .= Html::openElement(
'li', [
'class' =>
'column more' ] ) .
256 Html::closeElement(
'li' );
258 $output .= Html::closeElement(
'ul' );
259 $output .= Html::closeElement(
'div' );
260 $output .= Html::openElement(
'div', [
'class' =>
'three columns' ] );
261 $output .= Html::rawElement(
263 [
'class' =>
'tux-message-filter-wrapper' ],
264 Html::element(
'input', [
265 'class' =>
'tux-message-filter-box',
267 'placeholder' => $this->msg(
'tux-message-filter-placeholder' )->text()
272 $output .= Html::closeElement(
'div' ) . Html::closeElement(
'div' );
277 protected function tuxGroupSelector(): string {
278 $groupClass = [
'grouptitle',
'grouplink' ];
280 $groupClass[] =
'tux-breadcrumb__item--aggregate';
284 $output = Html::openElement(
'div', [
285 'class' =>
'eight columns tux-breadcrumb',
286 'data-language' => $this->options[
'language'],
288 Html::element(
'span',
289 [
'class' =>
'grouptitle' ],
290 $this->msg(
'translate-msggroupselector-projects' )->text()
292 Html::element(
'span',
293 [
'class' =>
'grouptitle grouplink tux-breadcrumb__item--aggregate' ],
294 $this->msg(
'translate-msggroupselector-search-all' )->text()
296 Html::element(
'span',
298 'class' => $groupClass,
299 'data-msggroupid' => $this->group->getId(),
301 $this->group->getLabel( $this->getContext() )
303 Html::closeElement(
'div' );
308 protected function tuxLanguageSelector(): string {
309 global $wgTranslateDocumentationLanguageCode;
311 if ( $this->options[
'language'] === $wgTranslateDocumentationLanguageCode ) {
312 $targetLangName = $this->msg(
'translate-documentation-language' )->text();
313 $targetLanguage = $this->contentLanguage;
315 $targetLangName = $this->languageNameUtils->getLanguageName( $this->options[
'language'] );
316 $targetLanguage = $this->languageFactory->getLanguage( $this->options[
'language'] );
319 $label = Html::element(
'span', [], $this->msg(
'tux-languageselector' )->text() );
321 $languageIcon = Html::element(
323 [
'class' =>
'ext-translate-language-icon' ]
326 $targetLanguageName = Html::element(
329 'class' =>
'ext-translate-target-language',
330 'dir' => $targetLanguage->getDir(),
331 'lang' => $targetLanguage->getHtmlCode()
336 $expandIcon = Html::element(
338 [
'class' =>
'ext-translate-language-selector-expand' ]
341 $value = Html::rawElement(
344 'class' =>
'uls mw-ui-button',
346 'title' => $this->msg(
'tux-select-target-language' )->text()
348 $languageIcon . $targetLanguageName . $expandIcon
351 return Html::rawElement(
353 [
'class' =>
'four columns ext-translate-language-selector' ],
358 protected function tuxGroupDescription(): string {
362 [
'class' =>
'twelve columns description' ],
363 $this->getGroupDescription( $this->group )
367 protected function getGroupDescription(
MessageGroup $group ): string {
368 $description = $group->getDescription( $this->getContext() );
369 return $description ===
null ?
370 '' : $this->getOutput()->parseAsInterface( $description );
373 protected function tuxGroupWarning(): string {
374 if ( $this->options[
'group'] ===
'' ) {
375 return Html::rawElement(
377 [
'class' =>
'twelve columns group-warning' ],
378 $this->msg(
'tux-translate-page-no-such-group' )->parse()
383 return Html::element(
385 [
'class' =>
'twelve columns group-warning' ],
390 protected function tuxWorkflowSelector(): string {
391 return Html::element(
'div', [
'class' =>
'tux-workflow twelve columns' ] );
398 public static function tabify( Skin $skin, array &$tabs ): bool {
399 $title = $skin->getTitle();
400 if ( !$title->isSpecialPage() ) {
403 [ $alias, $sub ] = MediaWikiServices::getInstance()
404 ->getSpecialPageFactory()->resolveAlias( $title->getText() );
406 $pagesInGroup = [
'Translate',
'LanguageStats',
'MessageGroupStats',
'ExportTranslations' ];
407 if ( !in_array( $alias, $pagesInGroup,
true ) ) {
413 if ( $sub !==
null && trim( $sub ) !==
'' ) {
414 if ( $alias ===
'Translate' || $alias ===
'MessageGroupStats' ) {
415 $params[
'group'] = $sub;
416 } elseif ( $alias ===
'LanguageStats' ) {
418 $params[
'language'] = $sub;
422 $request = $skin->getRequest();
424 $params[
'language'] = $request->getVal(
'language' );
425 $params[
'group'] = $request->getVal(
'group' );
427 $translate = SpecialPage::getTitleFor(
'Translate' );
428 $languagestats = SpecialPage::getTitleFor(
'LanguageStats' );
429 $messagegroupstats = SpecialPage::getTitleFor(
'MessageGroupStats' );
432 $tabs[
'namespaces'] = [];
434 $tabs[
'namespaces'][
'translate'] = [
435 'text' => wfMessage(
'translate-taction-translate' )->text(),
436 'href' => $translate->getLocalURL( $params ),
437 'class' =>
'tux-tab',
440 if ( $alias ===
'Translate' ) {
441 $tabs[
'namespaces'][
'translate'][
'class'] .=
' selected';
444 $tabs[
'views'][
'lstats'] = [
445 'text' => wfMessage(
'translate-taction-lstats' )->text(),
446 'href' => $languagestats->getLocalURL( $params ),
447 'class' =>
'tux-tab',
449 if ( $alias ===
'LanguageStats' ) {
450 $tabs[
'views'][
'lstats'][
'class'] .=
' selected';
453 $tabs[
'views'][
'mstats'] = [
454 'text' => wfMessage(
'translate-taction-mstats' )->text(),
455 'href' => $messagegroupstats->getLocalURL( $params ),
456 'class' =>
'tux-tab',
459 if ( $alias ===
'MessageGroupStats' ) {
460 $tabs[
'views'][
'mstats'][
'class'] .=
' selected';
463 $tabs[
'views'][
'export'] = [
464 'text' => wfMessage(
'translate-taction-export' )->text(),
465 'href' => SpecialPage::getTitleFor(
'ExportTranslations' )->getLocalURL( $params ),
466 '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'), MessageIndex::singleton());}, '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: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: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: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());}, '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