Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
MessageGroupConfigurationParser.php
1<?php
2declare( strict_types = 1 );
3
4namespace MediaWiki\Extension\Translate\MessageGroupConfiguration;
5
7use Exception;
11use RomaricDrigon\MetaYaml\MetaYaml;
13
20 private ?array $baseSchema = null;
21 private FileFormatFactory $fileFormatFactory;
22
23 public function __construct() {
24 // Don't perform validations if library not available
25 if ( class_exists( MetaYaml::class ) ) {
26 $this->baseSchema = $this->getBaseSchema();
27 }
28
29 $this->fileFormatFactory = Services::getInstance()->getFileFormatFactory();
30 }
31
41 public function getHopefullyValidConfigurations( string $data, ?callable $callback = null ): array {
42 if ( !is_callable( $callback ) ) {
43 $callback = static function ( $unused1, $unused2, $unused3 ) {
44 /*noop*/
45 };
46 }
47
48 $documents = self::getDocumentsFromYaml( $data );
49 $configurations = self::parseDocuments( $documents );
50 $groups = [];
51
52 if ( is_array( $this->baseSchema ) ) {
53 foreach ( $configurations as $index => $config ) {
54 try {
55 $this->validate( $config );
56 $groups[$config['BASIC']['id']] = $config;
57 } catch ( Exception $e ) {
58 $callback( $index, $config, $e->getMessage() );
59 }
60 }
61 } else {
62 foreach ( $configurations as $index => $config ) {
63 if ( isset( $config['BASIC']['id'] ) ) {
64 $groups[$config['BASIC']['id']] = $config;
65 } else {
66 $callback( $index, $config, 'id is missing' );
67 }
68 }
69 }
70
71 return $groups;
72 }
73
78 public function getDocumentsFromYaml( string $data ): array {
79 return preg_split( "/^---$/m", $data, -1, PREG_SPLIT_NO_EMPTY );
80 }
81
88 public function parseDocuments( array $documents ): array {
89 $groups = [];
90 $template = [];
91
92 foreach ( $documents as $document ) {
93 $document = TranslateYaml::loadString( $document );
94
95 if ( isset( $document['TEMPLATE'] ) ) {
96 $template = $document['TEMPLATE'];
97 } else {
98 $groups[] = $document;
99 }
100 }
101
102 if ( $template ) {
103 foreach ( $groups as $i => $group ) {
104 $groups[$i] = self::mergeTemplate( $template, $group );
105 // Little hack to allow aggregate groups to be defined in same file with other groups.
106 if ( $groups[$i]['BASIC']['class'] === AggregateMessageGroup::class ) {
107 unset( $groups[$i]['FILES'] );
108 }
109 }
110 }
111
112 return $groups;
113 }
114
115 public function getBaseSchema(): array {
116 return TranslateYaml::load( __DIR__ . '/../../data/group-yaml-schema.yaml' );
117 }
118
123 public function validate( array $config ): void {
124 $schema = $this->baseSchema;
125
126 foreach ( $config as $key => $section ) {
127 $extra = [];
128 if ( $key === 'FILES' ) {
129 $extra = $this->getFilesSchemaExtra( $section );
130 } elseif ( $key === 'MANGLER' ) {
131 $class = $section[ 'class' ] ?? null;
132 // FIXME: UGLY HACK: StringMatcher is now under a namespace so use the fully prefixed
133 // class to check if it has the getExtraSchema method
134 if ( $class === 'StringMatcher' ) {
135 $extra = StringMatcher::getExtraSchema();
136 }
137 } else {
138 $extra = $this->callGetExtraSchema( $section[ 'class' ] ?? null );
139 }
140
141 $schema = array_replace_recursive( $schema, $extra );
142 }
143
144 $schema = new MetaYaml( $schema );
145 $schema->validate( $config );
146 }
147
149 public static function mergeTemplate( array $base, array $specific ): array {
150 foreach ( $specific as $key => $value ) {
151 if ( is_array( $value ) && isset( $base[$key] ) && is_array( $base[$key] ) ) {
152 $base[$key] = self::mergeTemplate( $base[$key], $value );
153 } else {
154 $base[$key] = $value;
155 }
156 }
157
158 return $base;
159 }
160
161 private function getFilesSchemaExtra( array $section ): array {
162 $class = $section['class'] ?? null;
163 $format = $section['format'] ?? null;
164 $className = null;
165
166 if ( $format ) {
167 $className = $this->fileFormatFactory->getClassname( $format );
168 } elseif ( $class ) {
169 $className = $class;
170 }
171
172 return $this->callGetExtraSchema( $className );
173 }
174
175 private function callGetExtraSchema( ?string $className ): array {
176 if ( $className && is_callable( [ $className, 'getExtraSchema' ] ) ) {
177 return call_user_func( [ $className, 'getExtraSchema' ] );
178 }
179
180 return [];
181 }
182}
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
Groups multiple message groups together as one group.
A factory class used to instantiate instances of pre-provided File formats.
getDocumentsFromYaml(string $data)
Given a Yaml string, returns the non-empty documents as an array.
static mergeTemplate(array $base, array $specific)
Merges a document template (base) to actual definition (specific)
getHopefullyValidConfigurations(string $data, ?callable $callback=null)
Easy to use function to get valid group configurations from YAML.
The versatile default implementation of StringMangler interface.
Minimal service container.
Definition Services.php:44
This class is a wrapper class to provide interface to parse and generate YAML files with phpyaml or s...