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