Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 72 |
|
0.00% |
0 / 4 |
CRAP | |
0.00% |
0 / 1 |
FindUnsynchronizedDefinitionsMaintenanceScript | |
0.00% |
0 / 72 |
|
0.00% |
0 / 4 |
210 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 24 |
|
0.00% |
0 / 1 |
2 | |||
execute | |
0.00% |
0 / 29 |
|
0.00% |
0 / 1 |
72 | |||
getGroups | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
12 | |||
getSideBySide | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
6 |
1 | <?php |
2 | declare( strict_types = 1 ); |
3 | |
4 | namespace MediaWiki\Extension\Translate\Diagnostics; |
5 | |
6 | use FileBasedMessageGroup; |
7 | use MediaWiki\Extension\Translate\MessageGroupProcessing\MessageGroups; |
8 | use MediaWiki\Extension\Translate\Utilities\BaseMaintenanceScript; |
9 | use MediaWiki\Shell\Shell; |
10 | use MediaWiki\Title\Title; |
11 | |
12 | /** |
13 | * @since 2021.01 |
14 | * @license GPL-2.0-or-later |
15 | * @author Niklas Laxström |
16 | */ |
17 | class FindUnsynchronizedDefinitionsMaintenanceScript extends BaseMaintenanceScript { |
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 | |
47 | /** @inheritDoc */ |
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 | |
90 | /** @return FileBasedMessageGroup[] */ |
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 | } |