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