Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
MessageGroupBase.php
1<?php
2declare( strict_types = 1 );
3
4use MediaWiki\Context\IContextSource;
13use MediaWiki\Languages\LanguageNameUtils;
14use MediaWiki\Linker\LinkTarget;
15use MediaWiki\MediaWikiServices;
16
29abstract class MessageGroupBase implements MessageGroup {
30 protected array $conf;
31 protected int $namespace;
32 protected ?StringMatcher $mangler = null;
33
34 protected function __construct() {
35 }
36
37 public static function factory( array $conf ): MessageGroup {
39 $obj = new $conf['BASIC']['class']();
40 $obj->conf = $conf;
41 $obj->namespace = $obj->parseNamespace();
42
43 return $obj;
44 }
45
46 public function getConfiguration(): array {
47 return $this->conf;
48 }
49
51 public function getId() {
52 return $this->conf['BASIC']['id'] ?? null;
53 }
54
56 public function getLabel( ?IContextSource $context = null ) {
57 return $this->conf['BASIC']['label'] ?? null;
58 }
59
61 public function getDescription( ?IContextSource $context = null ) {
62 return $this->conf['BASIC']['description'] ?? null;
63 }
64
66 public function getIcon() {
67 return $this->conf['BASIC']['icon'] ?? null;
68 }
69
71 public function getNamespace() {
72 return $this->namespace;
73 }
74
76 public function isMeta() {
77 return $this->conf['BASIC']['meta'] ?? null;
78 }
79
81 public function getDefinitions() {
82 return $this->load( $this->getSourceLanguage() );
83 }
84
86 public function getValidator() {
87 $validatorConfigs = $this->conf['VALIDATORS'] ?? [];
88 if ( !$validatorConfigs ) {
89 return null;
90 }
91
92 $msgValidator = new ValidationRunner( $this->getId() );
93
94 foreach ( $validatorConfigs as $config ) {
95 try {
96 $msgValidator->addValidator( $config );
97 } catch ( InvalidArgumentException $e ) {
98 $id = $this->getId();
99 throw new InvalidArgumentException(
100 "Unable to construct validator for message group $id: " . $e->getMessage(),
101 0,
102 $e
103 );
104 }
105 }
106
107 return $msgValidator;
108 }
109
111 public function getMangler() {
112 if ( $this->mangler === null ) {
113 $class = $this->conf['MANGLER']['class'] ?? StringMatcher::class;
114
115 if ( $class === 'StringMatcher' || $class === StringMatcher::class ) {
116 $this->mangler = new StringMatcher();
117 $manglerConfig = $this->conf['MANGLER'] ?? null;
118 if ( $manglerConfig ) {
119 $this->mangler->setConf( $manglerConfig );
120 }
121 } else {
122 throw new InvalidArgumentException(
123 "Unable to create StringMangler for group {$this->getId()}: " .
124 "Custom StringManglers ($class) are currently not supported."
125 );
126 }
127 }
128
129 return $this->mangler;
130 }
131
137 public function getInsertablesSuggester() {
138 $suggesters = [];
139 $insertableConf = $this->conf['INSERTABLES'] ?? [];
140
141 foreach ( $insertableConf as $config ) {
142 if ( !isset( $config['class'] ) ) {
143 throw new InvalidArgumentException(
144 'Insertable configuration for group: ' . $this->getId() .
145 ' does not provide a class.'
146 );
147 }
148
149 if ( !is_string( $config['class'] ) ) {
150 throw new InvalidArgumentException(
151 'Expected Insertable class to be string, got: ' . get_debug_type( $config['class'] ) .
152 ' for group: ' . $this->getId()
153 );
154 }
155
156 $suggesters[] = InsertableFactory::make( $config['class'], $config['params'] ?? [] );
157 }
158
159 // Get validators marked as insertable
160 $messageValidator = $this->getValidator();
161 if ( $messageValidator ) {
162 $suggesters = array_merge( $suggesters, $messageValidator->getInsertableValidators() );
163 }
164
165 return new CombinedInsertablesSuggester( $suggesters );
166 }
167
169 public function getKeys() {
170 return array_keys( $this->getDefinitions() );
171 }
172
174 public function getTags( $type = null ) {
175 if ( $type === null ) {
176 return array_map(
177 fn ( $patterns ) => $this->parseTags( $patterns ),
178 $this->getRawTags()
179 );
180 } else {
181 return $this->parseTags( $this->getRawTags( $type ) );
182 }
183 }
184
185 protected function parseTags( array $patterns ): array {
186 $messageKeys = $this->getKeys();
187
188 $matches = [];
189
190 // Collect exact keys, no point running them through string matcher
191 foreach ( $patterns as $index => $pattern ) {
192 if ( !str_contains( $pattern, '*' ) ) {
193 $matches[] = $pattern;
194 unset( $patterns[$index] );
195 }
196 }
197
198 if ( count( $patterns ) ) {
199 // Rest of the keys contain wildcards.
200 $mangler = new StringMatcher( '', $patterns );
201
202 // Use mangler to find messages that match.
203 foreach ( $messageKeys as $key ) {
204 if ( $mangler->matches( $key ) ) {
205 $matches[] = $key;
206 }
207 }
208 }
209
210 return $matches;
211 }
212
213 protected function getRawTags( ?string $type = null ): array {
214 $tags = $this->conf['TAGS'] ?? [];
215 if ( !$type ) {
216 return $tags;
217 }
218
219 return $tags[$type] ?? [];
220 }
221
222 protected function setTags( MessageCollection $collection ): void {
223 foreach ( $this->getTags() as $type => $tags ) {
224 $collection->setTags( $type, $tags );
225 }
226 }
227
228 protected function parseNamespace(): int {
229 $ns = $this->conf['BASIC']['namespace'] ?? null;
230
231 if ( is_int( $ns ) ) {
232 return $ns;
233 }
234
235 if ( defined( $ns ) ) {
236 return constant( $ns );
237 }
238
239 $index = MediaWikiServices::getInstance()->getContentLanguage()
240 ->getNsIndex( $ns );
241
242 if ( $index === false ) {
243 throw new RuntimeException( "No valid namespace defined, got $ns." );
244 }
245
246 return $index;
247 }
248
249 protected function isSourceLanguage( string $code ): bool {
250 return $code === $this->getSourceLanguage();
251 }
252
255 global $wgTranslateWorkflowStates;
256 $conf = $wgTranslateWorkflowStates ?: [];
257
258 Services::getInstance()->getHookRunner()
259 ->onTranslate_modifyMessageGroupStates( $this->getId(), $conf );
260
261 return new MessageGroupStates( $conf );
262 }
263
265 public function getTranslatableLanguages() {
266 $languageConfig = $this->conf['LANGUAGES'] ?? null;
267 if ( $languageConfig === null ) {
268 // No LANGUAGES section in the configuration.
269 return self::DEFAULT_LANGUAGES;
270 }
271
272 $codes = array_flip( array_keys( Utilities::getLanguageNames( LanguageNameUtils::AUTONYMS ) ) );
273
274 $exclusionList = $languageConfig['exclude'] ?? null;
275 if ( $exclusionList !== null ) {
276 if ( $exclusionList === '*' ) {
277 // All excluded languages
278 $codes = [];
279 } elseif ( is_array( $exclusionList ) ) {
280 foreach ( $exclusionList as $code ) {
281 unset( $codes[$code] );
282 }
283 }
284 } else {
285 // Treat lack of explicit exclusion list the same as excluding everything. This way,
286 // when one defines only inclusions, it means that only those languages are allowed.
287 $codes = [];
288 }
289
290 $disabledLanguages = Services::getInstance()->getConfigHelper()->getDisabledTargetLanguages();
291 // DWIM with $wgTranslateDisabledTargetLanguages, e.g. languages in that list should not unexpectedly
292 // be enabled when an inclusion list is used to include any language.
293 $checks = [ $this->getId(), strtok( $this->getId(), '-' ), '*' ];
294 foreach ( $checks as $check ) {
295 if ( isset( $disabledLanguages[$check] ) ) {
296 foreach ( array_keys( $disabledLanguages[$check] ) as $excludedCode ) {
297 unset( $codes[$excludedCode] );
298 }
299 }
300 }
301
302 $inclusionList = $languageConfig['include'] ?? null;
303 if ( $inclusionList !== null ) {
304 if ( $inclusionList === '*' ) {
305 // All languages included (except $wgTranslateDisabledTargetLanguages)
306 return null;
307 } elseif ( is_array( $inclusionList ) ) {
308 foreach ( $inclusionList as $code ) {
309 $codes[$code] = true;
310 }
311 }
312 }
313
314 return $codes;
315 }
316
318 public function getSupportConfig(): ?array {
319 return $this->conf['BASIC']['support'] ?? null;
320 }
321
323 public function getRelatedPage(): ?LinkTarget {
324 return null;
325 }
326}
return[ 'Translate:AggregateGroupManager'=> static function(MediaWikiServices $services):AggregateGroupManager { return new AggregateGroupManager($services->getTitleFactory(), $services->get( 'Translate:MessageGroupMetadata'));}, '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(LogNames::GROUP_SYNCHRONIZATION), $services->get( 'Translate:MessageIndex'), $services->getTitleFactory(), $services->get( 'Translate:MessageGroupSubscription'), new ServiceOptions(ExternalMessageSourceStateImporter::CONSTRUCTOR_OPTIONS, $services->getMainConfig()));}, 'Translate:FileBasedMessageGroupFactory'=> static function(MediaWikiServices $services):FileBasedMessageGroupFactory { return new FileBasedMessageGroupFactory(new MessageGroupConfigurationParser(), $services->getContentLanguageCode() ->toString(), 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:MessageBundleDependencyPurger'=> static function(MediaWikiServices $services):MessageBundleDependencyPurger { return new MessageBundleDependencyPurger( $services->get( 'Translate:TranslatableBundleFactory'));}, '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->getConnectionProvider());}, 'Translate:MessageGroupReviewStore'=> static function(MediaWikiServices $services):MessageGroupReviewStore { return new MessageGroupReviewStore($services->getConnectionProvider(), $services->get( 'Translate:HookRunner'));}, 'Translate:MessageGroupStatsTableFactory'=> static function(MediaWikiServices $services):MessageGroupStatsTableFactory { return new MessageGroupStatsTableFactory($services->get( 'Translate:ProgressStatsTableFactory'), $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(LogNames::GROUP_SUBSCRIPTION), new ServiceOptions(MessageGroupSubscription::CONSTRUCTOR_OPTIONS, $services->getMainConfig()));}, 'Translate:MessageGroupSubscriptionHookHandler'=> static function(MediaWikiServices $services):?MessageGroupSubscriptionHookHandler { if(! $services->getExtensionRegistry() ->isLoaded( 'Echo')) { return null;} return new MessageGroupSubscriptionHookHandler($services->get( 'Translate:MessageGroupSubscription'), $services->getUserFactory());}, 'Translate:MessageGroupSubscriptionStore'=> static function(MediaWikiServices $services):MessageGroupSubscriptionStore { return new MessageGroupSubscriptionStore( $services->getConnectionProvider());}, '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(LogNames::MAIN), $services->getMainObjectStash(), $services->getConnectionProvider(), 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->getConnectionProvider(), $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->getConnectionProvider());}, '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->getConnectionProvider());}, '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(), $services->getFormatterFactory());}, '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->getConnectionProvider(), $services->getObjectCacheFactory(), $services->getMainConfig() ->get( 'TranslatePageMoveLimit'));}, 'Translate:TranslatableBundleStatusStore'=> static function(MediaWikiServices $services):TranslatableBundleStatusStore { return new TranslatableBundleStatusStore($services->getConnectionProvider() ->getPrimaryDatabase(), $services->getCollationFactory() ->makeCollation( 'uca-default-u-kn'), $services->getDBLoadBalancer() ->getMaintenanceConnectionRef(DB_PRIMARY));}, 'Translate:TranslatablePageMarker'=> static function(MediaWikiServices $services):TranslatablePageMarker { return new TranslatablePageMarker($services->getConnectionProvider(), $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'), $services->get( 'Translate:MessageGroupSubscription'), $services->getFormatterFactory(), $services->get( 'Translate:HookRunner'),);}, '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->getConnectionProvider(), $services->get( 'Translate:TranslatableBundleStatusStore'), $services->get( 'Translate:TranslatablePageParser'), $services->get( 'Translate:MessageGroupMetadata'));}, 'Translate:TranslatablePageView'=> static function(MediaWikiServices $services):TranslatablePageView { return new TranslatablePageView($services->getConnectionProvider(), $services->get( 'Translate:TranslatablePageStateStore'), new ServiceOptions(TranslatablePageView::SERVICE_OPTIONS, $services->getMainConfig()));}, 'Translate:TranslateSandbox'=> static function(MediaWikiServices $services):TranslateSandbox { return new TranslateSandbox($services->getUserFactory(), $services->getConnectionProvider(), $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 { return new TranslationStashStorage( $services->getConnectionProvider() ->getPrimaryDatabase());}, 'Translate:TranslationStatsDataProvider'=> static function(MediaWikiServices $services):TranslationStatsDataProvider { return new TranslationStatsDataProvider(new ServiceOptions(TranslationStatsDataProvider::CONSTRUCTOR_OPTIONS, $services->getMainConfig()), $services->getObjectFactory(), $services->getConnectionProvider());}, '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);}, 'Translate:WorkflowStatesMessageGroupLoader'=> static function(MediaWikiServices $services):WorkflowStatesMessageGroupLoader { return new WorkflowStatesMessageGroupLoader(new ServiceOptions(WorkflowStatesMessageGroupLoader::CONSTRUCTOR_OPTIONS, $services->getMainConfig()),);},]
@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:60
A factory class used to instantiate instances of Insertables.
Essentially random collection of helper functions, similar to GlobalFunctions.php.
Definition Utilities.php:29
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()
@inheritDoc
getDescription(?IContextSource $context=null)
@inheritDoc
getTags( $type=null)
@inheritDoc
getNamespace()
@inheritDoc
getSupportConfig()
@inheritDoc
getMessageGroupStates()
@inheritDoc
getMangler()
@inheritDoc
getLabel(?IContextSource $context=null)
@inheritDoc
getInsertablesSuggester()
Returns the configured InsertablesSuggester if any.
getValidator()
@inheritDoc
getRelatedPage()
@inheritDoc
Interface for message groups.
getSourceLanguage()
Returns language code depicting the language of source text.
load( $code)
Returns a list of messages in a given language code.