/* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.motorola.studio.android.localization.translators; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.Authenticator; import java.net.MalformedURLException; import java.net.URL; import java.net.URLEncoder; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.httpclient.Credentials; import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpException; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.UsernamePasswordCredentials; import org.apache.commons.httpclient.auth.AuthScope; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.params.HttpMethodParams; import org.eclipse.core.internal.net.ProxyManager; import org.eclipse.core.net.proxy.IProxyData; import org.eclipse.core.net.proxy.IProxyService; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.osgi.util.NLS; import org.eclipse.sequoyah.localization.tools.datamodel.node.TranslationResult; import org.eclipse.sequoyah.localization.tools.extensions.classes.ITranslator; import org.eclipse.sequoyah.localization.tools.extensions.implementation.generic.ITranslateDialog; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Link; import com.motorola.studio.android.common.log.StudioLogger; import com.motorola.studio.android.common.proxy.ProxyAuthenticator; import com.motorola.studio.android.common.utilities.EclipseUtils; import com.motorola.studio.android.json.JSONArray; import com.motorola.studio.android.json.JSONObject; import com.motorola.studio.android.json.JSONPair; import com.motorola.studio.android.json.JSONString; import com.motorola.studio.android.json.JSONValue; import com.motorola.studio.android.json.Jason; import com.motorola.studio.android.localization.translators.i18n.TranslateNLS; /** * Uses the Google translator web service (via executing a http request and * parsing the answer) in order to translate a text string. * * Google v2 supports only one source and one destination per request, but support many words. * */ @SuppressWarnings("restriction") public final class GoogleTranslator extends ITranslator implements GoogleTranslatorConstants { /** * Translate all words from some source language to destination language. * This method handles any needed splits in original request due to API limitations. * @param words the words being translated * @param fromLanguage the origin language * @param toLanguage the destination language * @param monitor progress monitor * @return a list of translation results * @throws Exception */ public List<TranslationResult> translate(List<String> words, String fromLanguage, String toLanguage, IProgressMonitor monitor) throws Exception { List<TranslationResult> translationResults = new ArrayList<TranslationResult>(); int characterCount = 0; int MAX_REQUEST_SIZE = getMaxQuerySize(fromLanguage, toLanguage); int maxRequestSize = MAX_REQUEST_SIZE; List<String> wordsToTranslate = new ArrayList<String>(); Iterator<String> wordsIterator = words.iterator(); int counter = 0; while (wordsIterator.hasNext()) { maxRequestSize -= STRING_PAR.length(); String word = wordsIterator.next(); /* try to add some more words to the request. * If there is no more room left to request, execute the translation and continue afterwards */ if (characterCount + word.length() < maxRequestSize) { wordsToTranslate.add(word); characterCount += word.length(); } else { URL translationURL = createTranslationURL(wordsToTranslate, fromLanguage, toLanguage); String httpRequestResponseBody = executeHttpGetRequest(translationURL); List<String> responses = parseTranslationResponse(httpRequestResponseBody); for (int i = 0; i < wordsToTranslate.size(); i++) { translationResults.add(new TranslationResult(words.get(counter++), this, responses.get(i), fromLanguage, toLanguage, Calendar.getInstance() .getTime(), true)); } characterCount = 0; maxRequestSize = MAX_REQUEST_SIZE; wordsToTranslate.clear(); wordsToTranslate.add(word); } } /* * execute the request with remaining sentences */ if (!wordsToTranslate.isEmpty()) { URL translationURL = createTranslationURL(wordsToTranslate, fromLanguage, toLanguage); String httpRequestResponseBody = executeHttpGetRequest(translationURL); List<String> responses = parseTranslationResponse(httpRequestResponseBody); for (int i = 0; i < wordsToTranslate.size(); i++) { translationResults.add(new TranslationResult(words.get(counter++), this, responses .get(i), fromLanguage, toLanguage, Calendar.getInstance().getTime(), true)); } } return translationResults; } /** * Translates a string. * * @param text The String to be translated. * @param from Original language. * @param to Target language. * @return The translated String. * @throws Exception on errors. */ @Override public TranslationResult translate(final String text, String from, String to) throws Exception { TranslationResult translationResult; if (text != null && !text.equals("") && text.length() < getMaxQuerySize(from, to)) //$NON-NLS-1$ { String httpResult = ""; //$NON-NLS-1$ URL url = null; // Creates the URL to be used as request try { List<String> sentences = new ArrayList<String>(); sentences.add(text); url = createTranslationURL(sentences, from, to); httpResult = executeHttpGetRequest(url); translationResult = new TranslationResult(text, this, parseTranslationResponse(httpResult).get( 0), from, to, Calendar.getInstance().getTime(), true); } catch (UnsupportedEncodingException e) { throw new HttpException(TranslateNLS.GoogleTranslator_Error_UnsupportedEncoding + ENCODING_TYPE); } } else if (text.length() >= getMaxQuerySize(from, to)) { throw new Exception(TranslateNLS.GoogleTranslator_Error_QueryTooBig); } else { translationResult = new TranslationResult(text, this, text, from, to, new Date(), true); } try { String descriptionToLog = StudioLogger.KEY_TRANSLATION_PROVIDER + StudioLogger.VALUE_GOOGLE + StudioLogger.SEPARATOR + StudioLogger.KEY_TRANSLATION_FROM_LANG + from + StudioLogger.SEPARATOR + StudioLogger.KEY_TRANSLATION_TO_LANG + to; StudioLogger.collectUsageData(StudioLogger.WHAT_LOCALIZATION_AUTOMATICTRANSLATION, StudioLogger.KIND_LOCALIZATION, descriptionToLog, TranslationPlugin.PLUGIN_ID, TranslationPlugin.getDefault().getBundle().getVersion().toString()); } catch (Throwable t) { // Do nothing, usage data collection is for statistics and should not prevent tool from work } return translationResult; } /** * Translate a single word from one language to several other * @param sentences sentence being translated * @param fromLanguage source language * @param toLanguages target languages */ @Override public List<TranslationResult> translate(String sentence, String fromLanguage, List<String> toLanguages) throws Exception { List<TranslationResult> translationResults = new ArrayList<TranslationResult>(); // Lets start with some checkings, one can never be too careful if (fromLanguage == null || toLanguages == null || toLanguages.isEmpty()) { // We must have a FROM and a TO languages throw new IllegalArgumentException( TranslateNLS.GoogleTranslator_Error_ToAndFromLanguagesAreEmpty); } else if (sentence == null || sentence.equals("")) //$NON-NLS-1$ { // We must have something to be translated sentence = ""; //$NON-NLS-1$ if (toLanguages.size() == 1) { translationResults.add(new TranslationResult("", this, "", fromLanguage, //$NON-NLS-1$ //$NON-NLS-2$ toLanguages.get(0), new Date(), true)); } } else if (sentence.length() >= getMaxQuerySize(fromLanguage, toLanguages.get(0))) { throw new Exception(TranslateNLS.GoogleTranslator_Error_QueryTooBig); } /* * Delegate the translation to another method */ else { for (String toLanguage : toLanguages) { translationResults.add(translate(sentence, fromLanguage, toLanguage)); } } try { // Collecting usage data for statistic purposes String descriptionToLog = StudioLogger.KEY_TRANSLATION_PROVIDER + StudioLogger.VALUE_GOOGLE + StudioLogger.SEPARATOR + StudioLogger.KEY_TRANSLATION_FROM_LANG + fromLanguage + StudioLogger.SEPARATOR + StudioLogger.KEY_TRANSLATION_TO_LANG + "several languages"; //$NON-NLS-1$ StudioLogger.collectUsageData(StudioLogger.WHAT_LOCALIZATION_AUTOMATICTRANSLATION, StudioLogger.KIND_LOCALIZATION, descriptionToLog, TranslationPlugin.PLUGIN_ID, TranslationPlugin.getDefault().getBundle().getVersion().toString()); } catch (Throwable t) { // Do nothing, usage data collection is for statistics and should not prevent tool from work } return translationResults; } /** * Translate a list of sentences from one language to another * @param sentences sentences being translated * @param fromLanguage source language * @param toLanguage target language * @param monitor progress monitor */ @Override public List<TranslationResult> translateAll(List<String> sentences, String fromLanguage, String toLanguage, IProgressMonitor monitor) throws Exception { // The result (duhh) List<TranslationResult> translationResults = new ArrayList<TranslationResult>(); // Lets start with some checkings, one can never be too carefull if (fromLanguage == null || toLanguage == null) { // We must have a FROM and a TO languages throw new IllegalArgumentException( TranslateNLS.GoogleTranslator_Error_ToAndFromLanguagesAreEmpty); } else if (sentences == null || sentences.size() == 0) { // We must have something to be translated throw new IllegalArgumentException(TranslateNLS.GoogleTranslator_Error_NoAvailableData); } else { translationResults.addAll(translate(sentences, fromLanguage, toLanguage, monitor)); } try { // Collecting usage data for statistic purposes String descriptionToLog = StudioLogger.KEY_TRANSLATION_PROVIDER + StudioLogger.VALUE_GOOGLE + StudioLogger.SEPARATOR + StudioLogger.KEY_TRANSLATION_FROM_LANG + fromLanguage + StudioLogger.SEPARATOR + StudioLogger.KEY_TRANSLATION_TO_LANG + toLanguage; StudioLogger.collectUsageData(StudioLogger.WHAT_LOCALIZATION_AUTOMATICTRANSLATION, StudioLogger.KIND_LOCALIZATION, descriptionToLog, TranslationPlugin.PLUGIN_ID, TranslationPlugin.getDefault().getBundle().getVersion().toString()); } catch (Throwable t) { // Do nothing, usage data collection is for statistics and should not prevent tool from work } return translationResults; } /** * Translates a list of strings from a list of given languages to other given languages (given by a list, or course), * using google Ajax API's for that. * * The three lists have the same number of elements. * * This comment feels like the "Three Swatch watch switching witches watched switched Swatch watch witches switch", * but I'll let it here anyway. */ @Override public List<TranslationResult> translateAll(List<String> words, List<String> fromLanguage, List<String> toLanguage, IProgressMonitor monitor) throws Exception { // The result (duhh) List<TranslationResult> translationResults = new ArrayList<TranslationResult>(); // Lets start with some checkings, one can never be too carefull if (fromLanguage == null || toLanguage == null) { // We must have a FROM and a TO languages throw new IllegalArgumentException( TranslateNLS.GoogleTranslator_Error_ToAndFromLanguagesAreEmpty); } else if (words == null || words.size() == 0) { // We must have something to be translated throw new IllegalArgumentException(TranslateNLS.GoogleTranslator_Error_NoAvailableData); } else { translationResults.addAll(groupAndTranslate(words, fromLanguage, toLanguage, monitor)); } try { // Collecting usage data for statistic purposes String descriptionToLog = StudioLogger.KEY_TRANSLATION_PROVIDER + StudioLogger.VALUE_GOOGLE + StudioLogger.SEPARATOR + StudioLogger.KEY_TRANSLATION_FROM_LANG + fromLanguage + StudioLogger.SEPARATOR + StudioLogger.KEY_TRANSLATION_TO_LANG + toLanguage; StudioLogger.collectUsageData(StudioLogger.WHAT_LOCALIZATION_AUTOMATICTRANSLATION, StudioLogger.KIND_LOCALIZATION, descriptionToLog, TranslationPlugin.PLUGIN_ID, TranslationPlugin.getDefault().getBundle().getVersion().toString()); } catch (Throwable t) { // Do nothing, usage data collection is for statistics and should not prevent tool from work } return translationResults; } private List<TranslationResult> groupAndTranslate(List<String> words, List<String> fromLanguage, List<String> toLanguage, IProgressMonitor monitor) throws Exception { List<TranslationResult> results = new ArrayList<TranslationResult>(); /* * Get all words with same source and same destination and group them to make translation more efficient * Notice that this implementation relies on input condition that all lists have the same size and all elements with same index makes one request */ class StringItem { private String sentence = ""; private int orderNumber = -1; public StringItem(String sentence, int orderNumber) { this.sentence = sentence; this.orderNumber = orderNumber; } public Integer getOrderNumber() { return orderNumber; } public String getSentence() { return sentence; } @Override public String toString() { return orderNumber + "|" + sentence; } } /* * This map holds a list of group of stringitems being translated. These items have the same from and to languages * Using linkedHashMap to keep insertionOrder */ Map<String, List<StringItem>> sameSourceDestMap = new LinkedHashMap<String, List<StringItem>>(); Map<Integer, TranslationResult> translations = new HashMap<Integer, TranslationResult>(); // group things with same from and to languages for (int i = 0; i < words.size(); i++) { /* * Check if one of the words are big enough to not be translated */ if (words.get(i).length() >= getMaxQuerySize(fromLanguage.get(i), toLanguage.get(i)) - STRING_PAR.length() - 1) { throw new Exception(TranslateNLS.GoogleTranslator_Error_QueryTooBig); } String key = fromLanguage.get(i) + "|" + toLanguage.get(i); List<StringItem> itemsToTranslate = sameSourceDestMap.get(key); if (itemsToTranslate == null) { itemsToTranslate = new ArrayList<StringItem>(); sameSourceDestMap.put(key, itemsToTranslate); } itemsToTranslate.add(new StringItem(words.get(i), i)); } for (String key : sameSourceDestMap.keySet()) { List<StringItem> itemsToTranslate = sameSourceDestMap.get(key); List<String> items = new ArrayList<String>(); for (StringItem item : itemsToTranslate) { items.add(item.getSentence()); } List<TranslationResult> tempResults = translate(items, key.split("\\|")[0], key.split("\\|")[1], monitor); for (int i = 0; i < itemsToTranslate.size(); i++) { translations.put(itemsToTranslate.get(i).getOrderNumber(), tempResults.get(i)); } } for (int i = 0; i < words.size(); i++) { results.add(translations.get(i)); } return results; } private int getMaxQuerySize(String fromLanguage, String toLanguage) { return MAX_QUERY_SIZE - TRANSLATE_URL_WITHOUT_PARAMS.length() - API_KEY_PARAM.length() - getApiKey().length() - SOURCE_PAR.length() - fromLanguage.length() - TARGET_PARAM.length() - toLanguage.length(); } /** * Parse the translation response of the http request * @param httpRequestResponseBody the response body * @param sourceLanguage the source language * @param destinationLanguage the destination language * @return a list of String objects for the strings translated for source/destination languages pair */ private List<String> parseTranslationResponse(String httpRequestResponseBody) { JSONPair translationSection = getTranslationSection(httpRequestResponseBody); return getTranslations(translationSection); } /* * { "data": { "translations": [ { "translatedText": "bla bla bla" }, { "translatedText": "foo bar" } ] } } */ private JSONPair getTranslationSection(String httpRequestResponseBody) { Jason ripper = new Jason(httpRequestResponseBody); JSONPair translationsSection = null; Iterator<JSONObject> jsonIterator = ripper.getJSON().iterator(); while (translationsSection == null && jsonIterator.hasNext()) { translationsSection = findPair(jsonIterator.next(), TRANSLATIONS_SECTION); } return translationsSection; } private static JSONPair findPair(JSONValue origin, String name) { JSONPair pair = null; if (origin instanceof JSONObject) { JSONObject object = (JSONObject) origin; Iterator<JSONPair> pairs = object.getValue().iterator(); while (pair == null && pairs.hasNext()) { JSONPair jsonPair = pairs.next(); if (jsonPair.getName().equals(name)) { pair = jsonPair; } else { pair = findPair(jsonPair.getValue(), name); } } } return pair; } private List<String> getTranslations(JSONPair translationSection) { List<String> translations = new ArrayList<String>(); if (translationSection.getValue() instanceof JSONArray) { JSONArray translationsArray = (JSONArray) translationSection.getValue(); for (JSONValue translationObject : translationsArray.getValue()) { translations.add(getTranslation(translationObject)); } } return translations; } /** * @param translationObject * { * "translatedText": "Hallo Welt" * } * @return pure translation * Hallo Welt */ private String getTranslation(JSONValue translationObject) { String translation = null; if (translationObject instanceof JSONObject) { JSONObject jsonObject = (JSONObject) translationObject; translation = ((JSONString) jsonObject.getValue().iterator().next().getValue()).getValue(); } return translation != null ? fixHTMLTags(translation) : null; } private URL createTranslationURL(List<String> wordsToTranslate, String fromLanguage, String toLanguage) throws UnsupportedEncodingException { URL translationURL = null; // We need to unescape the ' (apostrophe) before sending it to translation String regex = "\\\\'"; //$NON-NLS-1$ Pattern pattern = Pattern.compile(regex); StringBuilder urlBuilder = new StringBuilder(TRANSLATE_URL_WITHOUT_PARAMS); urlBuilder.append(URL_PARAMETERS.replace("#FROM#", fromLanguage) .replace("#TO#", toLanguage).replace("#API_KEY#", getApiKey())); for (String word : wordsToTranslate) { String wordToTranslate = pattern.matcher(word).replaceAll("'"); urlBuilder.append(STRING_PAR); urlBuilder.append(URLEncoder.encode(wordToTranslate, ENCODING_TYPE)); } try { translationURL = new URL(urlBuilder.toString()); } catch (MalformedURLException e) { StudioLogger.error(getClass(), "Unable to create translation URL", e); } return translationURL; } /** * The Android localization files text must accept three HTML tags: i, b and u. * Nevertheless, google translator returns the close part of this tags with * a extra-space that makes the sintax wrong. This method will try to fix it. * @param originalText * @return the text with the tags fixed */ private static String fixHTMLTags(String originalText) { String result = ""; //$NON-NLS-1$ if (originalText != null) { result = originalText; } result = originalText.replaceAll("</ b>", "</b>"); //$NON-NLS-1$ //$NON-NLS-2$ result = originalText.replaceAll("</ i>", "</i>"); //$NON-NLS-1$ //$NON-NLS-2$ result = originalText.replaceAll("</ u>", "</u>"); //$NON-NLS-1$ //$NON-NLS-2$ result = originalText.replaceAll("</ B>", "</B>"); //$NON-NLS-1$ //$NON-NLS-2$ result = originalText.replaceAll("</ I>", "</I>"); //$NON-NLS-1$ //$NON-NLS-2$ result = originalText.replaceAll("</ U>", "</U>"); //$NON-NLS-1$ //$NON-NLS-2$ result = originalText.replaceAll(" \\\\ n ", " \\\\n "); //$NON-NLS-1$ //$NON-NLS-2$ return result; } /** * Creates an HTTP request with the URL, execute it as a get, and returns * the a string with the result. * * @param url * URL to be executed. * @return String with the URL execution result. * @throws IOException * If an exception occurs on transport * @throws HttpException * If an exception occurs on the protocol * @throws Exception * on error. */ protected static String executeHttpGetRequest(final URL url) throws HttpException { // Checking query size due to google policies if (url.toString().length() > MAX_QUERY_SIZE) { throw new HttpException(TranslateNLS.GoogleTranslator_Error_QueryTooBig); } // Try to retrieve proxy configuration to use if necessary IProxyService proxyService = ProxyManager.getProxyManager(); IProxyData proxyData = null; if (proxyService.isProxiesEnabled() || proxyService.isSystemProxiesEnabled()) { Authenticator.setDefault(new ProxyAuthenticator()); String urlStr = url.toString(); if (urlStr.startsWith("https")) { proxyData = proxyService.getProxyData(IProxyData.HTTPS_PROXY_TYPE); StudioLogger.debug(GoogleTranslator.class, "Using https proxy"); //$NON-NLS-1$ } else if (urlStr.startsWith("http")) { proxyData = proxyService.getProxyData(IProxyData.HTTP_PROXY_TYPE); StudioLogger.debug(GoogleTranslator.class, "Using http proxy"); //$NON-NLS-1$ } else { StudioLogger.debug(GoogleTranslator.class, "Not using any proxy"); //$NON-NLS-1$ } } // Creates the http client and the method to be executed HttpClient client = null; client = new HttpClient(); // If there is proxy data, work with it if (proxyData != null) { if (proxyData.getHost() != null) { // Sets proxy host and port, if any client.getHostConfiguration().setProxy(proxyData.getHost(), proxyData.getPort()); } if (proxyData.getUserId() != null && proxyData.getUserId().trim().length() > 0) { // Sets proxy user and password, if any Credentials cred = new UsernamePasswordCredentials(proxyData.getUserId(), proxyData.getPassword() == null ? "" : proxyData.getPassword()); //$NON-NLS-1$ client.getState().setProxyCredentials(AuthScope.ANY, cred); } } // Creating the method to be executed, the URL at this point is enough // because it is complete GetMethod method = new GetMethod(url.toString()); // Set method to be retried three times in case of error method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(RETRIES, false)); method.setRequestHeader(REFERER_HEADER, REFERER_SITE); // Set the connection timeout client.getHttpConnectionManager().getParams().setConnectionTimeout(new Integer(TIMEOUT)); String result = ""; //$NON-NLS-1$ try { // Execute the method. int statusCode; try { statusCode = client.executeMethod(method); result = method.getResponseBodyAsString(MAX_SIZE); } catch (IOException e) { throw new HttpException(TranslateNLS.GoogleTranslator_Error_CannotConnectToServer + e.getMessage()); } checkStatusCode(statusCode, result); // Unescape any possible unicode char result = unescapeUnicode(result); // Unescape any possible HTML sequence result = unescapeHTML(result); } finally { // Release the connection. method.releaseConnection(); } return result; } private static void checkStatusCode(int statusCode, String response) throws HttpException { switch (statusCode) { case HttpStatus.SC_OK: //do nothing break; case HttpStatus.SC_BAD_REQUEST: throw new HttpException(NLS.bind( TranslateNLS.GoogleTranslator_ErrorMessageExecutingRequest, getErrorMessage(response))); case HttpStatus.SC_REQUEST_URI_TOO_LONG: throw new HttpException(TranslateNLS.GoogleTranslator_Error_QueryTooBig); case HttpStatus.SC_FORBIDDEN: throw new HttpException(NLS.bind( TranslateNLS.GoogleTranslator_ErrorMessageNoValidTranslationReturned, getErrorMessage(response))); default: throw new HttpException(NLS.bind( TranslateNLS.GoogleTranslator_Error_HTTPRequestError, new Object[] { statusCode, getErrorMessage(response) })); } } /** * According to APIv2, the error message is in the end of the response * { "error": { "errors": [ { "domain": "global", "reason": "invalid", "message": "Invalid Value" } ], "code": 400, "message": "Invalid Value" } } * @param response the method response body * @return the error message */ private static String getErrorMessage(String response) { Jason ripper = new Jason(response); JSONPair translationsSection = null; Iterator<JSONObject> jsonIterator = ripper.getJSON().iterator(); while (translationsSection == null && jsonIterator.hasNext()) { translationsSection = findPair(jsonIterator.next(), MESSAGE_TEXT); } return translationsSection != null ? ((JSONString) translationsSection.getValue()) .getValue() : null; } /** * Unescape any HTML sequence that exists inside the string. For example, * the sequence ' will be changed to the ' symbol * * @param source * original text * @return the result */ private static String unescapeHTML(String source) { Pattern p = Pattern.compile("&#([0-9]+);"); //$NON-NLS-1$ String result = source; Matcher m = p.matcher(result); while (m.find()) { char c = (char) Integer.parseInt(m.group(1)); if (c == "'".charAt(0)) //$NON-NLS-1$ { // Apostrophes must be escaped by preceding it with a backslash (\) on the XML file result = result.replaceAll(m.group(0), "\\\\'"); //$NON-NLS-1$ } else { result = result.replaceAll(m.group(0), "" + c); //$NON-NLS-1$ } } return result; } /** * Unescape any Unicode sequence that exists inside the string. * * For example, the sequence \u0000 will be changed to the symbol * correnponded to the 0000 unicode value. * * @param source * original text * @return the result */ private static String unescapeUnicode(String source) { int i = 0, len = source.length(); char c; StringBuffer buffer = new StringBuffer(len); while (i < len) { c = source.charAt(i++); if (c == '\\') { if (i < len) { c = source.charAt(i++); if (c == 'u') { c = (char) Integer.parseInt(source.substring(i, i + 4), 16); i += 4; } } } buffer.append(c); } return buffer.toString(); } private static String getApiKey() { String apiKey = GoogleTranslatorConstants.API_KEY; IPreferenceStore prefStore = TranslationPlugin.getDefault().getPreferenceStore(); if (!prefStore.isDefault(GoogleTranslatorConstants.API_KEY_VALUE_PREFERENCE)) { apiKey = prefStore.getString(GoogleTranslatorConstants.API_KEY_VALUE_PREFERENCE); if (apiKey == null) { apiKey = GoogleTranslatorConstants.API_KEY; } } return apiKey; } @Override public Composite createCustomArea(Composite parent, final ITranslateDialog dialog) { Composite mainComposite = new Composite(parent, SWT.NONE); mainComposite.setLayout(new GridLayout(1, false)); final Link prefPageLink = new Link(mainComposite, SWT.NONE); prefPageLink.setText(TranslateNLS.GoogleTranslator_ChangeAPIkeyLabel); prefPageLink.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, true, true, 1, 1)); prefPageLink.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { EclipseUtils.openPreference(prefPageLink.getShell(), "com.motorola.studio.android.localization.translators.preferencepage"); //$NON-NLS-1$ dialog.validate(); } }); mainComposite.setLayoutData(new GridData(SWT.RIGHT, SWT.BOTTOM, false, false)); return mainComposite; } @Override public String canTranslate(String fromLanguage, String[] toLanguages) { return getApiKey() == null || GoogleTranslatorConstants.API_KEY.equals(getApiKey()) ? TranslateNLS.GoogleTranslator_ErrorNoAPIkeySet : null; } }