Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
export-rename-language.php
Go to the documentation of this file.
1<?php
10
11// Standard boilerplate to define $IP
12if ( getenv( 'MW_INSTALL_PATH' ) !== false ) {
13 $IP = getenv( 'MW_INSTALL_PATH' );
14} else {
15 $dir = __DIR__;
16 $IP = "$dir/../../..";
17}
18require_once "$IP/maintenance/Maintenance.php";
19
20class ExportRenameLanguage extends Maintenance {
21 private const MARKER = '%CODE%';
22
23 public function __construct() {
24 parent::__construct();
25 $this->addDescription( 'Renames language codes in repos.' );
26 $this->addOption(
27 'group',
28 'Comma separated list of group IDs (can use * as wildcard)',
29 true, /*required*/
30 true /*has arg*/
31 );
32 $this->addOption(
33 'source-language',
34 'Language code',
35 true, /*required*/
36 true /*has arg*/
37 );
38
39 $this->addOption(
40 'target-language',
41 'Language code',
42 true, /*required*/
43 true /*has arg*/
44 );
45 $this->addOption(
46 'target',
47 'Target directory for exported files',
48 true, /*required*/
49 true /*has arg*/
50 );
51 $this->requireExtension( 'Translate' );
52 }
53
54 public function execute() {
55 $target = rtrim( $this->getOption( 'target' ), '/' );
56 $sourceLanguage = $this->getOption( 'source-language' );
57 $targetLanguage = $this->getOption( 'target-language' );
58
59 if ( !is_writable( $target ) ) {
60 $this->fatalError( "Target directory is not writable ($target)." );
61 }
62
63 $groupIds = explode( ',', trim( $this->getOption( 'group' ) ) );
64 $groupIds = MessageGroups::expandWildcards( $groupIds );
65 $groups = MessageGroups::getGroupsById( $groupIds );
66 $groups = $this->filterGroups( $groups );
67
68 if ( $groups === [] ) {
69 $this->fatalError( 'EE1: No valid message groups identified.' );
70 }
71
72 foreach ( $groups as $group ) {
73 // Source path can be wrong if source language is the source language of the
74 // message group. This is because getTargetFilename doesn't check definitionFile
75 // property first.
76 $sourcePath = $group->getTargetFilename( $sourceLanguage );
77 $targetPath = $group->getTargetFilename( $targetLanguage );
78
79 if ( !file_exists( "$target/$sourcePath" ) ) {
80 continue;
81 }
82
83 $this->output( "Renaming $sourcePath to $targetPath\n" );
84 $this->renameFile( "$target/$sourcePath", "$target/$targetPath" );
85
86 $pathPattern = "$target/" . $group->getTargetFilename( self::MARKER );
87 $pathToRemove = '';
88 $needsCleanup = $this->needsCleanup( $pathPattern, $sourceLanguage, $pathToRemove );
89 if ( $needsCleanup === 'yes' ) {
90 $this->output( "Removing empty directory $pathToRemove\n" );
91 rmdir( $pathToRemove );
92 } elseif ( $needsCleanup === 'maybe' ) {
93 $this->output( "Not removing (yet?) non-empty directory $pathToRemove\n" );
94 }
95 }
96
97 $this->output( "Done\n" );
98 }
99
104 private function filterGroups( array $groups ) {
105 $return = [];
106 foreach ( $groups as $groupId => $group ) {
107 if ( !$group instanceof FileBasedMessageGroup ) {
108 $this->output( "Skipping non-file based message group $groupId.\n" );
109 continue;
110 }
111 $return[$groupId] = $group;
112 }
113 return $return;
114 }
115
116 private function renameFile( $source, $target ) {
117 // In case %CODE% is in the path
118 if ( !is_dir( dirname( $target ) ) ) {
119 mkdir( dirname( $target ), 0777, true );
120 }
121
122 rename( $source, $target );
123 }
124
125 private function isDirectoryEmpty( $dir ) {
126 return array_diff( scandir( $dir ), [ '..', '.' ] ) === [];
127 }
128
129 private function needsCleanup( $pathPattern, $sourceLanguage, &$pathToRemove ) {
130 do {
131 $currentComponent = basename( $pathPattern );
132 if ( !str_contains( $currentComponent, self::MARKER ) ) {
133 $pathPattern = dirname( $pathPattern );
134 continue;
135 }
136
137 $pathToRemove = str_replace( self::MARKER, $sourceLanguage, $pathPattern );
138 if ( !is_dir( $pathToRemove ) ) {
139 // %CODE% is in the filename
140 return 'no';
141 }
142
143 return $this->isDirectoryEmpty( $pathToRemove ) ? 'yes' : 'maybe';
144 } while ( $currentComponent !== '' );
145
146 // This should never be reached.
147 return 'no';
148 }
149}
150
151$maintClass = ExportRenameLanguage::class;
152require_once RUN_MAINTENANCE_IF_MAIN;
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.