Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
MessageHandle.php
1<?php
2declare( strict_types = 1 );
3
4namespace MediaWiki\Extension\Translate\MessageLoading;
5
6use BadMethodCallException;
7use Language;
11use MediaWiki\Linker\LinkTarget;
12use MediaWiki\Logger\LoggerFactory;
13use MediaWiki\MediaWikiServices;
14use MediaWiki\Title\Title;
15use MessageGroup;
16
25 private LinkTarget $title;
26 private ?string $key = null;
27 private ?string $languageCode = null;
29 private ?array $groupIds = null;
30 private MessageIndex $messageIndex;
31
32 public function __construct( LinkTarget $title ) {
33 $this->title = $title;
34 $this->messageIndex = Services::getInstance()->getMessageIndex();
35 }
36
38 public function isMessageNamespace(): bool {
39 global $wgTranslateMessageNamespaces;
40 $namespace = $this->title->getNamespace();
41
42 return in_array( $namespace, $wgTranslateMessageNamespaces );
43 }
44
49 public function figureMessage(): array {
50 if ( $this->key === null ) {
51 // Check if this is a valid message first
52 $this->key = $this->title->getDBkey();
53 $known = $this->messageIndex->getGroupIds( $this ) !== [];
54
55 $pos = strrpos( $this->key, '/' );
56 if ( $known || $pos === false ) {
57 $this->languageCode = '';
58 } else {
59 // For keys like Foo/, substr returns false instead of ''
60 $this->languageCode = (string)( substr( $this->key, $pos + 1 ) );
61 $this->key = substr( $this->key, 0, $pos );
62 }
63 }
64
65 return [ $this->key, $this->languageCode ];
66 }
67
69 public function getKey(): string {
70 $this->figureMessage();
71
72 return $this->key;
73 }
74
79 public function getCode(): string {
80 $this->figureMessage();
81
82 return $this->languageCode;
83 }
84
89 public function getEffectiveLanguage(): Language {
90 $code = $this->getCode();
91 $mwServices = MediaWikiServices::getInstance();
92 if ( !$mwServices->getLanguageNameUtils()->isKnownLanguageTag( $code ) ||
93 $this->isDoc()
94 ) {
95 return $mwServices->getContentLanguage();
96 }
97
98 return $mwServices->getLanguageFactory()->getLanguage( $code );
99 }
100
102 public function isDoc(): bool {
103 global $wgTranslateDocumentationLanguageCode;
104
105 return $this->getCode() === $wgTranslateDocumentationLanguageCode;
106 }
107
112 public function isPageTranslation(): bool {
113 return $this->title->inNamespace( NS_TRANSLATIONS );
114 }
115
123 public function getGroupIds() {
124 if ( $this->groupIds === null ) {
125 $this->groupIds = $this->messageIndex->getGroupIds( $this );
126 }
127
128 return $this->groupIds;
129 }
130
135 public function getGroup(): ?MessageGroup {
136 $ids = $this->getGroupIds();
137 if ( !isset( $ids[0] ) ) {
138 throw new BadMethodCallException( 'called before isValid' );
139 }
140 return MessageGroups::getGroup( $ids[0] );
141 }
142
144 public function isValid(): bool {
145 static $jobHasBeenScheduled = false;
146
147 if ( !$this->isMessageNamespace() ) {
148 return false;
149 }
150
151 $groups = $this->getGroupIds();
152 if ( !$groups ) {
153 return false;
154 }
155
156 // Do another check that the group actually exists
157 $group = $this->getGroup();
158 if ( !$group ) {
159 $logger = LoggerFactory::getInstance( 'Translate' );
160 $logger->warning(
161 '[MessageHandle] MessageIndex is out of date. Page {pagename} refers to ' .
162 'unknown group {messagegroup}',
163 [
164 'pagename' => $this->getTitle()->getPrefixedText(),
165 'messagegroup' => $groups[0],
166 ]
167 );
168
169 if ( !$jobHasBeenScheduled ) {
170 // Schedule a job in the job queue (with deduplication)
171 $job = RebuildMessageIndexJob::newJob( __METHOD__ );
172 MediaWikiServices::getInstance()->getJobQueueGroup()->lazyPush( $job );
173 $jobHasBeenScheduled = true;
174 }
175
176 return false;
177 }
178
179 return true;
180 }
181
183 public function getTitle(): Title {
184 return Title::newFromLinkTarget( $this->title );
185 }
186
188 public function getTitleForLanguage( string $languageCode ): Title {
189 return Title::makeTitle(
190 $this->title->getNamespace(),
191 $this->getKey() . "/$languageCode"
192 );
193 }
194
196 public function getTitleForBase(): Title {
197 return Title::makeTitle(
198 $this->title->getNamespace(),
199 $this->getKey()
200 );
201 }
202
208 public static function hasFuzzyString( string $text ): bool {
209 return str_contains( $text, TRANSLATE_FUZZY );
210 }
211
213 public static function makeFuzzyString( string $text ): string {
214 return self::hasFuzzyString( $text ) ? $text : TRANSLATE_FUZZY . $text;
215 }
216
218 public function isFuzzy(): bool {
219 $dbr = MediaWikiServices::getInstance()->getDBLoadBalancer()->getConnection( DB_REPLICA );
220
221 $res = $dbr->newSelectQueryBuilder()
222 ->select( 'rt_type' )
223 ->from( 'page' )
224 ->join( 'revtag', null, [
225 'page_id=rt_page',
226 'page_latest=rt_revision',
227 'rt_type' => RevTagStore::FUZZY_TAG,
228 ] )
229 ->where( [
230 'page_namespace' => $this->title->getNamespace(),
231 'page_title' => $this->title->getDBkey(),
232 ] )
233 ->caller( __METHOD__ )
234 ->fetchField();
235
236 return $res !== false;
237 }
238
244 public function getInternalKey(): string {
245 $mwServices = MediaWikiServices::getInstance();
246 $nsInfo = $mwServices->getNamespaceInfo();
247 $contentLanguage = $mwServices->getContentLanguage();
248
249 $key = $this->getKey();
250 $group = $this->getGroup();
251 $groupKeys = $group->getKeys();
252
253 if ( in_array( $key, $groupKeys, true ) ) {
254 return $key;
255 }
256
257 $namespace = $this->title->getNamespace();
258 if ( $nsInfo->isCapitalized( $namespace ) ) {
259 $lowercaseKey = $contentLanguage->lcfirst( $key );
260 if ( in_array( $lowercaseKey, $groupKeys, true ) ) {
261 return $lowercaseKey;
262 }
263 }
264
265 // Brute force all the keys to find the one. This one should always find a match
266 // if there is one.
267 foreach ( $groupKeys as $haystackKey ) {
268 $normalizedHaystackKey = Title::makeTitleSafe( $namespace, $haystackKey )->getDBkey();
269 if ( $normalizedHaystackKey === $key ) {
270 return $haystackKey;
271 }
272 }
273
274 return "BUG:$key";
275 }
276}
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: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( 'Translate.GroupSynchronization'), $services->get( 'Translate:MessageIndex'), $services->getTitleFactory(), new ServiceOptions(ExternalMessageSourceStateImporter::CONSTRUCTOR_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:HookRunner'=> static function(MediaWikiServices $services):HookRunner { return new HookRunner( $services->getHookContainer());}, 'Translate:MessageBundleMessageGroupFactory'=> static function(MediaWikiServices $services):MessageBundleMessageGroupFactory { return new MessageBundleMessageGroupFactory($services->getDBLoadBalancerFactory() ->getPrimaryDatabase(), $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:MessageGroupMetadata'=> static function(MediaWikiServices $services):MessageGroupMetadata { return new MessageGroupMetadata( $services->getDBLoadBalancer());}, '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->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( 'Translate.MessageGroupSubscription'), new ServiceOptions(MessageGroupSubscription::CONSTRUCTOR_OPTIONS, $services->getMainConfig()));}, 'Translate:MessageGroupSubscriptionHookHandler'=> static function(MediaWikiServices $services):MessageGroupSubscriptionHookHandler { return new MessageGroupSubscriptionHookHandler($services->get( 'Translate:MessageGroupSubscription'), $services->getUserFactory());}, 'Translate:MessageGroupSubscriptionStore'=> static function(MediaWikiServices $services):MessageGroupSubscriptionStore { return new MessageGroupSubscriptionStore( $services->getDBLoadBalancerFactory());}, 'Translate:MessageIndex'=> static function(MediaWikiServices $services):MessageIndex { $params=$services->getMainConfig() ->get( 'TranslateMessageIndex');if(is_string( $params)) { $params=(array) $params;} $class=array_shift( $params);if(!class_exists( $class)) { $class="MediaWiki\\Extension\\Translate\\MessageLoading\\$class";} 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'), $services->get( 'Translate:MessageGroupMetadata'));}, 'Translate:RevTagStore'=> static function(MediaWikiServices $services):RevTagStore { return new RevTagStore( $services->getDBLoadBalancer());}, '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->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(), $services->getNamespaceInfo(), $services->getTitleFactory());}, '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:TranslatablePageMarker'=> static function(MediaWikiServices $services):TranslatablePageMarker { return new TranslatablePageMarker($services->getDBLoadBalancer(), $services->getJobQueueGroup(), $services->getLanguageNameUtils(), $services->getLinkRenderer(), MessageGroups::singleton(), $services->get( 'Translate:MessageIndex'), $services->getTitleFormatter(), $services->getTitleParser(), $services->get( 'Translate:TranslatablePageParser'), $services->get( 'Translate:TranslatablePageStore'), $services->get( 'Translate:TranslationUnitStoreFactory'), $services->get( 'Translate:MessageGroupMetadata'), $services->getWikiPageFactory());}, 'Translate:TranslatablePageMessageGroupFactory'=> static function(MediaWikiServices $services):TranslatablePageMessageGroupFactory { return new TranslatablePageMessageGroupFactory(new ServiceOptions(TranslatablePageMessageGroupFactory::SERVICE_OPTIONS, $services->getMainConfig()), $services->getDBLoadBalancerFactory() ->getPrimaryDatabase());}, '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'), $services->get( 'Translate:MessageGroupMetadata'));}, 'Translate:TranslateSandbox'=> static function(MediaWikiServices $services):TranslateSandbox { return new TranslateSandbox($services->getUserFactory(), $services->getDBLoadBalancer(), $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 { $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
Factory class for accessing message groups individually by id or all of them as a list.
Class to manage revision tags for translatable bundles.
Class for pointing to messages, like Title class is for titles.
static makeFuzzyString(string $text)
Check if a string has fuzzy string and if not, add it.
getTitleForLanguage(string $languageCode)
Get the original title with the passed language code.
getKey()
Returns the identified or guessed message key.
getEffectiveLanguage()
Return the Language object for the assumed language of the content, which might be different from the...
isPageTranslation()
Determine whether the current handle is for page translation feature.
getGroup()
Get the primary MessageGroup this message belongs to.
getInternalKey()
This returns the key that can be used for showMessage parameter for Special:Translate for regular mes...
isMessageNamespace()
Check if this handle is in a message namespace.
static hasFuzzyString(string $text)
Check if a string contains the fuzzy string.
getGroupIds()
Returns all message group ids this message belongs to.
figureMessage()
Recommended to use getCode and getKey instead.
isValid()
Checks if the handle corresponds to a known message.
isDoc()
Determine whether the current handle is for message documentation.
Creates a database of keys in all groups, so that namespace and key can be used to get the groups the...
Minimal service container.
Definition Services.php:52
Interface for message groups.