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