Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
MessageGroupBase.php
Go to the documentation of this file.
1<?php
19use MediaWiki\Languages\LanguageNameUtils;
20use MediaWiki\Linker\LinkTarget;
21use MediaWiki\MediaWikiServices;
22
32abstract class MessageGroupBase implements MessageGroup {
33 protected $conf;
34 protected $namespace;
36 protected $mangler;
37
38 protected function __construct() {
39 }
40
46 public static function factory( $conf ) {
47 $obj = new $conf['BASIC']['class']();
48 $obj->conf = $conf;
49 $obj->namespace = $obj->parseNamespace();
50
51 return $obj;
52 }
53
54 public function getConfiguration() {
55 return $this->conf;
56 }
57
58 public function getId() {
59 return $this->getFromConf( 'BASIC', 'id' );
60 }
61
62 public function getLabel( IContextSource $context = null ) {
63 return $this->getFromConf( 'BASIC', 'label' );
64 }
65
66 public function getDescription( IContextSource $context = null ) {
67 return $this->getFromConf( 'BASIC', 'description' );
68 }
69
70 public function getIcon() {
71 return $this->getFromConf( 'BASIC', 'icon' );
72 }
73
74 public function getNamespace() {
75 return $this->namespace;
76 }
77
78 public function isMeta() {
79 return $this->getFromConf( 'BASIC', 'meta' );
80 }
81
82 public function getSourceLanguage() {
83 $conf = $this->getFromConf( 'BASIC', 'sourcelanguage' );
84
85 return $conf ?? 'en';
86 }
87
88 public function getDefinitions() {
89 $defs = $this->load( $this->getSourceLanguage() );
90
91 return $defs;
92 }
93
94 protected function getFromConf( $section, $key = null ) {
95 if ( $key === null ) {
96 return $this->conf[$section] ?? null;
97 }
98 return $this->conf[$section][$key] ?? null;
99 }
100
101 public function getValidator() {
102 $validatorConfigs = $this->getFromConf( 'VALIDATORS' );
103 if ( $validatorConfigs === null ) {
104 return null;
105 }
106
107 $msgValidator = new ValidationRunner( $this->getId() );
108
109 foreach ( $validatorConfigs as $config ) {
110 try {
111 $msgValidator->addValidator( $config );
112 } catch ( Exception $e ) {
113 $id = $this->getId();
114 throw new InvalidArgumentException(
115 "Unable to construct validator for message group $id: " . $e->getMessage(),
116 0,
117 $e
118 );
119 }
120 }
121
122 return $msgValidator;
123 }
124
125 public function getMangler() {
126 if ( !isset( $this->mangler ) ) {
127 $class = $this->getFromConf( 'MANGLER', 'class' ) ?? StringMatcher::class;
128
129 if ( $class === 'StringMatcher' || $class === StringMatcher::class ) {
130 $this->mangler = new StringMatcher();
131 $manglerConfig = $this->conf['MANGLER'] ?? null;
132 if ( $manglerConfig ) {
133 $this->mangler->setConf( $manglerConfig );
134 }
135
136 return $this->mangler;
137 }
138
139 throw new InvalidArgumentException(
140 'Unable to create StringMangler for group ' . $this->getId() . ': ' .
141 "Custom StringManglers ($class) are currently not supported."
142 );
143 }
144
145 return $this->mangler;
146 }
147
153 public function getInsertablesSuggester() {
154 $suggesters = [];
155 $insertableConf = $this->getFromConf( 'INSERTABLES' ) ?? [];
156
157 foreach ( $insertableConf as $config ) {
158 if ( !isset( $config['class'] ) ) {
159 throw new InvalidArgumentException(
160 'Insertable configuration for group: ' . $this->getId() .
161 ' does not provide a class.'
162 );
163 }
164
165 if ( !is_string( $config['class'] ) ) {
166 throw new InvalidArgumentException(
167 'Expected Insertable class to be string, got: ' . gettype( $config['class'] ) .
168 ' for group: ' . $this->getId()
169 );
170 }
171
172 $suggesters[] = InsertableFactory::make( $config['class'], $config['params'] ?? [] );
173 }
174
175 // Get validators marked as insertable
176 $messageValidator = $this->getValidator();
177 if ( $messageValidator ) {
178 $suggesters = array_merge( $suggesters, $messageValidator->getInsertableValidators() );
179 }
180
181 return new CombinedInsertablesSuggester( $suggesters );
182 }
183
185 public function getKeys() {
186 return array_keys( $this->getDefinitions() );
187 }
188
189 public function getTags( $type = null ) {
190 if ( $type === null ) {
191 $taglist = [];
192
193 foreach ( $this->getRawTags() as $type => $patterns ) {
194 $taglist[$type] = $this->parseTags( $patterns );
195 }
196
197 return $taglist;
198 } else {
199 return $this->parseTags( $this->getRawTags( $type ) );
200 }
201 }
202
203 protected function parseTags( $patterns ) {
204 $messageKeys = $this->getKeys();
205
206 $matches = [];
207
211 foreach ( $patterns as $index => $pattern ) {
212 if ( !str_contains( $pattern, '*' ) ) {
213 $matches[] = $pattern;
214 unset( $patterns[$index] );
215 }
216 }
217
218 if ( count( $patterns ) ) {
222 $mangler = new StringMatcher( '', $patterns );
223
227 foreach ( $messageKeys as $key ) {
228 if ( $mangler->matches( $key ) ) {
229 $matches[] = $key;
230 }
231 }
232 }
233
234 return $matches;
235 }
236
237 protected function getRawTags( $type = null ) {
238 if ( !isset( $this->conf['TAGS'] ) ) {
239 return [];
240 }
241
242 $tags = $this->conf['TAGS'];
243 if ( !$type ) {
244 return $tags;
245 }
246
247 return $tags[$type] ?? [];
248 }
249
250 protected function setTags( MessageCollection $collection ) {
251 foreach ( $this->getTags() as $type => $tags ) {
252 $collection->setTags( $type, $tags );
253 }
254 }
255
256 protected function parseNamespace() {
257 $ns = $this->getFromConf( 'BASIC', 'namespace' );
258
259 if ( is_int( $ns ) ) {
260 return $ns;
261 }
262
263 if ( defined( $ns ) ) {
264 return constant( $ns );
265 }
266
267 $index = MediaWikiServices::getInstance()->getContentLanguage()
268 ->getNsIndex( $ns );
269
270 if ( !$index ) {
271 throw new RuntimeException( "No valid namespace defined, got $ns." );
272 }
273
274 return $index;
275 }
276
277 protected function isSourceLanguage( $code ) {
278 return $code === $this->getSourceLanguage();
279 }
280
285 public function getMessageGroupStates() {
286 global $wgTranslateWorkflowStates;
287 $conf = $wgTranslateWorkflowStates ?: [];
288
289 Services::getInstance()->getHookRunner()
290 ->onTranslate_modifyMessageGroupStates( $this->getId(), $conf );
291
292 return new MessageGroupStates( $conf );
293 }
294
296 public function getTranslatableLanguages() {
297 $groupConfiguration = $this->getConfiguration();
298 if ( !isset( $groupConfiguration['LANGUAGES'] ) ) {
299 // No LANGUAGES section in the configuration.
300 return null;
301 }
302
303 $codes = array_flip( array_keys( Utilities::getLanguageNames( LanguageNameUtils::AUTONYMS ) ) );
304
305 $lists = $groupConfiguration['LANGUAGES'];
306 $exclusionList = $lists['exclude'] ?? null;
307 if ( $exclusionList !== null ) {
308 if ( $exclusionList === '*' ) {
309 // All excluded languages
310 $codes = [];
311 } elseif ( is_array( $exclusionList ) ) {
312 foreach ( $exclusionList as $code ) {
313 unset( $codes[$code] );
314 }
315 }
316 } else {
317 // Treat lack of explicit exclusion list the same as excluding everything. This way,
318 // when one defines only inclusions, it means that only those languages are allowed.
319 $codes = [];
320 }
321
322 $disabledLanguages = Services::getInstance()->getConfigHelper()->getDisabledTargetLanguages();
323 // DWIM with $wgTranslateDisabledTargetLanguages, e.g. languages in that list should not unexpectedly
324 // be enabled when an inclusion list is used to include any language.
325 $checks = [ $this->getId(), strtok( $this->getId(), '-' ), '*' ];
326 foreach ( $checks as $check ) {
327 if ( isset( $disabledLanguages[ $check ] ) ) {
328 foreach ( array_keys( $disabledLanguages[ $check ] ) as $excludedCode ) {
329 unset( $codes[ $excludedCode ] );
330 }
331 }
332 }
333
334 $inclusionList = $lists['include'] ?? null;
335 if ( $inclusionList !== null ) {
336 if ( $inclusionList === '*' ) {
337 // All languages included (except $wgTranslateDisabledTargetLanguages)
338 return null;
339 } elseif ( is_array( $inclusionList ) ) {
340 foreach ( $inclusionList as $code ) {
341 $codes[$code] = true;
342 }
343 }
344 }
345
346 return $codes;
347 }
348
349 public function getSupportConfig(): ?array {
350 return $this->getFromConf( 'BASIC', 'support' );
351 }
352
354 public function getRelatedPage(): ?LinkTarget {
355 return null;
356 }
357}
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
This file contains the class for core message collections implementation.
The versatile default implementation of StringMangler interface.
Minimal service container.
Definition Services.php:58
A factory class used to instantiate instances of Insertables.
Essentially random collection of helper functions, similar to GlobalFunctions.php.
Definition Utilities.php:31
Message validator is used to run validators to find common mistakes so that translators can fix them ...
This class implements some basic functions that wrap around the YAML message group configurations.
getTranslatableLanguages()
@inheritDoc
getDefinitions()
Shortcut for load( getSourceLanguage() ).
getDescription(IContextSource $context=null)
Returns a longer description about the group.
getTags( $type=null)
Returns message tags.
getSourceLanguage()
Returns language code depicting the language of source text.
getNamespace()
Returns the namespace where messages are placed.
getSupportConfig()
Gets support URL defined for the group if any.
getLabel(IContextSource $context=null)
Returns the human readable label (as plain text).
getMessageGroupStates()
Get the message group workflow state configuration.
getMangler()
Return a message mangler or null.
getId()
Returns the unique identifier for this group.
getInsertablesSuggester()
Returns the configured InsertablesSuggester if any.
getValidator()
Returns a message validator object or null.
static factory( $conf)
getIcon()
Returns an icon for this message group if any.
getRelatedPage()
@inheritDoc
Interface for message groups.
load( $code)
Returns a list of messages in a given language code.