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 $format = $this->conf['FILES']['format'] ?? null;
68 $class = $this->conf['FILES']['class'] ?? null;
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
83 public function exists(): bool {
84 return $this->getMessageGroupCache( $this->getSourceLanguage() )->exists();
85 }
86
88 public function load( $code ) {
89 $ffs = $this->getFFS();
90 $data = $ffs->read( $code );
91
92 return $data ? $data['MESSAGES'] : [];
93 }
94
100 public function parseExternal( string $code ): array {
101 $supportedKeys = [ 'MESSAGES', 'AUTHORS', 'EXTRA' ];
102
103 $parsedData = $this->getFFS()->read( $code );
104
105 // Ensure we return correct keys
106 $data = [];
107 foreach ( $supportedKeys as $key ) {
108 $data[$key] = $parsedData[$key] ?? [];
109 }
110
111 return $data;
112 }
113
118 public function getSourceFilePath( $code ) {
119 if ( $this->isSourceLanguage( $code ) ) {
120 $pattern = $this->conf['FILES']['definitionFile'] ?? null;
121 if ( $pattern !== null ) {
122 return $this->replaceVariables( $pattern, $code );
123 }
124 }
125
126 $pattern = $this->conf['FILES']['sourcePattern'] ?? null;
127 if ( $pattern === null ) {
128 throw new RuntimeException( 'No source file pattern defined.' );
129 }
130
131 return $this->replaceVariables( $pattern, $code );
132 }
133
134 public function getTargetFilename( string $code ): string {
135 // Check if targetPattern explicitly defined
136 $pattern = $this->conf['FILES']['targetPattern'] ?? null;
137 if ( $pattern !== null ) {
138 return $this->replaceVariables( $pattern, $code );
139 }
140
141 // Check if definitionFile is explicitly defined
142 if ( $this->isSourceLanguage( $code ) ) {
143 $pattern = $this->conf['FILES']['definitionFile'] ?? null;
144 }
145
146 // Fallback to sourcePattern which must be defined
147 $pattern ??= $this->conf['FILES']['sourcePattern'] ?? null;
148
149 if ( $pattern === null ) {
150 throw new RuntimeException( 'No source file pattern defined.' );
151 }
152
153 // For exports, the scripts take output directory. We want to
154 // return a path where the prefix is current directory instead
155 // of full path of the source location.
156 $pattern = str_replace( '%GROUPROOT%', '.', $pattern );
157 return $this->replaceVariables( $pattern, $code );
158 }
159
166 public function replaceVariables( $pattern, $code ) {
167 global $IP, $wgTranslateGroupRoot;
168
169 $variables = [
170 '%CODE%' => $this->mapCode( $code ),
171 '%MWROOT%' => $IP,
172 '%GROUPROOT%' => $wgTranslateGroupRoot,
173 '%GROUPID%' => $this->getId(),
174 ];
175
176 return str_replace( array_keys( $variables ), array_values( $variables ), $pattern );
177 }
178
183 public function mapCode( $code ) {
184 if ( !isset( $this->conf['FILES']['codeMap'] ) ) {
185 return $code;
186 }
187
188 if ( isset( $this->conf['FILES']['codeMap'][$code] ) ) {
189 return $this->conf['FILES']['codeMap'][$code];
190 } else {
191 if ( $this->reverseCodeMap === null ) {
192 $this->reverseCodeMap = array_flip( $this->conf['FILES']['codeMap'] );
193 }
194
195 if ( isset( $this->reverseCodeMap[$code] ) ) {
196 return 'x-invalidLanguageCode';
197 }
198
199 return $code;
200 }
201 }
202
203 public static function getExtraSchema(): array {
204 $schema = [
205 'root' => [
206 '_type' => 'array',
207 '_children' => [
208 'FILES' => [
209 '_type' => 'array',
210 '_children' => [
211 'class' => [
212 '_type' => 'text'
213 ],
214 'format' => [
215 '_type' => 'text'
216 ],
217 'codeMap' => [
218 '_type' => 'array',
219 '_ignore_extra_keys' => true,
220 '_children' => [],
221 ],
222 'definitionFile' => [
223 '_type' => 'text',
224 ],
225 'sourcePattern' => [
226 '_type' => 'text',
227 '_not_empty' => true,
228 ],
229 'targetPattern' => [
230 '_type' => 'text',
231 ],
232 ]
233 ]
234 ]
235 ]
236 ];
237
238 return $schema;
239 }
240
242 public function getKeys() {
243 $cache = $this->getMessageGroupCache( $this->getSourceLanguage() );
244 if ( !$cache->exists() ) {
245 return array_keys( $this->getDefinitions() );
246 } else {
247 return $cache->getKeys();
248 }
249 }
250
252 public function initCollection( $code ) {
253 $namespace = $this->getNamespace();
254 $messages = [];
255
256 $cache = $this->getMessageGroupCache( $this->getSourceLanguage() );
257 if ( $cache->exists() ) {
258 foreach ( $cache->getKeys() as $key ) {
259 $messages[$key] = $cache->get( $key );
260 }
261 }
262
263 $definitions = new MessageDefinitions( $messages, $namespace );
264 $collection = MessageCollection::newFromDefinitions( $definitions, $code );
265 $this->setTags( $collection );
266
267 return $collection;
268 }
269
271 public function getMessage( $key, $code ) {
272 $cache = $this->getMessageGroupCache( $code );
273 if ( $cache->exists() ) {
274 $msg = $cache->get( $key );
275
276 if ( $msg !== false ) {
277 return $msg;
278 }
279
280 // Try harder
281 $nkey = str_replace( ' ', '_', strtolower( $key ) );
282 $keys = $cache->getKeys();
283
284 foreach ( $keys as $k ) {
285 if ( $nkey === str_replace( ' ', '_', strtolower( $k ) ) ) {
286 return $cache->get( $k );
287 }
288 }
289
290 return null;
291 } else {
292 return null;
293 }
294 }
295
296 public function getMessageGroupCache( string $code ): MessageGroupCache {
297 $cacheFilePath = Utilities::cacheFile(
298 "translate_groupcache-{$this->getId()}/{$code}.cdb"
299 );
300
301 return new MessageGroupCache( $this, $code, $cacheFilePath );
302 }
303
304 public function getSourceLanguage(): string {
305 // This is set in FileBasedMessageGroupFactory, so the fallback is only used if
306 // something is creating these groups manually.
307 return $this->conf['BASIC']['sourcelanguage'] ?? 'en';
308 }
309}
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->getConnectionProvider());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 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
getSourceLanguage()
Returns language code depicting the language of source text.
getMessage( $key, $code)
@inheritDoc
static newFromMessageGroup(MessageGroup $group, string $targetPattern='')
Constructs a FileBasedMessageGroup from any normal message group.
replaceVariables( $pattern, $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:60
Essentially random collection of helper functions, similar to GlobalFunctions.php.
Definition Utilities.php:29
This class implements some basic functions that wrap around the YAML message group configurations.
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).