Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
FileBasedMessageGroup.php
Go to the documentation of this file.
1<?php
18
28 public const NO_FILE_FORMAT = 1;
29
30 protected $reverseCodeMap;
31
39 public static function newFromMessageGroup(
40 MessageGroup $group,
41 string $targetPattern = ''
42 ) {
43 $conf = [
44 'BASIC' => [
45 'class' => self::class,
46 'id' => $group->getId(),
47 'label' => $group->getLabel(),
48 'namespace' => $group->getNamespace(),
49 ],
50 'FILES' => [
51 'sourcePattern' => '',
52 'targetPattern' => $targetPattern,
53 ],
54 ];
55
56 $group = MessageGroupBase::factory( $conf );
57 if ( !$group instanceof self ) {
58 $actual = get_class( $group );
59 throw new DomainException( "Expected FileBasedMessageGroup, got $actual" );
60 }
61
62 return $group;
63 }
64
65 public function getFFS(): SimpleFormat {
66 $class = $this->getFromConf( 'FILES', 'class' );
67 $format = $this->getFromConf( 'FILES', 'format' );
68
69 if ( $format !== null ) {
70 return Services::getInstance()->getFileFormatFactory()->create( $format, $this );
71 } elseif ( $class !== null ) {
72 return Services::getInstance()->getFileFormatFactory()->loadInstance( $class, $this );
73 } else {
74 throw new RuntimeException(
75 'FileFormatSupport class/format is not set for "' . $this->getId() . '".',
76 self::NO_FILE_FORMAT
77 );
78 }
79 }
80
81 public function exists(): bool {
82 return $this->getMessageGroupCache( $this->getSourceLanguage() )->exists();
83 }
84
85 public function load( $code ) {
86 $ffs = $this->getFFS();
87 $data = $ffs->read( $code );
88
89 return $data ? $data['MESSAGES'] : [];
90 }
91
97 public function parseExternal( string $code ): array {
98 $supportedKeys = [ 'MESSAGES', 'AUTHORS', 'EXTRA' ];
99
100 $parsedData = $this->getFFS()->read( $code );
101
102 // Ensure we return correct keys
103 $data = [];
104 foreach ( $supportedKeys as $key ) {
105 $data[$key] = $parsedData[$key] ?? [];
106 }
107
108 return $data;
109 }
110
115 public function getSourceFilePath( $code ) {
116 if ( $this->isSourceLanguage( $code ) ) {
117 $pattern = $this->getFromConf( 'FILES', 'definitionFile' );
118 if ( $pattern !== null ) {
119 return $this->replaceVariables( $pattern, $code );
120 }
121 }
122
123 $pattern = $this->getFromConf( 'FILES', 'sourcePattern' );
124 if ( $pattern === null ) {
125 throw new RuntimeException( 'No source file pattern defined.' );
126 }
127
128 return $this->replaceVariables( $pattern, $code );
129 }
130
131 public function getTargetFilename( $code ) {
132 // Check if targetPattern explicitly defined
133 $pattern = $this->getFromConf( 'FILES', 'targetPattern' );
134 if ( $pattern !== null ) {
135 return $this->replaceVariables( $pattern, $code );
136 }
137
138 // Check if definitionFile is explicitly defined
139 if ( $this->isSourceLanguage( $code ) ) {
140 $pattern = $this->getFromConf( 'FILES', 'definitionFile' );
141 }
142
143 // Fallback to sourcePattern which must be defined
144 if ( $pattern === null ) {
145 $pattern = $this->getFromConf( 'FILES', 'sourcePattern' );
146 }
147
148 if ( $pattern === null ) {
149 throw new RuntimeException( 'No source file pattern defined.' );
150 }
151
152 // For exports, the scripts take output directory. We want to
153 // return a path where the prefix is current directory instead
154 // of full path of the source location.
155 $pattern = str_replace( '%GROUPROOT%', '.', $pattern );
156 return $this->replaceVariables( $pattern, $code );
157 }
158
165 public function replaceVariables( $pattern, $code ) {
166 global $IP, $wgTranslateGroupRoot;
167
168 $variables = [
169 '%CODE%' => $this->mapCode( $code ),
170 '%MWROOT%' => $IP,
171 '%GROUPROOT%' => $wgTranslateGroupRoot,
172 '%GROUPID%' => $this->getId(),
173 ];
174
175 return str_replace( array_keys( $variables ), array_values( $variables ), $pattern );
176 }
177
182 public function mapCode( $code ) {
183 if ( !isset( $this->conf['FILES']['codeMap'] ) ) {
184 return $code;
185 }
186
187 if ( isset( $this->conf['FILES']['codeMap'][$code] ) ) {
188 return $this->conf['FILES']['codeMap'][$code];
189 } else {
190 if ( !isset( $this->reverseCodeMap ) ) {
191 $this->reverseCodeMap = array_flip( $this->conf['FILES']['codeMap'] );
192 }
193
194 if ( isset( $this->reverseCodeMap[$code] ) ) {
195 return 'x-invalidLanguageCode';
196 }
197
198 return $code;
199 }
200 }
201
202 public static function getExtraSchema(): array {
203 $schema = [
204 'root' => [
205 '_type' => 'array',
206 '_children' => [
207 'FILES' => [
208 '_type' => 'array',
209 '_children' => [
210 'class' => [
211 '_type' => 'text'
212 ],
213 'format' => [
214 '_type' => 'text'
215 ],
216 'codeMap' => [
217 '_type' => 'array',
218 '_ignore_extra_keys' => true,
219 '_children' => [],
220 ],
221 'definitionFile' => [
222 '_type' => 'text',
223 ],
224 'sourcePattern' => [
225 '_type' => 'text',
226 '_not_empty' => true,
227 ],
228 'targetPattern' => [
229 '_type' => 'text',
230 ],
231 ]
232 ]
233 ]
234 ]
235 ];
236
237 return $schema;
238 }
239
241 public function getKeys() {
242 $cache = $this->getMessageGroupCache( $this->getSourceLanguage() );
243 if ( !$cache->exists() ) {
244 return array_keys( $this->getDefinitions() );
245 } else {
246 return $cache->getKeys();
247 }
248 }
249
251 public function initCollection( $code ) {
252 $namespace = $this->getNamespace();
253 $messages = [];
254
255 $cache = $this->getMessageGroupCache( $this->getSourceLanguage() );
256 if ( $cache->exists() ) {
257 foreach ( $cache->getKeys() as $key ) {
258 $messages[$key] = $cache->get( $key );
259 }
260 }
261
262 $definitions = new MessageDefinitions( $messages, $namespace );
263 $collection = MessageCollection::newFromDefinitions( $definitions, $code );
264 $this->setTags( $collection );
265
266 return $collection;
267 }
268
270 public function getMessage( $key, $code ) {
271 $cache = $this->getMessageGroupCache( $code );
272 if ( $cache->exists() ) {
273 $msg = $cache->get( $key );
274
275 if ( $msg !== false ) {
276 return $msg;
277 }
278
279 // Try harder
280 $nkey = str_replace( ' ', '_', strtolower( $key ) );
281 $keys = $cache->getKeys();
282
283 foreach ( $keys as $k ) {
284 if ( $nkey === str_replace( ' ', '_', strtolower( $k ) ) ) {
285 return $cache->get( $k );
286 }
287 }
288
289 return null;
290 } else {
291 return null;
292 }
293 }
294
295 public function getMessageGroupCache( string $code ): MessageGroupCache {
296 $cacheFilePath = Utilities::cacheFile(
297 "translate_groupcache-{$this->getId()}/{$code}.cdb"
298 );
299
300 return new MessageGroupCache( $this, $code, $cacheFilePath );
301 }
302}
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 class implements default behavior for file based message groups.
static getExtraSchema()
Return a data structure that will be merged with the base schema.
initCollection( $code)
@inheritDoc
getMessage( $key, $code)
@inheritDoc
static newFromMessageGroup(MessageGroup $group, string $targetPattern='')
Constructs a FileBasedMessageGroup from any normal message group.
replaceVariables( $pattern, $code)
exists()
If this function returns false, the message group is ignored and treated like it would not be configu...
load( $code)
Returns a list of messages in a given language code.
A very basic FileFormatSupport module that implements some basic functionality and a simple binary ba...
Caches messages of file based message group source file.
This file contains the class for core message collections implementation.
Wrapper for message definitions, just to beauty the code.
Minimal service container.
Definition Services.php:58
Essentially random collection of helper functions, similar to GlobalFunctions.php.
Definition Utilities.php:31
This class implements some basic functions that wrap around the YAML message group configurations.
getId()
Returns the unique identifier for this group.
static factory( $conf)
Message groups are usually configured in YAML, though the actual storage format does not matter,...
Interface for message groups.
getNamespace()
Returns the namespace where messages are placed.
getId()
Returns the unique identifier for this group.
getLabel(IContextSource $context=null)
Returns the human readable label (as plain text).