Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
FileBasedMessageGroup.php
Go to the documentation of this file.
1<?php
17
27 public const NO_FILE_FORMAT = 1;
28
29 protected $reverseCodeMap;
30
38 public static function newFromMessageGroup(
39 MessageGroup $group,
40 string $targetPattern = ''
41 ) {
42 $conf = [
43 'BASIC' => [
44 'class' => self::class,
45 'id' => $group->getId(),
46 'label' => $group->getLabel(),
47 'namespace' => $group->getNamespace(),
48 ],
49 'FILES' => [
50 'sourcePattern' => '',
51 'targetPattern' => $targetPattern,
52 ],
53 ];
54
55 $group = MessageGroupBase::factory( $conf );
56 if ( !$group instanceof self ) {
57 $actual = get_class( $group );
58 throw new DomainException( "Expected FileBasedMessageGroup, got $actual" );
59 }
60
61 return $group;
62 }
63
64 public function getFFS(): SimpleFormat {
65 $class = $this->getFromConf( 'FILES', 'class' );
66 $format = $this->getFromConf( 'FILES', 'format' );
67
68 if ( $format !== null ) {
69 return Services::getInstance()->getFileFormatFactory()->create( $format, $this );
70 } elseif ( $class !== null ) {
71 return Services::getInstance()->getFileFormatFactory()->loadInstance( $class, $this );
72 } else {
73 throw new RuntimeException(
74 'FileFormatSupport class/format is not set for "' . $this->getId() . '".',
75 self::NO_FILE_FORMAT
76 );
77 }
78 }
79
80 public function exists(): bool {
81 return $this->getMessageGroupCache( $this->getSourceLanguage() )->exists();
82 }
83
84 public function load( $code ) {
85 $ffs = $this->getFFS();
86 $data = $ffs->read( $code );
87
88 return $data ? $data['MESSAGES'] : [];
89 }
90
96 public function parseExternal( string $code ): array {
97 $supportedKeys = [ 'MESSAGES', 'AUTHORS', 'EXTRA' ];
98
99 $parsedData = $this->getFFS()->read( $code );
100
101 // Ensure we return correct keys
102 $data = [];
103 foreach ( $supportedKeys as $key ) {
104 $data[$key] = $parsedData[$key] ?? [];
105 }
106
107 return $data;
108 }
109
114 public function getSourceFilePath( $code ) {
115 if ( $this->isSourceLanguage( $code ) ) {
116 $pattern = $this->getFromConf( 'FILES', 'definitionFile' );
117 if ( $pattern !== null ) {
118 return $this->replaceVariables( $pattern, $code );
119 }
120 }
121
122 $pattern = $this->getFromConf( 'FILES', 'sourcePattern' );
123 if ( $pattern === null ) {
124 throw new RuntimeException( 'No source file pattern defined.' );
125 }
126
127 return $this->replaceVariables( $pattern, $code );
128 }
129
130 public function getTargetFilename( $code ) {
131 // Check if targetPattern explicitly defined
132 $pattern = $this->getFromConf( 'FILES', 'targetPattern' );
133 if ( $pattern !== null ) {
134 return $this->replaceVariables( $pattern, $code );
135 }
136
137 // Check if definitionFile is explicitly defined
138 if ( $this->isSourceLanguage( $code ) ) {
139 $pattern = $this->getFromConf( 'FILES', 'definitionFile' );
140 }
141
142 // Fallback to sourcePattern which must be defined
143 if ( $pattern === null ) {
144 $pattern = $this->getFromConf( 'FILES', 'sourcePattern' );
145 }
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 ( !isset( $this->reverseCodeMap ) ) {
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: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: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:HookRunner'=> static function(MediaWikiServices $services):HookRunner { return new HookRunner( $services->getHookContainer());}, 'Translate:MessageBundleStore'=> static function(MediaWikiServices $services):MessageBundleStore { return new MessageBundleStore($services->get( 'Translate:RevTagStore'), $services->getJobQueueGroup(), $services->getLanguageNameUtils(), $services->get( 'Translate:MessageIndex'));}, '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->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:RevTagStore'=> static function(MediaWikiServices $services):RevTagStore { return new RevTagStore($services->getDBLoadBalancerFactory());}, 'Translate:SubpageListBuilder'=> static function(MediaWikiServices $services):SubpageListBuilder { return new SubpageListBuilder($services->get( 'Translate:TranslatableBundleFactory'), $services->getLinkBatchFactory());}, '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());}, '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(), $services->get( 'Translate:RevTagStore'), $services->getDBLoadBalancer(), $services->get( 'Translate:TranslatableBundleStatusStore'), $services->get( 'Translate:TranslatablePageParser'),);}, '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...
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:44
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)
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).