Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
PremadeMediawikiExtensionGroups.php
1<?php
2declare( strict_types = 1 );
3
4namespace MediaWiki\Extension\Translate\MessageGroupConfiguration;
5
6use FileDependency;
11use MessageGroup;
13use RuntimeException;
14use UnexpectedValueException;
15
22 protected string $idPrefix = 'ext-';
23 protected ?int $namespace = null;
25 protected string $path;
27 protected string $definitionFile;
28
37 public function __construct( string $def, string $path ) {
38 $this->definitionFile = $def;
39 $this->path = rtrim( $path, '/' );
40 }
41
43 protected function getDefaultNamespace(): int {
44 return NS_MEDIAWIKI;
45 }
46
48 protected function getNamespace(): int {
49 if ( $this->namespace === null ) {
50 $this->namespace = $this->getDefaultNamespace();
51 }
52 return $this->namespace;
53 }
54
56 public function setGroupPrefix( string $value ): void {
57 $this->idPrefix = $value;
58 }
59
61 public function setNamespace( int $value ): void {
62 $this->namespace = $value;
63 }
64
66 public function register( array &$list, array &$deps ): void {
67 $groups = $this->parseFile();
68 $groups = $this->processGroups( $groups );
69 foreach ( $groups as $id => $g ) {
70 $list[$id] = $this->createMessageGroup( $id, $g );
71 }
72
73 $deps[] = new FileDependency( $this->definitionFile );
74 }
75
81 protected function createMessageGroup( string $id, array $info ): MessageGroup {
82 $conf = [];
83 $conf['BASIC']['class'] = MediaWikiExtensionMessageGroup::class;
84 $conf['BASIC']['id'] = $id;
85 $conf['BASIC']['namespace'] = $this->getNamespace();
86 $conf['BASIC']['label'] = $info['name'];
87
88 if ( isset( $info['desc'] ) ) {
89 $conf['BASIC']['description'] = $info['desc'];
90 } else {
91 $conf['BASIC']['descriptionmsg'] = $info['descmsg'];
92 }
93
94 $conf['FILES']['class'] = JsonFormat::class;
95 $conf['FILES']['sourcePattern'] = $this->path . '/' . $info['file'];
96
97 // @todo Find a better way
98 if ( isset( $info['aliasfile'] ) ) {
99 $conf['FILES']['aliasFileSource'] = $this->path . '/' . $info['aliasfile'];
100 $conf['FILES']['aliasFile'] = $info['aliasfile'];
101 }
102 if ( isset( $info['magicfile'] ) ) {
103 $conf['FILES']['magicFileSource'] = $this->path . '/' . $info['magicfile'];
104 $conf['FILES']['magicFile'] = $info['magicfile'];
105 }
106
107 if ( isset( $info['prefix'] ) ) {
108 $conf['MANGLER']['class'] = StringMatcher::class;
109 $conf['MANGLER']['prefix'] = $info['prefix'];
110 $conf['MANGLER']['patterns'] = $info['mangle'];
111
112 $mangler = new StringMatcher( $info['prefix'], $info['mangle'] );
113 if ( isset( $info['ignored'] ) ) {
114 $info['ignored'] = $mangler->mangleList( $info['ignored'] );
115 }
116 if ( isset( $info['optional'] ) ) {
117 $info['optional'] = $mangler->mangleList( $info['optional'] );
118 }
119 }
120
121 $conf['VALIDATORS'] = [
122 [ 'id' => 'BraceBalance' ],
123 [ 'id' => 'MediaWikiLink' ],
124 [ 'id' => 'MediaWikiPageName' ],
125 [ 'id' => 'MediaWikiParameter' ],
126 [ 'id' => 'MediaWikiPlural' ],
127 ];
128
129 $conf['INSERTABLES'] = [
130 [ 'class' => MediaWikiInsertablesSuggester::class ]
131 ];
132
133 if ( isset( $info['optional'] ) ) {
134 $conf['TAGS']['optional'] = $info['optional'];
135 }
136 if ( isset( $info['ignored'] ) ) {
137 $conf['TAGS']['ignored'] = $info['ignored'];
138 }
139
140 if ( isset( $info['languages'] ) ) {
141 $conf['LANGUAGES'] = [
142 'include' => [],
143 'exclude' => [],
144 ];
145
146 foreach ( $info['languages'] as $tagSpec ) {
147 if ( preg_match( '/^([+-])?(.+)$/', $tagSpec, $m ) ) {
148 [ , $sign, $tag ] = $m;
149 if ( $sign === '+' ) {
150 $conf['LANGUAGES']['include'][] = $tag;
151 } elseif ( $sign === '-' ) {
152 $conf['LANGUAGES']['exclude'][] = $tag;
153 } else {
154 $conf['LANGUAGES']['exclude'] = '*';
155 $conf['LANGUAGES']['include'][] = $tag;
156 }
157 }
158 }
159 }
160
161 return MessageGroupBase::factory( $conf );
162 }
163
164 protected function parseFile(): array {
165 $defines = file_get_contents( $this->definitionFile );
166 $linefeed = '(\r\n|\n)';
167 $sections = array_map(
168 'trim',
169 preg_split( "/$linefeed{2,}/", $defines, -1, PREG_SPLIT_NO_EMPTY )
170 );
171 $groups = [];
172
173 foreach ( $sections as $section ) {
174 $lines = array_map( 'trim', preg_split( "/$linefeed/", $section ) );
175 $newGroup = [];
176
177 foreach ( $lines as $line ) {
178 if ( $line === '' || $line[0] === '#' ) {
179 continue;
180 }
181
182 if ( !str_contains( $line, '=' ) ) {
183 if ( empty( $newGroup['name'] ) ) {
184 $newGroup['name'] = $line;
185 } else {
186 throw new RuntimeException( 'Trying to define name twice: ' . $line );
187 }
188 } else {
189 [ $key, $value ] = array_map( 'trim', explode( '=', $line, 2 ) );
190 switch ( $key ) {
191 case 'aliasfile':
192 case 'desc':
193 case 'descmsg':
194 case 'file':
195 case 'id':
196 case 'magicfile':
197 case 'var':
198 $newGroup[$key] = $value;
199 break;
200 case 'optional':
201 case 'ignored':
202 case 'languages':
203 $values = array_map( 'trim', explode( ',', $value ) );
204 if ( !isset( $newGroup[$key] ) ) {
205 $newGroup[$key] = [];
206 }
207 $newGroup[$key] = array_merge( $newGroup[$key], $values );
208 break;
209 case 'prefix':
210 [ $prefix, $messages ] = array_map(
211 'trim',
212 explode( '|', $value, 2 )
213 );
214 if ( isset( $newGroup['prefix'] ) && $newGroup['prefix'] !== $prefix ) {
215 throw new RuntimeException(
216 "Only one prefix supported: {$newGroup['prefix']} !== $prefix"
217 );
218 }
219 $newGroup['prefix'] = $prefix;
220
221 if ( !isset( $newGroup['mangle'] ) ) {
222 $newGroup['mangle'] = [];
223 }
224
225 $messages = array_map( 'trim', explode( ',', $messages ) );
226 $newGroup['mangle'] = array_merge( $newGroup['mangle'], $messages );
227 break;
228 default:
229 throw new UnexpectedValueException( 'Unknown key:' . $key );
230 }
231 }
232 }
233
234 if ( count( $newGroup ) ) {
235 if ( empty( $newGroup['name'] ) ) {
236 throw new RuntimeException( "Name missing\n" . print_r( $newGroup, true ) );
237 }
238 $groups[] = $newGroup;
239 }
240 }
241
242 return $groups;
243 }
244
245 protected function processGroups( array $groups ): array {
246 $fixedGroups = [];
247 foreach ( $groups as $g ) {
248 $name = $g['name'];
249
250 $id = $g['id'] ?? $this->idPrefix . preg_replace( '/\s+/', '', strtolower( $name ) );
251
252 if ( !isset( $g['file'] ) ) {
253 $file = preg_replace( '/\s+/', '', "$name/i18n/%CODE%.json" );
254 } else {
255 $file = $g['file'];
256 }
257
258 $descMsg = $g['descmsg'] ?? str_replace( $this->idPrefix, '', $id ) . '-desc';
259
260 $newGroup = [
261 'name' => $name,
262 'file' => $file,
263 'descmsg' => $descMsg,
264 ];
265
266 $copyVars = [
267 'aliasfile',
268 'desc',
269 'ignored',
270 'languages',
271 'magicfile',
272 'mangle',
273 'optional',
274 'prefix',
275 'var',
276 ];
277
278 foreach ( $copyVars as $var ) {
279 if ( isset( $g[$var] ) ) {
280 $newGroup[$var] = $g[$var];
281 }
282 }
283
284 // Mark some fixed form optional messages automatically
285 if ( !isset( $newGroup['optional' ] ) ) {
286 $newGroup['optional'] = [];
287 }
288
289 // Mark extension name and skin names optional.
290 $newGroup['optional'][] = '*-extensionname';
291 $newGroup['optional'][] = 'skinname-*';
292
293 $fixedGroups[$id] = $newGroup;
294 }
295
296 return $fixedGroups;
297 }
298}
299
300class_alias( PremadeMediawikiExtensionGroups::class, 'PremadeMediawikiExtensionGroups' );
return[ 'Translate:ConfigHelper'=> static function():ConfigHelper { return new ConfigHelper();}, 'Translate:CsvTranslationImporter'=> static function(MediaWikiServices $services):CsvTranslationImporter { return new CsvTranslationImporter( $services->getWikiPageFactory());}, 'Translate:EntitySearch'=> static function(MediaWikiServices $services):EntitySearch { return new EntitySearch($services->getMainWANObjectCache(), $services->getCollationFactory() ->makeCollation( 'uca-default-u-kn'), MessageGroups::singleton(), $services->getNamespaceInfo(), $services->get( 'Translate:MessageIndex'), $services->getTitleParser(), $services->getTitleFormatter());}, 'Translate:ExternalMessageSourceStateImporter'=> static function(MediaWikiServices $services):ExternalMessageSourceStateImporter { return new ExternalMessageSourceStateImporter($services->getMainConfig(), $services->get( 'Translate:GroupSynchronizationCache'), $services->getJobQueueGroup(), LoggerFactory::getInstance( 'Translate.GroupSynchronization'), $services->get( 'Translate:MessageIndex'));}, 'Translate:FileFormatFactory'=> static function(MediaWikiServices $services):FileFormatFactory { return new FileFormatFactory( $services->getObjectFactory());}, 'Translate:GroupSynchronizationCache'=> static function(MediaWikiServices $services):GroupSynchronizationCache { return new GroupSynchronizationCache( $services->get( 'Translate:PersistentCache'));}, 'Translate:HookRunner'=> static function(MediaWikiServices $services):HookRunner { return new HookRunner( $services->getHookContainer());}, 'Translate:MessageBundleStore'=> static function(MediaWikiServices $services):MessageBundleStore { return new MessageBundleStore($services->get( 'Translate:RevTagStore'), $services->getJobQueueGroup(), $services->getLanguageNameUtils(), $services->get( 'Translate:MessageIndex'));}, 'Translate:MessageGroupReviewStore'=> static function(MediaWikiServices $services):MessageGroupReviewStore { return new MessageGroupReviewStore($services->getDBLoadBalancer(), $services->get( 'Translate:HookRunner'));}, 'Translate:MessageGroupStatsTableFactory'=> static function(MediaWikiServices $services):MessageGroupStatsTableFactory { return new MessageGroupStatsTableFactory($services->get( 'Translate:ProgressStatsTableFactory'), $services->getDBLoadBalancer(), $services->getLinkRenderer(), $services->get( 'Translate:MessageGroupReviewStore'), $services->getMainConfig() ->get( 'TranslateWorkflowStates') !==false);}, 'Translate:MessageIndex'=> static function(MediaWikiServices $services):MessageIndex { $params=$services->getMainConfig() ->get( 'TranslateMessageIndex');if(is_string( $params)) { $params=(array) $params;} $class=array_shift( $params);return new $class( $params);}, 'Translate:MessagePrefixStats'=> static function(MediaWikiServices $services):MessagePrefixStats { return new MessagePrefixStats( $services->getTitleParser());}, 'Translate:ParsingPlaceholderFactory'=> static function():ParsingPlaceholderFactory { return new ParsingPlaceholderFactory();}, 'Translate:PersistentCache'=> static function(MediaWikiServices $services):PersistentCache { return new PersistentDatabaseCache($services->getDBLoadBalancer(), $services->getJsonCodec());}, 'Translate:ProgressStatsTableFactory'=> static function(MediaWikiServices $services):ProgressStatsTableFactory { return new ProgressStatsTableFactory($services->getLinkRenderer(), $services->get( 'Translate:ConfigHelper'));}, 'Translate:RevTagStore'=> static function(MediaWikiServices $services):RevTagStore { return new RevTagStore($services->getDBLoadBalancerFactory());}, 'Translate:SubpageListBuilder'=> static function(MediaWikiServices $services):SubpageListBuilder { return new SubpageListBuilder($services->get( 'Translate:TranslatableBundleFactory'), $services->getLinkBatchFactory());}, 'Translate:TranslatableBundleExporter'=> static function(MediaWikiServices $services):TranslatableBundleExporter { return new TranslatableBundleExporter($services->get( 'Translate:SubpageListBuilder'), $services->getWikiExporterFactory(), $services->getDBLoadBalancer());}, 'Translate:TranslatableBundleFactory'=> static function(MediaWikiServices $services):TranslatableBundleFactory { return new TranslatableBundleFactory($services->get( 'Translate:TranslatablePageStore'), $services->get( 'Translate:MessageBundleStore'));}, 'Translate:TranslatableBundleImporter'=> static function(MediaWikiServices $services):TranslatableBundleImporter { return new TranslatableBundleImporter($services->getWikiImporterFactory(), $services->get( 'Translate:TranslatablePageParser'), $services->getRevisionLookup());}, 'Translate:TranslatableBundleMover'=> static function(MediaWikiServices $services):TranslatableBundleMover { return new TranslatableBundleMover($services->getMovePageFactory(), $services->getJobQueueGroup(), $services->getLinkBatchFactory(), $services->get( 'Translate:TranslatableBundleFactory'), $services->get( 'Translate:SubpageListBuilder'), $services->getMainConfig() ->get( 'TranslatePageMoveLimit'));}, 'Translate:TranslatableBundleStatusStore'=> static function(MediaWikiServices $services):TranslatableBundleStatusStore { return new TranslatableBundleStatusStore($services->getDBLoadBalancer() ->getConnection(DB_PRIMARY), $services->getCollationFactory() ->makeCollation( 'uca-default-u-kn'), $services->getDBLoadBalancer() ->getMaintenanceConnectionRef(DB_PRIMARY));}, 'Translate:TranslatablePageParser'=> static function(MediaWikiServices $services):TranslatablePageParser { return new TranslatablePageParser($services->get( 'Translate:ParsingPlaceholderFactory'));}, 'Translate:TranslatablePageStore'=> static function(MediaWikiServices $services):TranslatablePageStore { return new TranslatablePageStore($services->get( 'Translate:MessageIndex'), $services->getJobQueueGroup(), $services->get( 'Translate:RevTagStore'), $services->getDBLoadBalancer(), $services->get( 'Translate:TranslatableBundleStatusStore'), $services->get( 'Translate:TranslatablePageParser'),);}, 'Translate:TranslationStashReader'=> static function(MediaWikiServices $services):TranslationStashReader { $db=$services->getDBLoadBalancer() ->getConnection(DB_REPLICA);return new TranslationStashStorage( $db);}, 'Translate:TranslationStatsDataProvider'=> static function(MediaWikiServices $services):TranslationStatsDataProvider { return new TranslationStatsDataProvider(new ServiceOptions(TranslationStatsDataProvider::CONSTRUCTOR_OPTIONS, $services->getMainConfig()), $services->getObjectFactory(), $services->getDBLoadBalancer());}, 'Translate:TranslationUnitStoreFactory'=> static function(MediaWikiServices $services):TranslationUnitStoreFactory { return new TranslationUnitStoreFactory( $services->getDBLoadBalancer());}, 'Translate:TranslatorActivity'=> static function(MediaWikiServices $services):TranslatorActivity { $query=new TranslatorActivityQuery($services->getMainConfig(), $services->getDBLoadBalancer());return new TranslatorActivity($services->getMainObjectStash(), $query, $services->getJobQueueGroup());}, 'Translate:TtmServerFactory'=> static function(MediaWikiServices $services):TtmServerFactory { $config=$services->getMainConfig();$default=$config->get( 'TranslateTranslationDefaultService');if( $default===false) { $default=null;} return new TtmServerFactory( $config->get( 'TranslateTranslationServices'), $default);}]
@phpcs-require-sorted-array
Message group for MediaWiki extensions.
JsonFormat implements a message format where messages are encoded as key-value pairs in JSON objects.
Class which handles special definition format for MediaWiki extensions and skins.
createMessageGroup(string $id, array $info)
Creates MediaWikiExtensionMessageGroup objects from parsed data.
The versatile default implementation of StringMangler interface.
This class implements some basic functions that wrap around the YAML message group configurations.
static factory( $conf)
Interface for message groups.