Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
MicrosoftWebService.php
1<?php
2declare( strict_types = 1 );
3
4namespace MediaWiki\Extension\Translate\WebService;
5
6use MediaWiki\Http\HttpRequestFactory;
7
19 private $httpRequestFactory;
20
21 public function __construct(
22 HttpRequestFactory $httpRequestFactory,
23 string $serviceName,
24 array $config
25 ) {
26 parent::__construct( $serviceName, $config );
27 $this->httpRequestFactory = $httpRequestFactory;
28 }
29
31 public function getType(): string {
32 return 'mt';
33 }
34
36 protected function mapCode( string $code ): string {
37 $map = [
38 'tl' => 'fil',
39 'zh-hant' => 'zh-Hant',
40 'zh-hans' => 'zh-Hans',
41 'sr-ec' => 'sr-Cyrl',
42 'sr-el' => 'sr-Latn',
43 'pt-br' => 'pt',
44 ];
45
46 return $map[$code] ?? $code;
47 }
48
50 protected function doPairs(): array {
51 if ( !isset( $this->config['key'] ) ) {
52 throw new TranslationWebServiceConfigurationException( 'key is not set' );
53 }
54
55 $key = $this->config['key'];
56
57 $options = [];
58 $options['method'] = 'GET';
59 $options['timeout'] = $this->config['timeout'];
60
61 $url = $this->config['url'] . '/languages?api-version=3.0';
62
63 $req = $this->httpRequestFactory->create( $url, $options, __METHOD__ );
64 $req->setHeader( 'Ocp-Apim-Subscription-Key', $key );
65
66 $status = $req->execute();
67 if ( !$status->isOK() ) {
68 $error = $req->getContent();
69 // Most likely a timeout or other general error
71 'Http::get failed:' . serialize( $error ) . serialize( $status )
72 );
73 }
74
75 $json = $req->getContent();
76 $response = json_decode( $json, true );
77 if ( !isset( $response[ 'translation' ] ) ) {
78 throw new TranslationWebServiceException(
79 'Unable to fetch list of available languages: ' . $json
80 );
81 }
82
83 $languages = array_keys( $response[ 'translation' ] );
84
85 // Let's make a cartesian product, assuming we can translate from any language to any language
86 $pairs = [];
87 foreach ( $languages as $from ) {
88 foreach ( $languages as $to ) {
89 $pairs[$from][$to] = true;
90 }
91 }
92
93 return $pairs;
94 }
95
97 protected function getQuery( string $text, string $sourceLanguage, string $targetLanguage ): TranslationQuery {
98 if ( !isset( $this->config['key'] ) ) {
99 throw new TranslationWebServiceConfigurationException( 'key is not set' );
100 }
101
102 $key = $this->config['key'];
103 $text = trim( $text );
104 $text = $this->wrapUntranslatable( $text );
105
106 $url = $this->config['url'] . '/translate';
107 $params = [
108 'api-version' => '3.0',
109 'from' => $sourceLanguage,
110 'to' => $targetLanguage,
111 'textType' => 'html',
112 ];
113 $headers = [
114 'Ocp-Apim-Subscription-Key' => $key,
115 'Content-Type' => 'application/json',
116 ];
117 $body = json_encode( [ [ 'Text' => $text ] ] );
118
119 if ( $body === false ) {
120 throw new TranslationWebServiceInvalidInputException( 'Could not JSON encode source text' );
121 }
122
123 if ( strlen( $body ) > 5000 ) {
124 throw new TranslationWebServiceInvalidInputException( 'Source text too long' );
125 }
126
127 return TranslationQuery::factory( $url )
128 ->timeout( intval( $this->config['timeout'] ) )
129 ->queryParameters( $params )
130 ->queryHeaders( $headers )
131 ->postWithData( $body );
132 }
133
135 protected function parseResponse( TranslationQueryResponse $reply ): string {
136 $body = $reply->getBody();
137
138 $response = json_decode( $body, true );
139 if ( !isset( $response[ 0 ][ 'translations' ][ 0 ][ 'text' ] ) ) {
141 'Unable to parse translation response: ' . $body
142 );
143 }
144
145 $text = $response[ 0 ][ 'translations' ][ 0 ][ 'text' ];
146 $text = $this->unwrapUntranslatable( $text );
147
148 return $text;
149 }
150
152 protected function wrapUntranslatable( string $text ): string {
153 $pattern = '~%[^% ]+%|\$\d|{VAR:[^}]+}|{?{(PLURAL|GRAMMAR|GENDER):[^|]+\||%(\d\$)?[sd]~';
154 $wrap = '<span class="notranslate">\0</span>';
155 return preg_replace( $pattern, $wrap, $text );
156 }
157
159 protected function unwrapUntranslatable( string $text ): string {
160 $pattern = '~<span class="notranslate">\s*(.*?)\s*</span>~';
161 return preg_replace( $pattern, '\1', $text );
162 }
163}
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
Implements support for Microsoft translation api v3.
getQuery(string $text, string $sourceLanguage, string $targetLanguage)
@inheritDoc
parseResponse(TranslationQueryResponse $reply)
@inheritDoc
Value object that represents a HTTP(S) query response.
Mutable objects that represents a HTTP(S) query.
Used to signal that the requested input is rejected and cannot be used with an external web service.