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;
12use MessageGroup;
14use RuntimeException;
15use UnexpectedValueException;
16
23 protected string $idPrefix = 'ext-';
24 protected ?int $namespace = null;
26 protected string $path;
28 protected string $definitionFile;
29
38 public function __construct( string $def, string $path ) {
39 $this->definitionFile = $def;
40 $this->path = rtrim( $path, '/' );
41 }
42
44 protected function getDefaultNamespace(): int {
45 return NS_MEDIAWIKI;
46 }
47
49 protected function getNamespace(): int {
50 if ( $this->namespace === null ) {
51 $this->namespace = $this->getDefaultNamespace();
52 }
53 return $this->namespace;
54 }
55
57 public function setGroupPrefix( string $value ): void {
58 $this->idPrefix = $value;
59 }
60
62 public function setNamespace( int $value ): void {
63 $this->namespace = $value;
64 }
65
67 public function register( array &$list, array &$deps ): void {
68 $groups = $this->parseFile();
69 $groups = $this->processGroups( $groups );
70 foreach ( $groups as $id => $g ) {
71 $list[$id] = $this->createMessageGroup( $id, $g );
72 }
73
74 $deps[] = new FileDependency( $this->definitionFile );
75 }
76
82 protected function createMessageGroup( string $id, array $info ): MessageGroup {
83 $conf = [];
84 $conf['BASIC']['class'] = MediaWikiExtensionMessageGroup::class;
85 $conf['BASIC']['id'] = $id;
86 $conf['BASIC']['namespace'] = $this->getNamespace();
87 $conf['BASIC']['label'] = $info['name'];
88
89 if ( isset( $info['desc'] ) ) {
90 $conf['BASIC']['description'] = $info['desc'];
91 } else {
92 $conf['BASIC']['descriptionmsg'] = $info['descmsg'];
93 }
94
95 $conf['FILES']['class'] = JsonFormat::class;
96 $conf['FILES']['sourcePattern'] = $this->path . '/' . $info['file'];
97
98 // @todo Find a better way
99 if ( isset( $info['aliasfile'] ) ) {
100 $conf['FILES']['aliasFileSource'] = $this->path . '/' . $info['aliasfile'];
101 $conf['FILES']['aliasFile'] = $info['aliasfile'];
102 }
103 if ( isset( $info['magicfile'] ) ) {
104 $conf['FILES']['magicFileSource'] = $this->path . '/' . $info['magicfile'];
105 $conf['FILES']['magicFile'] = $info['magicfile'];
106 }
107
108 if ( isset( $info['prefix'] ) ) {
109 $conf['MANGLER']['class'] = StringMatcher::class;
110 $conf['MANGLER']['prefix'] = $info['prefix'];
111 $conf['MANGLER']['patterns'] = $info['mangle'];
112
113 $mangler = new StringMatcher( $info['prefix'], $info['mangle'] );
114 if ( isset( $info['ignored'] ) ) {
115 $info['ignored'] = $mangler->mangleList( $info['ignored'] );
116 }
117 if ( isset( $info['optional'] ) ) {
118 $info['optional'] = $mangler->mangleList( $info['optional'] );
119 }
120 }
121
122 $conf['VALIDATORS'] = [
123 [ 'id' => 'BraceBalance' ],
124 [ 'id' => 'MediaWikiLink' ],
125 [ 'id' => 'MediaWikiPageName' ],
126 [ 'id' => 'MediaWikiParameter' ],
127 [ 'id' => 'MediaWikiPlural' ],
128 ];
129
130 $conf['INSERTABLES'] = [
131 [ 'class' => MediaWikiInsertablesSuggester::class ],
132 [ 'class' => UrlInsertablesSuggester::class ]
133 ];
134
135 if ( isset( $info['optional'] ) ) {
136 $conf['TAGS']['optional'] = $info['optional'];
137 }
138 if ( isset( $info['ignored'] ) ) {
139 $conf['TAGS']['ignored'] = $info['ignored'];
140 }
141
142 if ( isset( $info['languages'] ) ) {
143 $conf['LANGUAGES'] = [
144 'include' => [],
145 'exclude' => [],
146 ];
147
148 foreach ( $info['languages'] as $tagSpec ) {
149 if ( preg_match( '/^([+-])?(.+)$/', $tagSpec, $m ) ) {
150 [ , $sign, $tag ] = $m;
151 if ( $sign === '+' ) {
152 $conf['LANGUAGES']['include'][] = $tag;
153 } elseif ( $sign === '-' ) {
154 $conf['LANGUAGES']['exclude'][] = $tag;
155 } else {
156 $conf['LANGUAGES']['exclude'] = '*';
157 $conf['LANGUAGES']['include'][] = $tag;
158 }
159 }
160 }
161 }
162
163 return MessageGroupBase::factory( $conf );
164 }
165
166 protected function parseFile(): array {
167 $defines = file_get_contents( $this->definitionFile );
168 $linefeed = '(\r\n|\n)';
169 $sections = array_map(
170 'trim',
171 preg_split( "/$linefeed{2,}/", $defines, -1, PREG_SPLIT_NO_EMPTY )
172 );
173 $groups = [];
174
175 foreach ( $sections as $section ) {
176 $lines = array_map( 'trim', preg_split( "/$linefeed/", $section ) );
177 $newGroup = [];
178
179 foreach ( $lines as $line ) {
180 if ( $line === '' || $line[0] === '#' ) {
181 continue;
182 }
183
184 if ( !str_contains( $line, '=' ) ) {
185 if ( empty( $newGroup['name'] ) ) {
186 $newGroup['name'] = $line;
187 } else {
188 throw new RuntimeException( 'Trying to define name twice: ' . $line );
189 }
190 } else {
191 [ $key, $value ] = array_map( 'trim', explode( '=', $line, 2 ) );
192 switch ( $key ) {
193 case 'aliasfile':
194 case 'desc':
195 case 'descmsg':
196 case 'file':
197 case 'id':
198 case 'magicfile':
199 case 'var':
200 $newGroup[$key] = $value;
201 break;
202 case 'optional':
203 case 'ignored':
204 case 'languages':
205 $values = array_map( 'trim', explode( ',', $value ) );
206 if ( !isset( $newGroup[$key] ) ) {
207 $newGroup[$key] = [];
208 }
209 $newGroup[$key] = array_merge( $newGroup[$key], $values );
210 break;
211 case 'prefix':
212 [ $prefix, $messages ] = array_map(
213 'trim',
214 explode( '|', $value, 2 )
215 );
216 if ( isset( $newGroup['prefix'] ) && $newGroup['prefix'] !== $prefix ) {
217 throw new RuntimeException(
218 "Only one prefix supported: {$newGroup['prefix']} !== $prefix"
219 );
220 }
221 $newGroup['prefix'] = $prefix;
222
223 if ( !isset( $newGroup['mangle'] ) ) {
224 $newGroup['mangle'] = [];
225 }
226
227 $messages = array_map( 'trim', explode( ',', $messages ) );
228 $newGroup['mangle'] = array_merge( $newGroup['mangle'], $messages );
229 break;
230 default:
231 throw new UnexpectedValueException( 'Unknown key:' . $key );
232 }
233 }
234 }
235
236 if ( count( $newGroup ) ) {
237 if ( empty( $newGroup['name'] ) ) {
238 throw new RuntimeException( "Name missing\n" . print_r( $newGroup, true ) );
239 }
240 $groups[] = $newGroup;
241 }
242 }
243
244 return $groups;
245 }
246
247 protected function processGroups( array $groups ): array {
248 $fixedGroups = [];
249 foreach ( $groups as $g ) {
250 $name = $g['name'];
251
252 $id = $g['id'] ?? $this->idPrefix . preg_replace( '/\s+/', '', strtolower( $name ) );
253
254 if ( !isset( $g['file'] ) ) {
255 $file = preg_replace( '/\s+/', '', "$name/i18n/%CODE%.json" );
256 } else {
257 $file = $g['file'];
258 }
259
260 $descMsg = $g['descmsg'] ?? str_replace( $this->idPrefix, '', $id ) . '-desc';
261
262 $newGroup = [
263 'name' => $name,
264 'file' => $file,
265 'descmsg' => $descMsg,
266 ];
267
268 $copyVars = [
269 'aliasfile',
270 'desc',
271 'ignored',
272 'languages',
273 'magicfile',
274 'mangle',
275 'optional',
276 'prefix',
277 'var',
278 ];
279
280 foreach ( $copyVars as $var ) {
281 if ( isset( $g[$var] ) ) {
282 $newGroup[$var] = $g[$var];
283 }
284 }
285
286 // Mark some fixed form optional messages automatically
287 if ( !isset( $newGroup['optional' ] ) ) {
288 $newGroup['optional'] = [];
289 }
290
291 // Mark extension name and skin names optional.
292 $newGroup['optional'][] = '*-extensionname';
293 $newGroup['optional'][] = 'skinname-*';
294
295 $fixedGroups[$id] = $newGroup;
296 }
297
298 return $fixedGroups;
299 }
300}
301
302class_alias( PremadeMediaWikiExtensionGroups::class, 'PremadeMediaWikiExtensionGroups' );
return[ 'Translate:AggregateGroupManager'=> static function(MediaWikiServices $services):AggregateGroupManager { return new AggregateGroupManager( $services->getTitleFactory());}, 'Translate:AggregateGroupMessageGroupFactory'=> static function(MediaWikiServices $services):AggregateGroupMessageGroupFactory { return new AggregateGroupMessageGroupFactory($services->get( 'Translate:MessageGroupMetadata'));}, '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:ExternalMessageSourceStateComparator'=> static function(MediaWikiServices $services):ExternalMessageSourceStateComparator { return new ExternalMessageSourceStateComparator(new SimpleStringComparator(), $services->getRevisionLookup(), $services->getPageStore());}, 'Translate:ExternalMessageSourceStateImporter'=> static function(MediaWikiServices $services):ExternalMessageSourceStateImporter { return new ExternalMessageSourceStateImporter($services->get( 'Translate:GroupSynchronizationCache'), $services->getJobQueueGroup(), LoggerFactory::getInstance( 'Translate.GroupSynchronization'), $services->get( 'Translate:MessageIndex'), $services->getTitleFactory(), new ServiceOptions(ExternalMessageSourceStateImporter::CONSTRUCTOR_OPTIONS, $services->getMainConfig()));}, 'Translate:FileBasedMessageGroupFactory'=> static function(MediaWikiServices $services):FileBasedMessageGroupFactory { return new FileBasedMessageGroupFactory(new MessageGroupConfigurationParser(), new ServiceOptions(FileBasedMessageGroupFactory::SERVICE_OPTIONS, $services->getMainConfig()),);}, '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:HookDefinedMessageGroupFactory'=> static function(MediaWikiServices $services):HookDefinedMessageGroupFactory { return new HookDefinedMessageGroupFactory( $services->get( 'Translate:HookRunner'));}, 'Translate:HookRunner'=> static function(MediaWikiServices $services):HookRunner { return new HookRunner( $services->getHookContainer());}, 'Translate:MessageBundleMessageGroupFactory'=> static function(MediaWikiServices $services):MessageBundleMessageGroupFactory { return new MessageBundleMessageGroupFactory($services->get( 'Translate:MessageGroupMetadata'), new ServiceOptions(MessageBundleMessageGroupFactory::SERVICE_OPTIONS, $services->getMainConfig()),);}, 'Translate:MessageBundleStore'=> static function(MediaWikiServices $services):MessageBundleStore { return new MessageBundleStore($services->get( 'Translate:RevTagStore'), $services->getJobQueueGroup(), $services->getLanguageNameUtils(), $services->get( 'Translate:MessageIndex'), $services->get( 'Translate:MessageGroupMetadata'));}, 'Translate:MessageBundleTranslationLoader'=> static function(MediaWikiServices $services):MessageBundleTranslationLoader { return new MessageBundleTranslationLoader( $services->getLanguageFallback());}, 'Translate:MessageGroupMetadata'=> static function(MediaWikiServices $services):MessageGroupMetadata { return new MessageGroupMetadata( $services->getDBLoadBalancer());}, '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->get( 'Translate:MessageGroupMetadata'), $services->getMainConfig() ->get( 'TranslateWorkflowStates') !==false);}, 'Translate:MessageGroupSubscription'=> static function(MediaWikiServices $services):MessageGroupSubscription { return new MessageGroupSubscription($services->get( 'Translate:MessageGroupSubscriptionStore'), $services->getJobQueueGroup(), $services->getUserIdentityLookup(), LoggerFactory::getInstance( 'Translate.MessageGroupSubscription'), new ServiceOptions(MessageGroupSubscription::CONSTRUCTOR_OPTIONS, $services->getMainConfig()));}, 'Translate:MessageGroupSubscriptionHookHandler'=> static function(MediaWikiServices $services):MessageGroupSubscriptionHookHandler { return new MessageGroupSubscriptionHookHandler($services->get( 'Translate:MessageGroupSubscription'), $services->getUserFactory());}, 'Translate:MessageGroupSubscriptionStore'=> static function(MediaWikiServices $services):MessageGroupSubscriptionStore { return new MessageGroupSubscriptionStore( $services->getDBLoadBalancerFactory());}, 'Translate:MessageIndex'=> static function(MediaWikiServices $services):MessageIndex { $params=(array) $services->getMainConfig() ->get( 'TranslateMessageIndex');$class=array_shift( $params);$implementationMap=['HashMessageIndex'=> HashMessageIndex::class, 'CDBMessageIndex'=> CDBMessageIndex::class, 'DatabaseMessageIndex'=> DatabaseMessageIndex::class, 'hash'=> HashMessageIndex::class, 'cdb'=> CDBMessageIndex::class, 'database'=> DatabaseMessageIndex::class,];$messageIndexStoreClass=$implementationMap[$class] ?? $implementationMap['database'];return new MessageIndex(new $messageIndexStoreClass, $services->getMainWANObjectCache(), $services->getJobQueueGroup(), $services->get( 'Translate:HookRunner'), LoggerFactory::getInstance( 'Translate'), $services->getMainObjectStash(), $services->getDBLoadBalancerFactory(), $services->get( 'Translate:MessageGroupSubscription'), new ServiceOptions(MessageIndex::SERVICE_OPTIONS, $services->getMainConfig()),);}, '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'), $services->get( 'Translate:MessageGroupMetadata'));}, 'Translate:RevTagStore'=> static function(MediaWikiServices $services):RevTagStore { return new RevTagStore( $services->getDBLoadBalancer());}, 'Translate:SubpageListBuilder'=> static function(MediaWikiServices $services):SubpageListBuilder { return new SubpageListBuilder($services->get( 'Translate:TranslatableBundleFactory'), $services->getLinkBatchFactory());}, 'Translate:TranslatableBundleDeleter'=> static function(MediaWikiServices $services):TranslatableBundleDeleter { return new TranslatableBundleDeleter($services->getMainObjectStash(), $services->getJobQueueGroup(), $services->get( 'Translate:SubpageListBuilder'), $services->get( 'Translate:TranslatableBundleFactory'));}, '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(), $services->getNamespaceInfo(), $services->getTitleFactory());}, '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->getDBLoadBalancerFactory(), $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:TranslatablePageMarker'=> static function(MediaWikiServices $services):TranslatablePageMarker { return new TranslatablePageMarker($services->getDBLoadBalancer(), $services->getJobQueueGroup(), $services->getLinkRenderer(), MessageGroups::singleton(), $services->get( 'Translate:MessageIndex'), $services->getTitleFormatter(), $services->getTitleParser(), $services->get( 'Translate:TranslatablePageParser'), $services->get( 'Translate:TranslatablePageStore'), $services->get( 'Translate:TranslatablePageStateStore'), $services->get( 'Translate:TranslationUnitStoreFactory'), $services->get( 'Translate:MessageGroupMetadata'), $services->getWikiPageFactory(), $services->get( 'Translate:TranslatablePageView'));}, 'Translate:TranslatablePageMessageGroupFactory'=> static function(MediaWikiServices $services):TranslatablePageMessageGroupFactory { return new TranslatablePageMessageGroupFactory(new ServiceOptions(TranslatablePageMessageGroupFactory::SERVICE_OPTIONS, $services->getMainConfig()),);}, 'Translate:TranslatablePageParser'=> static function(MediaWikiServices $services):TranslatablePageParser { return new TranslatablePageParser($services->get( 'Translate:ParsingPlaceholderFactory'));}, 'Translate:TranslatablePageStateStore'=> static function(MediaWikiServices $services):TranslatablePageStateStore { return new TranslatablePageStateStore($services->get( 'Translate:PersistentCache'), $services->getPageStore());}, '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'), $services->get( 'Translate:MessageGroupMetadata'));}, 'Translate:TranslatablePageView'=> static function(MediaWikiServices $services):TranslatablePageView { return new TranslatablePageView($services->getDBLoadBalancerFactory(), $services->get( 'Translate:TranslatablePageStateStore'), new ServiceOptions(TranslatablePageView::SERVICE_OPTIONS, $services->getMainConfig()));}, 'Translate:TranslateSandbox'=> static function(MediaWikiServices $services):TranslateSandbox { return new TranslateSandbox($services->getUserFactory(), $services->getDBLoadBalancer(), $services->getPermissionManager(), $services->getAuthManager(), $services->getUserGroupManager(), $services->getActorStore(), $services->getUserOptionsManager(), $services->getJobQueueGroup(), $services->get( 'Translate:HookRunner'), new ServiceOptions(TranslateSandbox::CONSTRUCTOR_OPTIONS, $services->getMainConfig()));}, '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.