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
31 protected $reverseCodeMap;
32
40 public static function newFromMessageGroup(
41 MessageGroup $group,
42 string $targetPattern = ''
43 ) {
44 $conf = [
45 'BASIC' => [
46 'class' => self::class,
47 'id' => $group->getId(),
48 'label' => $group->getLabel(),
49 'namespace' => $group->getNamespace(),
50 ],
51 'FILES' => [
52 'sourcePattern' => '',
53 'targetPattern' => $targetPattern,
54 ],
55 ];
56
57 $group = MessageGroupBase::factory( $conf );
58 if ( !$group instanceof self ) {
59 $actual = get_class( $group );
60 throw new DomainException( "Expected FileBasedMessageGroup, got $actual" );
61 }
62
63 return $group;
64 }
65
66 public function getFFS(): SimpleFormat {
67 $class = $this->getFromConf( 'FILES', 'class' );
68 $format = $this->getFromConf( 'FILES', 'format' );
69
70 if ( $format !== null ) {
71 return Services::getInstance()->getFileFormatFactory()->create( $format, $this );
72 } elseif ( $class !== null ) {
73 return Services::getInstance()->getFileFormatFactory()->loadInstance( $class, $this );
74 } else {
75 throw new RuntimeException(
76 'FileFormatSupport class/format is not set for "' . $this->getId() . '".',
77 self::NO_FILE_FORMAT
78 );
79 }
80 }
81
82 public function exists(): bool {
83 return $this->getMessageGroupCache( $this->getSourceLanguage() )->exists();
84 }
85
86 public function load( $code ) {
87 $ffs = $this->getFFS();
88 $data = $ffs->read( $code );
89
90 return $data ? $data['MESSAGES'] : [];
91 }
92
98 public function parseExternal( string $code ): array {
99 $supportedKeys = [ 'MESSAGES', 'AUTHORS', 'EXTRA' ];
100
101 $parsedData = $this->getFFS()->read( $code );
102
103 // Ensure we return correct keys
104 $data = [];
105 foreach ( $supportedKeys as $key ) {
106 $data[$key] = $parsedData[$key] ?? [];
107 }
108
109 return $data;
110 }
111
116 public function getSourceFilePath( $code ) {
117 if ( $this->isSourceLanguage( $code ) ) {
118 $pattern = $this->getFromConf( 'FILES', 'definitionFile' );
119 if ( $pattern !== null ) {
120 return $this->replaceVariables( $pattern, $code );
121 }
122 }
123
124 $pattern = $this->getFromConf( 'FILES', 'sourcePattern' );
125 if ( $pattern === null ) {
126 throw new RuntimeException( 'No source file pattern defined.' );
127 }
128
129 return $this->replaceVariables( $pattern, $code );
130 }
131
132 public function getTargetFilename( $code ) {
133 // Check if targetPattern explicitly defined
134 $pattern = $this->getFromConf( 'FILES', 'targetPattern' );
135 if ( $pattern !== null ) {
136 return $this->replaceVariables( $pattern, $code );
137 }
138
139 // Check if definitionFile is explicitly defined
140 if ( $this->isSourceLanguage( $code ) ) {
141 $pattern = $this->getFromConf( 'FILES', 'definitionFile' );
142 }
143
144 // Fallback to sourcePattern which must be defined
145 $pattern ??= $this->getFromConf( 'FILES', 'sourcePattern' );
146
147 if ( $pattern === null ) {
148 throw new RuntimeException( 'No source file pattern defined.' );
149 }
150
151 // For exports, the scripts take output directory. We want to
152 // return a path where the prefix is current directory instead
153 // of full path of the source location.
154 $pattern = str_replace( '%GROUPROOT%', '.', $pattern );
155 return $this->replaceVariables( $pattern, $code );
156 }
157
164 public function replaceVariables( $pattern, $code ) {
165 global $IP, $wgTranslateGroupRoot;
166
167 $variables = [
168 '%CODE%' => $this->mapCode( $code ),
169 '%MWROOT%' => $IP,
170 '%GROUPROOT%' => $wgTranslateGroupRoot,
171 '%GROUPID%' => $this->getId(),
172 ];
173
174 return str_replace( array_keys( $variables ), array_values( $variables ), $pattern );
175 }
176
181 public function mapCode( $code ) {
182 if ( !isset( $this->conf['FILES']['codeMap'] ) ) {
183 return $code;
184 }
185
186 if ( isset( $this->conf['FILES']['codeMap'][$code] ) ) {
187 return $this->conf['FILES']['codeMap'][$code];
188 } else {
189 if ( $this->reverseCodeMap === null ) {
190 $this->reverseCodeMap = array_flip( $this->conf['FILES']['codeMap'] );
191 }
192
193 if ( isset( $this->reverseCodeMap[$code] ) ) {
194 return 'x-invalidLanguageCode';
195 }
196
197 return $code;
198 }
199 }
200
201 public static function getExtraSchema(): array {
202 $schema = [
203 'root' => [
204 '_type' => 'array',
205 '_children' => [
206 'FILES' => [
207 '_type' => 'array',
208 '_children' => [
209 'class' => [
210 '_type' => 'text'
211 ],
212 'format' => [
213 '_type' => 'text'
214 ],
215 'codeMap' => [
216 '_type' => 'array',
217 '_ignore_extra_keys' => true,
218 '_children' => [],
219 ],
220 'definitionFile' => [
221 '_type' => 'text',
222 ],
223 'sourcePattern' => [
224 '_type' => 'text',
225 '_not_empty' => true,
226 ],
227 'targetPattern' => [
228 '_type' => 'text',
229 ],
230 ]
231 ]
232 ]
233 ]
234 ];
235
236 return $schema;
237 }
238
240 public function getKeys() {
241 $cache = $this->getMessageGroupCache( $this->getSourceLanguage() );
242 if ( !$cache->exists() ) {
243 return array_keys( $this->getDefinitions() );
244 } else {
245 return $cache->getKeys();
246 }
247 }
248
250 public function initCollection( $code ) {
251 $namespace = $this->getNamespace();
252 $messages = [];
253
254 $cache = $this->getMessageGroupCache( $this->getSourceLanguage() );
255 if ( $cache->exists() ) {
256 foreach ( $cache->getKeys() as $key ) {
257 $messages[$key] = $cache->get( $key );
258 }
259 }
260
261 $definitions = new MessageDefinitions( $messages, $namespace );
262 $collection = MessageCollection::newFromDefinitions( $definitions, $code );
263 $this->setTags( $collection );
264
265 return $collection;
266 }
267
269 public function getMessage( $key, $code ) {
270 $cache = $this->getMessageGroupCache( $code );
271 if ( $cache->exists() ) {
272 $msg = $cache->get( $key );
273
274 if ( $msg !== false ) {
275 return $msg;
276 }
277
278 // Try harder
279 $nkey = str_replace( ' ', '_', strtolower( $key ) );
280 $keys = $cache->getKeys();
281
282 foreach ( $keys as $k ) {
283 if ( $nkey === str_replace( ' ', '_', strtolower( $k ) ) ) {
284 return $cache->get( $k );
285 }
286 }
287
288 return null;
289 } else {
290 return null;
291 }
292 }
293
294 public function getMessageGroupCache( string $code ): MessageGroupCache {
295 $cacheFilePath = Utilities::cacheFile(
296 "translate_groupcache-{$this->getId()}/{$code}.cdb"
297 );
298
299 return new MessageGroupCache( $this, $code, $cacheFilePath );
300 }
301}
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(), 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 { 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());}, '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);}]
@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:59
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).