/**************************************************************************
OmegaT - Computer Assisted Translation (CAT) tool
with fuzzy matching, translation memory, keyword search,
glossaries, and translation leveraging into updated projects.
Copyright (C) 2012 Alex Buloichik, Didier Briel
2016-2017 Aaron Madlon-Kay
Home page: http://www.omegat.org/
Support center: http://groups.yahoo.com/group/OmegaT/
This file is part of OmegaT.
OmegaT is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OmegaT is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
**************************************************************************/
package org.omegat.core.machinetranslators;
import java.awt.Window;
import java.util.Collections;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.omegat.gui.exttrans.MTConfigDialog;
import org.omegat.util.Language;
import org.omegat.util.OStrings;
import org.omegat.util.Preferences;
import org.omegat.util.StringUtil;
import org.omegat.util.WikiGet;
/**
* Support for Microsoft Translator API machine translation.
*
* @author Alex Buloichik (alex73mail@gmail.com)
* @author Didier Briel
* @author Aaron Madlon-Kay
*
* @see <a href="https://www.microsoft.com/en-us/translator/translatorapi.aspx">Translator API</a>
* @see <a href="https://docs.microsofttranslator.com/text-translate.html">Translate Method reference</a>
*/
public class MicrosoftTranslatorAzure extends BaseTranslate {
private static final Logger LOGGER = Logger.getLogger(MicrosoftTranslatorAzure.class.getName());
protected static final String PROPERTY_SUBSCRIPTION_KEY = "microsoft.api.subscription_key";
protected static final String URL_TOKEN = "https://api.cognitive.microsoft.com/sts/v1.0/issueToken";
protected static final String URL_TRANSLATE = "https://api.microsofttranslator.com/v2/http.svc/Translate";
protected static final Pattern RE_RESPONSE = Pattern.compile("<string[^>]*>(.+)</string>");
protected String accessToken;
@Override
protected String getPreferenceName() {
return Preferences.ALLOW_MICROSOFT_TRANSLATOR_AZURE;
}
public String getName() {
return OStrings.getString("MT_ENGINE_MICROSOFT_AZURE");
}
@Override
protected synchronized String translate(Language sLang, Language tLang, String text) throws Exception {
text = text.length() > 10000 ? text.substring(0, 9997) + "..." : text;
String prev = getFromCache(sLang, tLang, text);
if (prev != null) {
return prev;
}
String langFrom = checkMSLang(sLang);
String langTo = checkMSLang(tLang);
try {
String translation;
if (accessToken == null) {
requestToken();
translation = requestTranslate(langFrom, langTo, text);
} else {
try {
translation = requestTranslate(langFrom, langTo, text);
} catch (WikiGet.ResponseError ex) {
if (ex.code == 400) {
LOGGER.finer("Re-fetching Microsoft Translator API token due to 400 response");
requestToken();
translation = requestTranslate(langFrom, langTo, text);
} else {
throw ex;
}
}
}
if (translation != null) {
putToCache(sLang, tLang, text, translation);
}
return translation;
} catch (WikiGet.ResponseError ex) {
return ex.getLocalizedMessage();
} catch (Exception ex) {
LOGGER.log(Level.FINE, ex.getLocalizedMessage(), ex);
return ex.getLocalizedMessage();
}
}
/**
* Converts language codes to Microsoft ones.
* @param language
* a project language
* @return either a language code, or a Chinese language code plus a Microsoft variant
*/
private String checkMSLang(Language language) {
String lang = language.getLanguage();
if (lang.equalsIgnoreCase("zh-cn")) {
return "zh-CHS";
} else if (lang.equalsIgnoreCase("zh-tw") || lang.equalsIgnoreCase("zh-hk")) {
return "zh-CHT";
} else {
return language.getLanguageCode();
}
}
private void requestToken() throws Exception {
String key = getCredential(PROPERTY_SUBSCRIPTION_KEY);
if (StringUtil.isEmpty(key)) {
throw new Exception(OStrings.getString("MT_ENGINE_MICROSOFT_SUBSCRIPTION_KEY_NOTFOUND"));
}
Map<String, String> headers = new TreeMap<String, String>();
headers.put("Ocp-Apim-Subscription-Key", key);
headers.put("Content-Type", "application/json");
headers.put("Accept", "application/jwt");
accessToken = WikiGet.post(URL_TOKEN, Collections.emptyMap(), headers);
}
private String requestTranslate(String langFrom, String langTo, String text) throws Exception {
Map<String, String> p = new TreeMap<String, String>();
p.put("appid", "Bearer " + accessToken);
p.put("text", text);
p.put("from", langFrom);
p.put("to", langTo);
p.put("contentType", "text/plain");
String r = WikiGet.get(URL_TRANSLATE, p, null);
Matcher m = RE_RESPONSE.matcher(r);
if (m.matches()) {
String translatedText = m.group(1);
translatedText = translatedText.replace("<", "<");
translatedText = translatedText.replace(">", ">");
return translatedText;
} else {
LOGGER.warning(OStrings.getString("MT_ENGINE_MICROSOFT_WRONG_RESPONSE"));
return null;
}
}
@Override
public boolean isConfigurable() {
return true;
}
@Override
public void showConfigurationUI(Window parent) {
MTConfigDialog dialog = new MTConfigDialog(parent, getName()) {
@Override
protected void onConfirm() {
String key = panel.valueField1.getText().trim();
boolean temporary = panel.temporaryCheckBox.isSelected();
setCredential(PROPERTY_SUBSCRIPTION_KEY, key, temporary);
}
};
dialog.panel.valueLabel1.setText(OStrings.getString("MT_ENGINE_MICROSOFT_SUBSCRIPTION_KEY_LABEL"));
dialog.panel.valueField1.setText(getCredential(PROPERTY_SUBSCRIPTION_KEY));
dialog.panel.valueLabel2.setVisible(false);
dialog.panel.valueField2.setVisible(false);
dialog.panel.temporaryCheckBox.setSelected(isCredentialStoredTemporarily(PROPERTY_SUBSCRIPTION_KEY));
dialog.show();
}
}