Plugins/UiExtensionPlugins/Translator/TranslatorEngines/MicrosoftEngine.py

changeset 6018
1c858879d3d0
child 6048
82ad8ec9548c
diff -r dab01678626d -r 1c858879d3d0 Plugins/UiExtensionPlugins/Translator/TranslatorEngines/MicrosoftEngine.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/UiExtensionPlugins/Translator/TranslatorEngines/MicrosoftEngine.py	Sun Dec 10 16:23:29 2017 +0100
@@ -0,0 +1,204 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2014 - 2017 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the Microsoft translation engine.
+"""
+
+from __future__ import unicode_literals
+try:
+    str = unicode
+except NameError:
+    pass
+
+from PyQt5.QtCore import QUrl, QDateTime, QByteArray
+
+from .TranslationEngine import TranslationEngine
+
+
+class MicrosoftEngine(TranslationEngine):
+    """
+    Class implementing the translation engine for the Microsoft
+    translation service.
+    """
+    AccessTokenUrl = \
+        "https://api.cognitive.microsoft.com/sts/v1.0/issueToken"
+    TranslatorUrl = "https://api.microsofttranslator.com/V2/Http.svc/Translate"
+    TextToSpeechUrl = "https://api.microsofttranslator.com/V2/Http.svc/Speak"
+    
+    def __init__(self, plugin, parent=None):
+        """
+        Constructor
+        
+        @param plugin reference to the plugin object (TranslatorPlugin)
+        @param parent reference to the parent object (QObject)
+        """
+        super(MicrosoftEngine, self).__init__(plugin, parent)
+        
+        self.__mappings = {
+            "zh-CN": "zh-CHS",
+            "zh-TW": "zh-CHT",
+        }
+    
+    def engineName(self):
+        """
+        Public method to return the name of the engine.
+        
+        @return engine name (string)
+        """
+        return "microsoft"
+    
+    def supportedLanguages(self):
+        """
+        Public method to get the supported languages.
+        
+        @return list of supported language codes (list of string)
+        """
+        return ["ar", "bg", "ca", "cs", "da", "de", "en",
+                "es", "et", "fi", "fr", "hi", "hu", "id",
+                "it", "ja", "ko", "lt", "lv", "mt",
+                "nl", "no", "pl", "pt", "ro", "ru", "sk", "sl",
+                "sv", "th", "tr", "uk", "vi", "zh-CN", "zh-TW",
+                ]
+    
+    def hasTTS(self):
+        """
+        Public method indicating the Text-to-Speech capability.
+        
+        @return flag indicating the Text-to-Speech capability (boolean)
+        """
+        return True
+    
+    def __mapLanguageCode(self, code):
+        """
+        Private method to map a language code to the Microsoft code.
+        
+        @param code language code (string)
+        @return mapped language code (string)
+        """
+        if code in self.__mappings:
+            return self.__mapping[code]
+        else:
+            return code
+    
+    def __getClientDataAzure(self):
+        """
+        Private method to retrieve the client data.
+        
+        @return tuple giving the API subscription key and a flag indicating
+            validity
+        @rtype tuple of (str, bool)
+        """
+        subscriptionKey = self.plugin.getPreferences("MsTranslatorKey")
+        valid = bool(subscriptionKey)
+        return subscriptionKey, valid
+    
+    def __getAccessToken(self, requestObject):
+        """
+        Private slot to get an access token.
+        
+        If the stored token is no longer valid, get a new one and store it.
+        
+        @param requestObject reference to the request object
+            (TranslatorRequest)
+        @return access token (string)
+        """
+        if self.plugin.getPreferences("MsAuthTokenExpire") > \
+                QDateTime.currentDateTime():
+            return self.plugin.getPreferences("MsAuthToken")
+        
+        # Token expired, get a new one
+        subscriptionKey, valid = self.__getClientDataAzure()
+        if not valid:
+            return ""
+        
+        subscriptionHeader = (b"Ocp-Apim-Subscription-Key",
+                              subscriptionKey.encode("utf-8"))
+        response, ok = requestObject.post(
+            QUrl(self.AccessTokenUrl), QByteArray(b""),
+            extraHeaders=[subscriptionHeader])
+        if ok:
+            self.plugin.setPreferences("MsAuthToken", response)
+            self.plugin.setPreferences(
+                "MsAuthTokenExpire",
+                QDateTime.currentDateTime().addSecs(8 * 60))
+            return response
+        else:
+            return ""
+    
+    def getTranslation(self, requestObject, text, originalLanguage,
+                       translationLanguage):
+        """
+        Public method to translate the given text.
+        
+        @param requestObject reference to the request object
+            (TranslatorRequest)
+        @param text text to be translated (string)
+        @param originalLanguage language code of the original (string)
+        @param translationLanguage language code of the translation (string)
+        @return tuple of translated text (string) and flag indicating
+            success (boolean)
+        """
+        subscriptionKey, valid = self.__getClientDataAzure()
+        if not valid:
+            return (self.tr("""You have not registered for the Microsoft"""
+                            """ Translation service."""),
+                    False)
+        
+        accessToken = self.__getAccessToken(requestObject)
+        if not accessToken:
+            return (self.tr("No valid access token available."), False)
+        
+        authHeader = (b"Authorization",
+                      "Bearer {0}".format(accessToken).encode("utf-8"))
+        params = "?appid=&from={0}&to={1}&text={2}".format(
+            self.__mapLanguageCode(originalLanguage),
+            self.__mapLanguageCode(translationLanguage),
+            text)
+        url = QUrl(self.TranslatorUrl + params)
+        response, ok = requestObject.get(url, extraHeaders=[authHeader])
+        response = str(response, "utf-8", "replace")
+        if ok and response.startswith("<string") and \
+                response.endswith("</string>"):
+            result = response.split(">", 1)[1].rsplit("<", 1)[0]
+        else:
+            result = self.tr("No translation available.")
+            ok = False
+        return result, ok
+    
+    def getTextToSpeechData(self, requestObject, text, language):
+        """
+        Public method to pronounce the given text.
+        
+        @param requestObject reference to the request object
+            (TranslatorRequest)
+        @param text text to be pronounced (string)
+        @param language language code of the text (string)
+        @return tuple with pronounce data (QByteArray) or error string (string)
+            and success flag (boolean)
+        """
+        subscriptionKey, valid = self.__getClientDataAzure()
+        if not valid:
+            return (self.tr("""You have not registered for the Microsoft"""
+                            """ Translation service."""),
+                    False)
+        
+        accessToken = self.__getAccessToken(requestObject)
+        if not accessToken:
+            return (self.tr("No valid access token available."), False)
+        
+        params = "?language={0}&format={1}&options={2}&text={3}".format(
+            self.__mapLanguageCode(language),
+            "audio/wav",
+            "MaxQuality",
+            text)
+        authHeader = (b"Authorization",
+                      "Bearer {0}".format(accessToken).encode("utf-8"))
+        url = QUrl(self.TextToSpeechUrl + params)
+        data, ok = requestObject.get(url, extraHeaders=[authHeader])
+        if not ok:
+            data = self.tr("No Text-to-Speech for the selected language"
+                           " available.")
+        return data, ok

eric ide

mercurial