Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
FindUnsynchronizedDefinitionsMaintenanceScript.php
1<?php
2declare( strict_types = 1 );
3
4namespace MediaWiki\Extension\Translate\Diagnostics;
5
9use MediaWiki\Shell\Shell;
10use MediaWiki\Title\Title;
11
18 public function __construct() {
19 parent::__construct();
20 $this->addDescription(
21 'This scripts finds definition pages in the wiki that do not have the expected ' .
22 'content with regards to the message group definition cache for file based message ' .
23 'groups. This causes the definition diff to appear for translations when it should ' .
24 'not. See https://phabricator.wikimedia.org/T270844'
25 );
26
27 $this->addArg(
28 'group-pattern',
29 'For example page-*,main',
30 self::REQUIRED
31 );
32 $this->addOption(
33 'ignore-trailing-whitespace',
34 'Ignore trailing whitespace',
35 self::OPTIONAL,
36 self::NO_ARG,
37 'w'
38 );
39 $this->addOption(
40 'fix',
41 'Try to fix the issues by triggering reprocessing'
42 );
43
44 $this->requireExtension( 'Translate' );
45 }
46
48 public function execute() {
49 $ignoreTrailingWhitespace = $this->getOption( 'ignore-trailing-whitespace' );
50 $groups = $this->getGroups( $this->getArg( 0 ) );
51 $matched = count( $groups );
52 $this->output( "Pattern matched $matched file based message group(s).\n" );
53 $this->output( "Left side is the expected value. Right side is the actual value in wiki.\n" );
54
55 $groupsWithIssues = [];
56 foreach ( $groups as $group ) {
57 $sourceLanguage = $group->getSourceLanguage();
58 $collection = $group->initCollection( $sourceLanguage );
59 $collection->loadTranslations();
60
61 foreach ( $collection->keys() as $mkey => $title ) {
62 $message = $collection[$mkey];
63 $definition = $message->definition() ?? '';
64 $translation = $message->translation() ?? '';
65
66 $differs = $ignoreTrailingWhitespace
67 ? rtrim( $definition ) !== $translation
68 : $definition !== $translation;
69
70 if ( $differs ) {
71 $groupsWithIssues[$group->getId()] = $group;
72 echo Title::newFromLinkTarget( $title )->getPrefixedText() . "\n";
73 echo $this->getSideBySide( "'$definition'", "'$translation'", 80 ) . "\n";
74 }
75 }
76 }
77
78 if ( $this->hasOption( 'fix' ) && $groupsWithIssues ) {
79 foreach ( $groupsWithIssues as $group ) {
80 $cache = $group->getMessageGroupCache( $group->getSourceLanguage() );
81 $cache->invalidate();
82 }
83 $script = realpath( __DIR__ . '/../../scripts/importExternalTranslations.php' );
84 $groupPattern = implode( ',', array_keys( $groupsWithIssues ) );
85 $command = Shell::makeScriptCommand( $script, [ '--group', $groupPattern ] )->getCommandString();
86 echo "Now run the following command and finish the sync in the wiki:\n$command\n";
87 }
88 }
89
91 private function getGroups( string $patternList ): array {
92 $patterns = array_map( 'trim', explode( ',', $patternList ) );
93 $groupIds = MessageGroups::expandWildcards( $patterns );
94 $groups = MessageGroups::getGroupsById( $groupIds );
95
96 foreach ( $groups as $index => $group ) {
97 if ( !$group instanceof FileBasedMessageGroup ) {
98 unset( $groups[$index] );
99 }
100 }
101
102 // @phan-suppress-next-line PhanTypeMismatchReturn
103 return $groups;
104 }
105
106 private function getSideBySide( string $a, string $b, int $width ): string {
107 $wrapWidth = (int)floor( ( $width - 3 ) / 2 );
108 $aArray = explode( "\n", wordwrap( $a, $wrapWidth, "\n", true ) );
109 $bArray = explode( "\n", wordwrap( $b, $wrapWidth, "\n", true ) );
110 $lines = max( count( $aArray ), count( $bArray ) );
111
112 $out = '';
113 for ( $i = 0; $i < $lines; $i++ ) {
114 $out .= sprintf(
115 "%-{$wrapWidth}s | %-{$wrapWidth}s\n",
116 $aArray[$i] ?? '',
117 $bArray[$i] ?? ''
118 );
119 }
120 return $out;
121 }
122}
This class implements default behavior for file based message groups.
Factory class for accessing message groups individually by id or all of them as a list.
Base maintenance script containing constants and methods used in multiple scripts Hopefully the const...