30 private bool $noComplete =
true;
32 private bool $noEmpty =
false;
34 private string $target;
36 private ?
string $targetType =
null;
37 private ServiceOptions $options;
38 private JobQueueGroup $jobQueueGroup;
42 private LanguageNameUtils $languageNameUtils;
45 private const GROUPS =
'group';
46 private const MESSAGES =
'messages';
48 private const CONSTRUCTOR_OPTIONS = [
49 'TranslateMessagePrefixStatsLimit',
52 public function __construct(
54 JobQueueGroup $jobQueueGroup,
58 LanguageNameUtils $languageNameUtils,
61 parent::__construct(
'MessageGroupStats' );
62 $this->options =
new ServiceOptions( self::CONSTRUCTOR_OPTIONS, $config );
63 $this->jobQueueGroup = $jobQueueGroup;
64 $this->messageGroupStatsTableFactory = $messageGroupStatsTableFactory;
65 $this->entitySearch = $entitySearch;
66 $this->messagePrefixStats = $messagePrefixStats;
67 $this->languageNameUtils = $languageNameUtils;
68 $this->messageGroupMetadata = $messageGroupMetadata;
71 public function getDescription() {
72 return $this->msg(
'translate-mgs-pagename' );
75 public function isIncludable() {
79 protected function getGroupName() {
83 public function execute( $par ) {
84 $request = $this->getRequest();
86 $purge = $request->getVal(
'action' ) ===
'purge';
87 if ( $purge && !$request->wasPosted() ) {
88 LanguageStatsSpecialPage::showPurgeForm( $this->getContext() );
93 $this->outputHeader();
95 $out = $this->getOutput();
97 $out->addModules(
'ext.translate.special.languagestats' );
98 $out->addModuleStyles(
'ext.translate.statstable' );
99 $out->addModuleStyles(
'ext.translate.special.groupstats' );
101 $params = $par ? explode(
'/', $par ) : [];
103 if ( isset( $params[0] ) && trim( $params[0] ) ) {
104 $this->target = $params[0];
107 if ( isset( $params[1] ) ) {
108 $this->noComplete = (bool)$params[1];
111 if ( isset( $params[2] ) ) {
112 $this->noEmpty = (bool)$params[2];
116 $submitted = !$this->including() && $request->getVal(
'x' ) ===
'D';
118 $this->target = $request->getVal( self::GROUPS, $this->target ??
'' );
119 if ( $this->target !==
'' ) {
120 $this->targetType = self::GROUPS;
122 $this->target = $request->getVal( self::MESSAGES,
'' );
123 if ( $this->target !==
'' ) {
124 $this->targetType = self::MESSAGES;
129 $this->noComplete = $request->getBool(
131 $this->noComplete && !$submitted
133 $this->noEmpty = $request->getBool(
'suppressempty', $this->noEmpty && !$submitted );
135 if ( !$this->including() ) {
136 $out->addHelpLink(
'Help:Extension:Translate/Statistics_and_reporting' );
140 $stats = $output =
null;
141 if ( $this->targetType === self::GROUPS && $this->isValidGroup( $this->target ) ) {
142 $this->outputIntroduction();
146 $messageGroupStatsTable = $this->messageGroupStatsTableFactory->newFromContext( $this->getContext() );
147 $output = $messageGroupStatsTable->get(
149 MessageGroups::getGroup( $this->target ),
154 $incomplete = $messageGroupStatsTable->areStatsIncomplete();
157 "<div class='error'>$1</div>",
158 'translate-langstats-incomplete'
162 if ( $incomplete || $purge ) {
163 DeferredUpdates::addCallableUpdate(
function () use ( $purge ) {
174 $jobParams = $this->getCacheRebuildJobParameters( $this->target );
175 $jobParams[
'purge' ] = $purge;
176 $job = RebuildMessageGroupStatsJob::newJob( $jobParams );
177 $this->jobQueueGroup->push( $job );
181 $this->loadStatistics( $this->target );
185 } elseif ( $this->targetType === self::MESSAGES ) {
186 $messagesWithPrefix = $this->entitySearch->matchMessages( $this->target );
187 if ( $messagesWithPrefix ) {
188 $messageWithPrefixLimit = $this->options->get(
'TranslateMessagePrefixStatsLimit' );
189 if ( count( $messagesWithPrefix ) > $messageWithPrefixLimit ) {
192 $this->msg(
'translate-mgs-message-prefix-limit' )
193 ->params( $messageWithPrefixLimit )
200 $stats = $this->messagePrefixStats->forAll( ...$messagesWithPrefix );
201 $messageGroupStatsTable = $this->messageGroupStatsTableFactory
202 ->newFromContext( $this->getContext() );
203 $output = $messageGroupStatsTable->get(
214 $out->addHTML( $output );
215 } elseif ( $stats !==
null ) {
217 $out->addHTML( Html::warningBox( $this->msg(
'translate-mgs-nothing' )->parse() ) );
218 } elseif ( $submitted ) {
219 $this->invalidTarget();
223 private function loadStatistics(
string $target,
int $flags = 0 ): array {
227 private function getCacheRebuildJobParameters(
string $target ): array {
228 return [
'groupid' => $target ];
231 private function isValidGroup( ?
string $value ):
bool {
232 if ( $value ===
null ) {
236 $group = MessageGroups::getGroup( $value );
238 if ( MessageGroups::isDynamic( $group ) ) {
246 $this->target = $group->getId();
253 private function invalidTarget():
void {
254 $this->getOutput()->wrapWikiMsg(
255 "<div class='error'>$1</div>",
256 [
'translate-mgs-invalid-group', $this->target ]
260 private function outputIntroduction():
void {
261 $priorityLangs = $this->messageGroupMetadata->get( $this->target,
'prioritylangs' );
262 if ( $priorityLangs ) {
263 $languagesFormatted = $this->formatLanguageList( explode(
',', $priorityLangs ) );
264 $hasPriorityForce = $this->messageGroupMetadata->get( $this->target,
'priorityforce' ) ===
'on';
265 if ( $hasPriorityForce ) {
266 $this->getOutput()->addWikiMsg(
'tpt-priority-languages-force', $languagesFormatted );
268 $this->getOutput()->addWikiMsg(
'tpt-priority-languages', $languagesFormatted );
273 private function formatLanguageList( array $codes ):
string {
274 foreach ( $codes as &$value ) {
275 $value = $this->languageNameUtils->getLanguageName( $value, $this->getLanguage()->getCode() )
276 . $this->msg(
'word-separator' )->plain()
277 . $this->msg(
'parentheses', $value )->plain();
280 return $this->getLanguage()->listToText( $codes );
283 private function addForm():
void {
287 'name' => self::GROUPS,
288 'id' => self::GROUPS,
289 'label' => $this->msg(
'translate-mgs-group' )->text(),
290 'options' => $this->getGroupOptions(),
291 'default' => $this->targetType === self::GROUPS ? $this->target :
null,
292 'cssclass' =>
'message-group-selector'
296 'name' => self::MESSAGES,
297 'id' => self::MESSAGES,
298 'label' => $this->msg(
'translate-mgs-prefix' )->text(),
299 'default' => $this->targetType === self::MESSAGES ? $this->target :
null,
300 'cssclass' =>
'message-prefix-selector'
302 'nocomplete-check' => [
304 'name' =>
'suppresscomplete',
305 'id' =>
'suppresscomplete',
306 'label' => $this->msg(
'translate-mgs-nocomplete' )->text(),
307 'default' => $this->noComplete,
311 'name' =>
'suppressempty',
312 'id' =>
'suppressempty',
313 'label' => $this->msg(
'translate-mgs-noempty' )->text(),
314 'default' => $this->noEmpty,
318 $htmlForm = HTMLForm::factory(
'ooui', $formDescriptor, $this->getContext() );
323 $val = $this->getRequest()->getVal(
'language' );
324 if ( $val !==
null ) {
325 $htmlForm->addHiddenField(
'language', $val );
329 ->addHiddenField(
'x',
'D' )
331 ->setId(
'mw-message-group-stats-form' )
332 ->setSubmitTextMsg(
'translate-mgs-submit' )
333 ->setWrapperLegendMsg(
'translate-mgs-fieldset' )
335 ->displayForm(
false );
339 private function getGroupOptions(): array {
340 $options = [
'' => null ];
341 $groups = MessageGroups::getAllGroups();
343 foreach ( $groups as $id => $class ) {
344 if ( MessageGroups::getGroup( $id )->exists() ) {
345 $options[$class->getLabel()] = $id;