2declare( strict_types = 1 );
4namespace MediaWiki\Extension\Translate\WebService;
7use MediaWiki\Logger\LoggerFactory;
8use MediaWiki\MediaWikiServices;
10use Psr\Log\LoggerAwareInterface;
11use Psr\Log\LoggerInterface;
35 'deps' => [
'HttpRequestFactory' ]
39 'deps' => [
'HttpRequestFactory' ]
43 'deps' => [
'HttpRequestFactory' ]
47 'deps' => [
'HttpRequestFactory' ]
49 'remote-ttmserver' => [
54 'deps' => [
'HttpRequestFactory' ]
58 'deps' => [
'HttpRequestFactory' ]
65 if ( !isset( $config[
'timeout'] ) ) {
66 $config[
'timeout'] = 3;
69 $serviceDetails = $handlers[$config[
'type']] ??
null;
70 if ( $serviceDetails ) {
71 $objectFactory = MediaWikiServices::getInstance()->getObjectFactory();
73 'class' => $serviceDetails[
'class'],
74 'args' => [ $serviceName, $config ],
75 'services' => $serviceDetails[
'deps'] ?? [],
79 $serviceObject = $objectFactory->createObject( $spec );
80 if ( $serviceObject instanceof LoggerAwareInterface ) {
81 $serviceObject->setLogger( LoggerFactory::getInstance(
'translationservices' ) );
84 return $serviceObject;
105 public function getQueries(
string $text,
string $sourceLanguage,
string $targetLanguage ): array {
106 $from = $this->mapCode( $sourceLanguage );
107 $to = $this->mapCode( $targetLanguage );
110 return [ $this->getQuery( $text, $from, $to ) ];
112 $this->reportTranslationServiceFailure( $e->getMessage() );
114 }
catch ( TranslationWebServiceInvalidInputException $e ) {
126 if ( $response->getStatusCode() !== 200 ) {
127 $this->reportTranslationServiceFailure(
128 'STATUS: ' . $response->getStatusMessage() .
"\n" .
129 'BODY: ' . $response->getBody()
135 return $this->parseResponse( $response );
137 $this->reportTranslationServiceFailure( $e->getMessage() );
154 abstract protected function mapCode(
string $code ): string;
177 string $text,
string $sourceLanguage,
string $targetLanguage
196 public function __construct(
string $service, array $config ) {
197 $this->service = $service;
198 $this->config = $config;
207 $pairs = $this->getSupportedLanguagePairs();
208 $from = $this->mapCode( $sourceLanguage );
209 $to = $this->mapCode( $targetLanguage );
211 return isset( $pairs[$from][$to] );
219 $cache = ObjectCache::getInstance( CACHE_ANYTHING );
221 return $cache->getWithSetCallback(
222 $cache->makeKey(
'translate-tmsug-pairs-' . $this->service ),
224 function ( &$ttl ) use ( $cache ) {
226 $pairs = $this->doPairs();
227 }
catch ( Exception $e ) {
229 $this->reportTranslationServiceFailure( $e->getMessage() );
230 $ttl = $cache::TTL_UNCACHEABLE;
244 $text = str_replace(
"\n",
'!N!', $text );
245 $pattern =
'~%[^% ]+%|\$\d|{VAR:[^}]+}|{?{(PLURAL|GRAMMAR|GENDER):[^|]+\||%(\d\$)?[sd]~';
246 $wrap =
'<span class="notranslate" translate="no">\0</span>';
247 return preg_replace( $pattern, $wrap, $text );
252 $text = str_replace(
'!N!',
"\n", $text );
253 $pattern =
'~<span class="notranslate" translate="no">(.*?)</span>~';
254 return preg_replace( $pattern,
'\1', $text );
259 public function setLogger( LoggerInterface $logger ): void {
260 $this->logger = $logger;
267 protected $serviceFailureCount = 5;
272 protected $serviceFailurePeriod = 900;
276 $service = $this->service;
277 $cache = ObjectCache::getInstance( CACHE_ANYTHING );
279 $key = $cache->makeKey(
"translate-service-$service" );
280 $value = $cache->get( $key );
281 if ( !is_string( $value ) ) {
285 list( $count, $failed ) = explode(
'|', $value, 2 );
287 if ( $failed + ( 2 * $this->serviceFailurePeriod ) < wfTimestamp() ) {
288 if ( $count >= $this->serviceFailureCount ) {
289 $this->logger->warning(
"Translation service $service (was) restored" );
291 $cache->delete( $key );
294 } elseif ( $failed + $this->serviceFailurePeriod < wfTimestamp() ) {
303 return $count >= $this->serviceFailureCount;
308 $service = $this->service;
309 $this->logger->warning(
"Translation service $service problem: $msg" );
311 $cache = ObjectCache::getInstance( CACHE_ANYTHING );
312 $key = $cache->makeKey(
"translate-service-$service" );
314 $value = $cache->get( $key );
315 if ( !is_string( $value ) ) {
318 list( $count, ) = explode(
'|', $value, 2 );
322 $failed = wfTimestamp();
326 $this->serviceFailurePeriod * 5
329 if ( $count === $this->serviceFailureCount ) {
330 $this->logger->error(
"Translation service $service suspended" );
331 } elseif ( $count > $this->serviceFailureCount ) {
332 $this->logger->warning(
"Translation service $service still suspended" );
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