Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 57 |
|
0.00% |
0 / 7 |
CRAP | |
0.00% |
0 / 1 |
GoogleTranslateWebService | |
0.00% |
0 / 57 |
|
0.00% |
0 / 7 |
272 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getType | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
mapCode | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
2 | |||
isSupportedLanguagePair | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
12 | |||
doPairs | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
30 | |||
getQuery | |
0.00% |
0 / 16 |
|
0.00% |
0 / 1 |
12 | |||
parseResponse | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
6 |
1 | <?php |
2 | declare( strict_types = 1 ); |
3 | |
4 | namespace MediaWiki\Extension\Translate\WebService; |
5 | |
6 | use MediaWiki\Http\HttpRequestFactory; |
7 | use MediaWiki\Json\FormatJson; |
8 | use MediaWiki\Parser\Sanitizer; |
9 | |
10 | /** |
11 | * Implements support for Google Translate API |
12 | * @author Carsten Schmitz / LimeSurvey GmbH |
13 | * @license GPL-2.0-or-later |
14 | * @since 2020.05 |
15 | * @ingroup TranslationWebService |
16 | * @see https://cloud.google.com/translate/docs/reference/rest |
17 | */ |
18 | class GoogleTranslateWebService extends TranslationWebService { |
19 | private const PUBLIC_API = 'https://translation.googleapis.com/language/translate/v2'; |
20 | private HttpRequestFactory $httpRequestFactory; |
21 | |
22 | public function __construct( |
23 | HttpRequestFactory $httpRequestFactory, |
24 | string $serviceName, |
25 | array $config |
26 | ) { |
27 | parent::__construct( $serviceName, $config ); |
28 | $this->httpRequestFactory = $httpRequestFactory; |
29 | } |
30 | |
31 | /** @inheritDoc */ |
32 | public function getType(): string { |
33 | return 'mt'; |
34 | } |
35 | |
36 | /** @inheritDoc */ |
37 | protected function mapCode( string $code ): string { |
38 | /** @phpcs-require-sorted-array */ |
39 | $map = [ |
40 | 'be-tarask' => 'be', |
41 | 'nb' => 'no', |
42 | 'tw' => 'ak', |
43 | 'zh-cn' => 'zh-CN', |
44 | 'zh-hans' => 'zh-CN', |
45 | 'zh-hant' => 'zh-TW', |
46 | 'zh-tw' => 'zh-TW', |
47 | ]; |
48 | |
49 | return $map[$code] ?? $code; |
50 | } |
51 | |
52 | /** @inheritDoc */ |
53 | public function isSupportedLanguagePair( string $sourceLanguage, string $targetLanguage ): bool { |
54 | $pairs = $this->getSupportedLanguagePairs(); |
55 | $from = $this->mapCode( $sourceLanguage ); |
56 | $to = $this->mapCode( $targetLanguage ); |
57 | |
58 | // As long as the source & target language exist at Google it is fine |
59 | return isset( $pairs[$from] ) && isset( $pairs[$to] ) && $from !== $to; |
60 | } |
61 | |
62 | /** @inheritDoc */ |
63 | protected function doPairs(): array { |
64 | if ( !isset( $this->config['key'] ) ) { |
65 | throw new TranslationWebServiceConfigurationException( 'API key is not set' ); |
66 | } |
67 | |
68 | $api = $this->config['pairs'] ?? self::PUBLIC_API . '/languages'; |
69 | $json = $this->httpRequestFactory->get( |
70 | wfAppendQuery( $api, [ 'key' => $this->config['key'], ] ), |
71 | [ 'timeout' => $this->config['timeout'] ], |
72 | __METHOD__ |
73 | ); |
74 | if ( $json === null ) { |
75 | throw new TranslationWebServiceException( 'Failure encountered when contacting remote server' ); |
76 | } |
77 | |
78 | $response = FormatJson::decode( $json ); |
79 | if ( !is_object( $response ) ) { |
80 | throw new TranslationWebServiceException( 'Malformed reply from remote server: ' . $json ); |
81 | } |
82 | |
83 | $pairs = []; |
84 | foreach ( $response->data->languages as $language ) { |
85 | // Google can translate from any language to any language |
86 | $pairs[$language->language] = true; |
87 | } |
88 | |
89 | return $pairs; |
90 | } |
91 | |
92 | /** @inheritDoc */ |
93 | protected function getQuery( string $text, string $sourceLanguage, string $targetLanguage ): TranslationQuery { |
94 | if ( !isset( $this->config['key'] ) ) { |
95 | throw new TranslationWebServiceConfigurationException( 'API key is not set' ); |
96 | } |
97 | # https://cloud.google.com/translate/docs/reference/translate |
98 | if ( strlen( $text ) > 10000 ) { |
99 | // There is no limitation but we don't want the translation service to be abused, don't we? |
100 | throw new TranslationWebServiceInvalidInputException( 'Source text too long' ); |
101 | } |
102 | |
103 | $url = $this->config['url'] ?? self::PUBLIC_API; |
104 | $text = trim( $text ); |
105 | $text = $this->wrapUntranslatable( $text ); |
106 | |
107 | return TranslationQuery::factory( $url ) |
108 | ->timeout( intval( $this->config['timeout'] ?? 3 ) ) |
109 | ->postWithData( wfArrayToCgi( [ |
110 | 'key' => $this->config['key'], |
111 | 'q' => $text, |
112 | 'target' => $targetLanguage, |
113 | 'source' => $sourceLanguage, |
114 | 'format' => 'html', |
115 | ] ) ); |
116 | } |
117 | |
118 | /** @inheritDoc */ |
119 | protected function parseResponse( TranslationQueryResponse $response ): string { |
120 | $body = $response->getBody(); |
121 | $responseBody = FormatJson::decode( $body ); |
122 | if ( !is_object( $responseBody ) ) { |
123 | throw new TranslationWebServiceException( 'Invalid json: ' . serialize( $body ) ); |
124 | } |
125 | $text = Sanitizer::decodeCharReferences( $responseBody->data->translations[0]->translatedText ); |
126 | $text = $this->unwrapUntranslatable( $text ); |
127 | |
128 | return trim( $text ); |
129 | } |
130 | } |