Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
FileBasedMessageGroup.php
Go to the documentation of this file.
1<?php
15
25 public const NO_FFS_CLASS = 1;
26 public const INVALID_FFS_CLASS = 2;
27
28 protected $reverseCodeMap;
29
37 public static function newFromMessageGroup(
38 MessageGroup $group,
39 string $targetPattern = ''
40 ) {
41 $conf = [
42 'BASIC' => [
43 'class' => self::class,
44 'id' => $group->getId(),
45 'label' => $group->getLabel(),
46 'namespace' => $group->getNamespace(),
47 ],
48 'FILES' => [
49 'sourcePattern' => '',
50 'targetPattern' => $targetPattern,
51 ],
52 ];
53
54 $group = MessageGroupBase::factory( $conf );
55 if ( !$group instanceof self ) {
56 $actual = get_class( $group );
57 throw new DomainException( "Expected FileBasedMessageGroup, got $actual" );
58 }
59
60 return $group;
61 }
62
63 public function getFFS(): SimpleFFS {
64 $class = $this->getFromConf( 'FILES', 'class' );
65
66 if ( $class === null ) {
67 throw new RuntimeException( 'FFS class is not set.', self::NO_FFS_CLASS );
68 }
69
70 if ( !class_exists( $class ) ) {
71 throw new RuntimeException( "FFS class $class does not exist.", self::INVALID_FFS_CLASS );
72 }
73
74 return new $class( $this );
75 }
76
77 public function exists(): bool {
78 return $this->getMessageGroupCache( $this->getSourceLanguage() )->exists();
79 }
80
81 public function load( $code ) {
82 $ffs = $this->getFFS();
83 $data = $ffs->read( $code );
84
85 return $data ? $data['MESSAGES'] : [];
86 }
87
93 public function parseExternal( string $code ): array {
94 $supportedKeys = [ 'MESSAGES', 'AUTHORS', 'EXTRA' ];
95
96 $parsedData = $this->getFFS()->read( $code );
97
98 // Ensure we return correct keys
99 $data = [];
100 foreach ( $supportedKeys as $key ) {
101 $data[$key] = $parsedData[$key] ?? [];
102 }
103
104 return $data;
105 }
106
112 public function getSourceFilePath( $code ) {
113 if ( $this->isSourceLanguage( $code ) ) {
114 $pattern = $this->getFromConf( 'FILES', 'definitionFile' );
115 if ( $pattern !== null ) {
116 return $this->replaceVariables( $pattern, $code );
117 }
118 }
119
120 $pattern = $this->getFromConf( 'FILES', 'sourcePattern' );
121 if ( $pattern === null ) {
122 throw new MWException( 'No source file pattern defined.' );
123 }
124
125 return $this->replaceVariables( $pattern, $code );
126 }
127
128 public function getTargetFilename( $code ) {
129 // Check if targetPattern explicitly defined
130 $pattern = $this->getFromConf( 'FILES', 'targetPattern' );
131 if ( $pattern !== null ) {
132 return $this->replaceVariables( $pattern, $code );
133 }
134
135 // Check if definitionFile is explicitly defined
136 if ( $this->isSourceLanguage( $code ) ) {
137 $pattern = $this->getFromConf( 'FILES', 'definitionFile' );
138 }
139
140 // Fallback to sourcePattern which must be defined
141 if ( $pattern === null ) {
142 $pattern = $this->getFromConf( 'FILES', 'sourcePattern' );
143 }
144
145 if ( $pattern === null ) {
146 throw new MWException( 'No source file pattern defined.' );
147 }
148
149 // For exports, the scripts take output directory. We want to
150 // return a path where the prefix is current directory instead
151 // of full path of the source location.
152 $pattern = str_replace( '%GROUPROOT%', '.', $pattern );
153 return $this->replaceVariables( $pattern, $code );
154 }
155
162 public function replaceVariables( $pattern, $code ) {
163 global $IP, $wgTranslateGroupRoot;
164
165 $variables = [
166 '%CODE%' => $this->mapCode( $code ),
167 '%MWROOT%' => $IP,
168 '%GROUPROOT%' => $wgTranslateGroupRoot,
169 '%GROUPID%' => $this->getId(),
170 ];
171
172 return str_replace( array_keys( $variables ), array_values( $variables ), $pattern );
173 }
174
179 public function mapCode( $code ) {
180 if ( !isset( $this->conf['FILES']['codeMap'] ) ) {
181 return $code;
182 }
183
184 if ( isset( $this->conf['FILES']['codeMap'][$code] ) ) {
185 return $this->conf['FILES']['codeMap'][$code];
186 } else {
187 if ( !isset( $this->reverseCodeMap ) ) {
188 $this->reverseCodeMap = array_flip( $this->conf['FILES']['codeMap'] );
189 }
190
191 if ( isset( $this->reverseCodeMap[$code] ) ) {
192 return 'x-invalidLanguageCode';
193 }
194
195 return $code;
196 }
197 }
198
199 public static function getExtraSchema(): array {
200 $schema = [
201 'root' => [
202 '_type' => 'array',
203 '_children' => [
204 'FILES' => [
205 '_type' => 'array',
206 '_children' => [
207 'class' => [
208 '_type' => 'text',
209 '_not_empty' => true,
210 ],
211 'codeMap' => [
212 '_type' => 'array',
213 '_ignore_extra_keys' => true,
214 '_children' => [],
215 ],
216 'definitionFile' => [
217 '_type' => 'text',
218 ],
219 'sourcePattern' => [
220 '_type' => 'text',
221 '_not_empty' => true,
222 ],
223 'targetPattern' => [
224 '_type' => 'text',
225 ],
226 ]
227 ]
228 ]
229 ]
230 ];
231
232 return $schema;
233 }
234
236 public function getKeys() {
237 $cache = $this->getMessageGroupCache( $this->getSourceLanguage() );
238 if ( !$cache->exists() ) {
239 return array_keys( $this->getDefinitions() );
240 } else {
241 return $cache->getKeys();
242 }
243 }
244
246 public function initCollection( $code ) {
247 $namespace = $this->getNamespace();
248 $messages = [];
249
250 $cache = $this->getMessageGroupCache( $this->getSourceLanguage() );
251 if ( $cache->exists() ) {
252 foreach ( $cache->getKeys() as $key ) {
253 $messages[$key] = $cache->get( $key );
254 }
255 }
256
257 $definitions = new MessageDefinitions( $messages, $namespace );
258 $collection = MessageCollection::newFromDefinitions( $definitions, $code );
259 $this->setTags( $collection );
260
261 return $collection;
262 }
263
265 public function getMessage( $key, $code ) {
266 $cache = $this->getMessageGroupCache( $code );
267 if ( $cache->exists() ) {
268 $msg = $cache->get( $key );
269
270 if ( $msg !== false ) {
271 return $msg;
272 }
273
274 // Try harder
275 $nkey = str_replace( ' ', '_', strtolower( $key ) );
276 $keys = $cache->getKeys();
277
278 foreach ( $keys as $k ) {
279 if ( $nkey === str_replace( ' ', '_', strtolower( $k ) ) ) {
280 return $cache->get( $k );
281 }
282 }
283
284 return null;
285 } else {
286 return null;
287 }
288 }
289
290 public function getMessageGroupCache( string $code ): MessageGroupCache {
291 $cacheFilePath = Utilities::cacheFile(
292 "translate_groupcache-{$this->getId()}/{$code}.cdb"
293 );
294
295 return new MessageGroupCache( $this, $code, $cacheFilePath );
296 }
297}
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 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.
This file contains the class for core message collections implementation.
Wrapper for message definitions, just to beauty the code.
Essentially random collection of helper functions, similar to GlobalFunctions.php.
Definition Utilities.php:30
This class implements some basic functions that wrap around the YAML message group configurations.
static factory( $conf)
Caches messages of file based message group source file.
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).