Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 82
0.00% covered (danger)
0.00%
0 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
ImportExtensionMessages
0.00% covered (danger)
0.00%
0 / 79
0.00% covered (danger)
0.00%
0 / 8
930
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
42
 init
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 getMessagesDirs
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
30
 processDir
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
20
 processFile
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
56
 getCoreData
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
12
 setCoreData
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2
3use MediaWiki\MainConfigNames;
4
5require_once __DIR__ . '/../Maintenance.php';
6
7class ImportExtensionMessages extends Maintenance {
8    private $extensionDir;
9    private $extName;
10    private $excludedMsgs;
11    private $outDir;
12    private $coreDataCache;
13
14    public function __construct() {
15        parent::__construct();
16        $this->addArg( 'extension', 'The extension name' );
17        $this->addOption( 'outdir',
18            'The output directory, default $IP/languages/i18n', false, true );
19    }
20
21    public function execute() {
22        $this->init();
23
24        $this->outDir = $this->getOption( 'outdir', MW_INSTALL_PATH . '/languages/i18n' );
25        if ( !file_exists( $this->outDir ) ) {
26            mkdir( $this->outDir, 0777, true );
27        }
28
29        $this->extName = $this->getArg();
30        $extJsonPath = $this->extensionDir . "/{$this->extName}/extension.json";
31        $extJson = file_get_contents( $extJsonPath );
32        if ( $extJson === false ) {
33            $this->fatalError( "Unable to open \"$extJsonPath\"" );
34        }
35        $extData = json_decode( $extJson, JSON_THROW_ON_ERROR );
36
37        $this->excludedMsgs = [];
38        foreach ( [ 'namemsg', 'descriptionmsg' ] as $key ) {
39            if ( isset( $extData[$key] ) ) {
40                $this->excludedMsgs[] = $extData[$key];
41            }
42        }
43
44        foreach ( $this->getMessagesDirs( $extData ) as $dir ) {
45            $this->processDir( $dir );
46        }
47    }
48
49    private function init() {
50        $services = $this->getServiceContainer();
51        $config = $services->getMainConfig();
52        $this->extensionDir = $config->get( MainConfigNames::ExtensionDirectory );
53    }
54
55    private function getMessagesDirs( $extData ) {
56        if ( isset( $extData['MessagesDirs'] ) ) {
57            $messagesDirs = [];
58            foreach ( $extData['MessagesDirs'] as $dirs ) {
59                if ( is_array( $dirs ) ) {
60                    foreach ( $dirs as $dir ) {
61                        $messagesDirs[] = $dir;
62                    }
63                } else {
64                    $messagesDirs[] = $dirs;
65                }
66            }
67        } else {
68            $messagesDirs = [ 'i18n' ];
69        }
70        return $messagesDirs;
71    }
72
73    private function processDir( $dir ) {
74        $path = $this->extensionDir . "/{$this->extName}/$dir";
75
76        foreach ( new DirectoryIterator( $path ) as $file ) {
77            if ( !$file->isDot() && str_ends_with( $file->getFilename(), '.json' ) ) {
78                $this->processFile(
79                    substr( $file->getFilename(), 0, -5 ),
80                    $file->getPathname()
81                );
82            }
83        }
84    }
85
86    private function processFile( $lang, $extI18nPath ) {
87        $extJson = file_get_contents( $extI18nPath );
88        if ( $extJson === false ) {
89            $this->error( "Unable to read i18n file \"$extI18nPath\"" );
90            return;
91        }
92        $extData = json_decode( $extJson, JSON_THROW_ON_ERROR );
93        $coreData = $this->getCoreData( $lang );
94
95        if ( isset( $extData['@metadata']['authors'] ) ) {
96            $authors = array_unique( array_merge(
97                $coreData['@metadata']['authors'] ?? [],
98                $extData['@metadata']['authors']
99            ) );
100            // Fix numeric authors
101            foreach ( $authors as &$author ) {
102                $author = (string)$author;
103            }
104            sort( $authors );
105            $coreData['@metadata']['authors'] = $authors;
106        }
107
108        foreach ( $extData as $name => $value ) {
109            if ( str_starts_with( $name, '@' ) ) {
110                continue;
111            }
112            if ( in_array( $name, $this->excludedMsgs ) ) {
113                continue;
114            }
115            $coreData[$name] = $value;
116        }
117
118        $this->setCoreData( $lang, $coreData );
119    }
120
121    private function getCoreData( $lang ) {
122        if ( !isset( $this->coreDataCache[$lang] ) ) {
123            $corePath = MW_INSTALL_PATH . "/languages/i18n/$lang.json";
124            // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
125            $coreJson = @file_get_contents( $corePath );
126            if ( $coreJson === false ) {
127                $this->error( "Warning: discarding extension localisation " .
128                    "for language \"$lang\" not present in core" );
129                // Do not write to coreDataCache -- suppress creation of the core file.
130                return [];
131            }
132            $this->coreDataCache[$lang] = json_decode( $coreJson, JSON_THROW_ON_ERROR );
133        }
134        return $this->coreDataCache[$lang];
135    }
136
137    private function setCoreData( $lang, $data ) {
138        if ( !isset( $this->coreDataCache[$lang] ) ) {
139            // Non-existent file, do not create
140            return;
141        }
142
143        $this->coreDataCache[$lang] = $data;
144        $outPath = "{$this->outDir}/$lang.json";
145        if ( !file_put_contents(
146            $outPath,
147            FormatJson::encode( $data, "\t", FormatJson::ALL_OK ) . "\n"
148        ) ) {
149            $this->error( "Unable to write core i18n file \"$outPath\"" );
150        }
151    }
152}
153
154$maintClass = ImportExtensionMessages::class;
155require_once RUN_MAINTENANCE_IF_MAIN;