Plugins/UiExtensionPlugins/Translator/TranslatorEngines/DeepLEngine.py

Sun, 31 Dec 2017 16:52:09 +0100

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Sun, 31 Dec 2017 16:52:09 +0100
changeset 6048
82ad8ec9548c
parent 6026
4773c9469880
child 6227
fc0869cd16dc
permissions
-rw-r--r--

Updated copyright for 2018.

# -*- coding: utf-8 -*-

# Copyright (c) 2017 - 2018 Detlev Offenbach <detlev@die-offenbachs.de>
#

"""
Module implementing the DeepL translation engine.
"""

from __future__ import unicode_literals
try:
    str = unicode
except NameError:
    pass

import json
import re

from PyQt5.QtCore import QUrl, QByteArray

from .TranslationEngine import TranslationEngine


class DeepLEngine(TranslationEngine):
    """
    Class implementing the translation engine for the DeepL
    translation service.
    """
    TranslatorUrl = "https://deepl.com/jsonrpc"
    MaxTranslationTextLen = 5000
    
    def __init__(self, plugin, parent=None):
        """
        Constructor
        
        @param plugin reference to the plugin object
        @type TranslatorPlugin
        @param parent reference to the parent object
        @type QObject
        """
        super(DeepLEngine, self).__init__(plugin, parent)
        
        self.__splitPattern = re.compile(r"([^\.!\?;]+[\.!\?;]*)")
    
    def engineName(self):
        """
        Public method to return the name of the engine.
        
        @return engine name
        @rtype str
        """
        return "deepl"
    
    def supportedLanguages(self):
        """
        Public method to get the supported languages.
        
        @return list of supported language codes
        @rtype list of str
        """
        return ["de", "en", "es", "fr", "it", "nl", "pl", ]
    
    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)
        """
        if len(text) > self.MaxTranslationTextLen:
            return self.tr(
                "Text to be translated exceeds the translation limit of {0}"
                " characters.").format(self.MaxTranslationTextLen), False
        
        sentences = [s for s in self.__splitPattern.split(text) if len(s) > 0]
        if originalLanguage in self.supportedLanguages():
            originalLanguageU = originalLanguage.upper()
        else:
            originalLanguageU = "auto"
        payload = {
            "jsonrpc": "2.0",
            "method": "LMT_handle_jobs",
            "id": 1,
            "params": {
                "jobs": [
                    {"kind": "default", "raw_en_sentence": s}
                    for s in sentences
                ],
                "lang": {
                    "user_preferred_langs": [
                        originalLanguage.upper(),
                        translationLanguage.upper(),
                    ],
                    "source_lang_user_selected": originalLanguageU,
                    "target_lang": translationLanguage.upper()
                },
                "priority": 1,
            }
        }
        request = QByteArray(json.dumps(payload).encode("utf-8"))
        response, ok = requestObject.post(QUrl(self.TranslatorUrl), request,
                                          "json")
        if ok:
            try:
                responseDict = json.loads(response)
            except ValueError:
                return self.tr("Invalid response received from DeepL"), False
            
            if "error" in responseDict:
                return self.tr("DeepL reported an error.\nMessage: {0}")\
                    .format(responseDict["error"]["message"]), False
            
            if "result" not in responseDict:
                return self.tr("DeepL call returned an unknown result"), False
            
            if not responseDict["result"]["source_lang"] or \
               not responseDict["result"]["target_lang"]:
                return self.tr(
                    "Unsupported language code given (source: {0},"
                    " target: {1}).").format(
                    originalLanguage, translationLanguage), False
            
            translations = responseDict["result"]["translations"]
            if len(translations) == 0:
                return self.tr("<p>No translation found</p>"), True
            
            # show sentence by sentence separated by a line
            translationStrings = []
            for translation in translations:
                translationStrings.append(
                    "<br/>".join(
                        [
                            s["postprocessed_sentence"] for s in sorted(
                                translation["beams"],
                                key=lambda b: -1 * b["score"])
                        ]
                    )
                )
            result = "<p>" + "<hr/>".join(translationStrings) + "</p>"
        
        else:
            result = response
        return result, ok

eric ide

mercurial