Skip to content

Commit 46f5eb1

Browse files
UnGastdamencho
authored andcommitted
feat(transcription): add Microsoft Translator service
1 parent b4174dc commit 46f5eb1

3 files changed

Lines changed: 265 additions & 0 deletions

File tree

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,27 @@ before starting to listen to requests. You may refer to the
263263
[documentation](https://github.com/LibreTranslate/LibreTranslate/blob/main/README.md)
264264
to set up a volume or set the available languages to reduce download time.
265265

266+
Microsoft Translator configuration for translation
267+
==================
268+
269+
To use [Microsoft Translator](https://learn.microsoft.com/azure/ai-services/translator/)
270+
for translation, configure the following properties in `/etc/jitsi/jigasi/sip-communicator.properties`:
271+
272+
```
273+
org.jitsi.jigasi.transcription.translationService=org.jitsi.jigasi.transcription.BingTranslationService
274+
org.jitsi.jigasi.transcription.bing.subscription_key=<your-translator-key>
275+
org.jitsi.jigasi.transcription.bing.subscription_region=<your-resource-region>
276+
```
277+
278+
The default endpoint is `https://api.cognitive.microsofttranslator.com` and
279+
the default API version is `3.0`. Override them only when using a custom
280+
Translator endpoint or API version:
281+
282+
```
283+
org.jitsi.jigasi.transcription.bing.endpoint=https://api.cognitive.microsofttranslator.com
284+
org.jitsi.jigasi.transcription.bing.api_version=3.0
285+
```
286+
266287
Transcription options
267288
=====================
268289

jigasi-home/sip-communicator.properties

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,13 @@ org.jitsi.jigasi.xmpp.acc.USE_DEFAULT_STUN_SERVER=false
239239
# org.jitsi.jigasi.transcription.translationService=org.jitsi.jigasi.transcription.LibreTranslateTranslationService
240240
# org.jitsi.jigasi.transcription.libreTranslate.api_url=http://localhost:5000/translate
241241

242+
# Bing (Microsoft Translator Text API v3) server
243+
# org.jitsi.jigasi.transcription.translationService=org.jitsi.jigasi.transcription.BingTranslationService
244+
# org.jitsi.jigasi.transcription.bing.subscription_key=
245+
# org.jitsi.jigasi.transcription.bing.subscription_region=
246+
# org.jitsi.jigasi.transcription.bing.endpoint=https://api.cognitive.microsofttranslator.com
247+
# org.jitsi.jigasi.transcription.bing.api_version=3.0
248+
242249
# translation
243250
# org.jitsi.jigasi.transcription.ENABLE_TRANSLATION=false
244251

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
/*
2+
* Jigasi, the JItsi GAteway to SIP.
3+
*
4+
* Copyright @ 2018 - present 8x8, Inc.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.jitsi.jigasi.transcription;
19+
20+
import com.google.gson.Gson;
21+
import com.google.gson.GsonBuilder;
22+
import com.google.gson.JsonArray;
23+
import com.google.gson.JsonObject;
24+
import org.apache.http.HttpResponse;
25+
import org.apache.http.client.methods.HttpPost;
26+
import org.apache.http.entity.ContentType;
27+
import org.apache.http.entity.StringEntity;
28+
import org.apache.http.impl.client.CloseableHttpClient;
29+
import org.apache.http.impl.client.HttpClientBuilder;
30+
import org.apache.http.util.EntityUtils;
31+
import org.jitsi.jigasi.JigasiBundleActivator;
32+
import org.jitsi.utils.logging.Logger;
33+
34+
import java.io.IOException;
35+
import java.util.List;
36+
37+
38+
/**
39+
* Implements a {@link TranslationService} which uses the Microsoft Translator
40+
* Text API (v3) to translate a given text from one language to another.
41+
* <p>
42+
* The service requires a valid Azure Cognitive Services subscription key,
43+
* passed through the configuration property
44+
* {@code org.jitsi.jigasi.transcription.bing.subscription_key}. For
45+
* multi-service or regional resources the region must additionally be set via
46+
* {@code org.jitsi.jigasi.transcription.bing.subscription_region}.
47+
* <p>
48+
* <a href="https://learn.microsoft.com/azure/ai-services/translator/reference/v3-0-translate">
49+
* Microsoft Translator v3 reference</a> for request / response details.
50+
*/
51+
public class BingTranslationService
52+
implements TranslationService
53+
{
54+
/**
55+
* Property name for the Azure subscription key used to authenticate
56+
* against the Translator API. Required; without it every call returns
57+
* an empty string.
58+
*/
59+
public final static String SUBSCRIPTION_KEY
60+
= "org.jitsi.jigasi.transcription.bing.subscription_key";
61+
62+
/**
63+
* Property name for the Azure region of the Translator resource.
64+
* Required for multi-service and regional resources.
65+
*/
66+
public final static String SUBSCRIPTION_REGION
67+
= "org.jitsi.jigasi.transcription.bing.subscription_region";
68+
69+
/**
70+
* Property name for the Translator endpoint. Defaults to the global
71+
* endpoint; a custom-domain resource would use its own hostname.
72+
*/
73+
public final static String ENDPOINT
74+
= "org.jitsi.jigasi.transcription.bing.endpoint";
75+
76+
/**
77+
* Property name for the Translator API version. Defaults to {@code 3.0}.
78+
*/
79+
public final static String API_VERSION
80+
= "org.jitsi.jigasi.transcription.bing.api_version";
81+
82+
public final static String DEFAULT_ENDPOINT
83+
= "https://api.cognitive.microsofttranslator.com";
84+
85+
public final static String DEFAULT_API_VERSION = "3.0";
86+
87+
private final String subscriptionKey;
88+
89+
private final String subscriptionRegion;
90+
91+
private final String endpoint;
92+
93+
private final String apiVersion;
94+
95+
private final Logger logger
96+
= Logger.getLogger(BingTranslationService.class);
97+
98+
public BingTranslationService()
99+
{
100+
subscriptionKey = JigasiBundleActivator.getConfigurationService()
101+
.getString(SUBSCRIPTION_KEY, "");
102+
subscriptionRegion = JigasiBundleActivator.getConfigurationService()
103+
.getString(SUBSCRIPTION_REGION, "");
104+
endpoint = JigasiBundleActivator.getConfigurationService()
105+
.getString(ENDPOINT, DEFAULT_ENDPOINT);
106+
apiVersion = JigasiBundleActivator.getConfigurationService()
107+
.getString(API_VERSION, DEFAULT_API_VERSION);
108+
}
109+
110+
/**
111+
* Utility function to extract the primary language code like:
112+
* 'en-GB', 'en_GB', 'enGB', 'zh-CN', 'zh-TW'
113+
* <p>
114+
* Behaves equivalent to the function "_getPrimaryLanguageCode"
115+
* in jitsi-meet/blob/master/react/features/subtitles/middleware.ts,
116+
* matching {@link LibreTranslateTranslationService}.
117+
*
118+
* @param language The language to use for translation or user requested.
119+
* @return Primary language code
120+
*/
121+
private static String getPrimaryLanguageCode(String language)
122+
{
123+
if (language == null)
124+
{
125+
return "auto";
126+
}
127+
128+
return language.replaceAll("[-_A-Z].*", "");
129+
}
130+
131+
/**
132+
* {@inheritDoc}
133+
*/
134+
@Override
135+
public String translate(String sourceText, String sourceLang,
136+
String targetLang)
137+
{
138+
if (subscriptionKey == null || subscriptionKey.isEmpty())
139+
{
140+
logger.error("Bing translation requested but "
141+
+ SUBSCRIPTION_KEY + " is not set.");
142+
return "";
143+
}
144+
145+
String from = getPrimaryLanguageCode(sourceLang);
146+
String to = getPrimaryLanguageCode(targetLang);
147+
148+
StringBuilder url = new StringBuilder(endpoint);
149+
if (!endpoint.endsWith("/"))
150+
{
151+
url.append('/');
152+
}
153+
url.append("translate?api-version=").append(apiVersion);
154+
if (!"auto".equals(from))
155+
{
156+
url.append("&from=").append(from);
157+
}
158+
url.append("&to=").append(to);
159+
160+
Gson gson = new GsonBuilder().disableHtmlEscaping().create();
161+
JsonArray body = new JsonArray();
162+
JsonObject item = new JsonObject();
163+
item.addProperty("Text", sourceText);
164+
body.add(item);
165+
166+
StringEntity entity = new StringEntity(
167+
gson.toJson(body), ContentType.APPLICATION_JSON);
168+
169+
HttpResponse response;
170+
try (CloseableHttpClient httpClient
171+
= HttpClientBuilder.create().build())
172+
{
173+
HttpPost request = new HttpPost(url.toString());
174+
request.setEntity(entity);
175+
request.setHeader("Accept", "application/json");
176+
request.setHeader("Content-type", "application/json");
177+
request.setHeader(
178+
"Ocp-Apim-Subscription-Key", subscriptionKey);
179+
if (subscriptionRegion != null && !subscriptionRegion.isEmpty())
180+
{
181+
request.setHeader(
182+
"Ocp-Apim-Subscription-Region", subscriptionRegion);
183+
}
184+
185+
response = httpClient.execute(request);
186+
String jsonBody = EntityUtils.toString(response.getEntity());
187+
int statusCode = response.getStatusLine().getStatusCode();
188+
if (statusCode != 200)
189+
{
190+
logger.error("Microsoft Translator responded with status code "
191+
+ statusCode + ".");
192+
logger.error(jsonBody);
193+
return "";
194+
}
195+
196+
BingResponse[] parsed
197+
= gson.fromJson(jsonBody, BingResponse[].class);
198+
if (parsed == null || parsed.length == 0
199+
|| parsed[0].translations == null
200+
|| parsed[0].translations.isEmpty())
201+
{
202+
logger.error(
203+
"Microsoft Translator returned an empty translation set: "
204+
+ jsonBody);
205+
return "";
206+
}
207+
String text = parsed[0].translations.get(0).text;
208+
return text == null ? "" : text;
209+
}
210+
catch (IOException e)
211+
{
212+
logger.error("Error during request to Microsoft Translator.");
213+
logger.error(e.toString());
214+
return "";
215+
}
216+
}
217+
218+
/**
219+
* Class representing the top-level JSON response objects returned by the
220+
* Microsoft Translator v3 {@code /translate} endpoint. Used for
221+
* JSON-to-POJO conversion with Gson.
222+
*/
223+
static class BingResponse
224+
{
225+
List<BingTranslation> translations;
226+
}
227+
228+
/**
229+
* Class representing each element inside the {@code translations} array
230+
* of a Microsoft Translator v3 response.
231+
*/
232+
static class BingTranslation
233+
{
234+
String text;
235+
String to;
236+
}
237+
}

0 commit comments

Comments
 (0)