Plugins/UiExtensionPlugins/Translator/TranslatorEngines/DeepLEngine.py

changeset 6539
0de153b92d53
parent 6412
d71b094845e7
child 6545
f669c4c0d39b
equal deleted inserted replaced
6538:0a24808561e0 6539:0de153b92d53
12 str = unicode 12 str = unicode
13 except NameError: 13 except NameError:
14 pass 14 pass
15 15
16 import json 16 import json
17 import re
18 17
19 from PyQt5.QtCore import QUrl, QByteArray, QTimer 18 from PyQt5.QtCore import QUrl, QByteArray, QTimer
19
20 import Utilities
20 21
21 from .TranslationEngine import TranslationEngine 22 from .TranslationEngine import TranslationEngine
22 23
23 24
24 class DeepLEngine(TranslationEngine): 25 class DeepLEngine(TranslationEngine):
25 """ 26 """
26 Class implementing the translation engine for the DeepL 27 Class implementing the translation engine for the DeepL
27 translation service. 28 translation service.
28 """ 29 """
29 TranslatorUrl = "https://www.deepl.com/jsonrpc" 30 TranslatorUrl = "https://api.deepl.com/v1/translate"
30 MaxTranslationTextLen = 5000 31 MaxTranslationTextLen = 30 * 1024
31 32
32 def __init__(self, plugin, parent=None): 33 def __init__(self, plugin, parent=None):
33 """ 34 """
34 Constructor 35 Constructor
35 36
37 @type TranslatorPlugin 38 @type TranslatorPlugin
38 @param parent reference to the parent object 39 @param parent reference to the parent object
39 @type QObject 40 @type QObject
40 """ 41 """
41 super(DeepLEngine, self).__init__(plugin, parent) 42 super(DeepLEngine, self).__init__(plugin, parent)
42
43 self.__splitPattern = re.compile(r"([^\.!\?;]+[\.!\?;]*)")
44 43
45 QTimer.singleShot(0, self.availableTranslationsLoaded.emit) 44 QTimer.singleShot(0, self.availableTranslationsLoaded.emit)
46 45
47 def engineName(self): 46 def engineName(self):
48 """ 47 """
78 if len(text) > self.MaxTranslationTextLen: 77 if len(text) > self.MaxTranslationTextLen:
79 return self.tr( 78 return self.tr(
80 "Text to be translated exceeds the translation limit of {0}" 79 "Text to be translated exceeds the translation limit of {0}"
81 " characters.").format(self.MaxTranslationTextLen), False 80 " characters.").format(self.MaxTranslationTextLen), False
82 81
83 sentences = [s for s in self.__splitPattern.split(text) if len(s) > 0] 82 apiKey = self.plugin.getPreferences("DeeplKey")
84 if originalLanguage in self.supportedLanguages(): 83 if not apiKey:
85 originalLanguageU = originalLanguage.upper() 84 return self.tr("A valid DeepL Pro key is required."), False
86 else: 85
87 originalLanguageU = "auto" 86 params = QByteArray(
88 payload = { 87 "auth_key={0}&source_lang={1}&target_lang={2}&text=".format(
89 "jsonrpc": "2.0", 88 apiKey, originalLanguage.upper(), translationLanguage.upper()).encode("utf-8"))
90 "method": "LMT_handle_jobs", 89 encodedText = QByteArray(Utilities.html_encode(text).encode("utf-8"))\
91 "id": 1, 90 .toPercentEncoding()
92 "params": { 91 request = params + encodedText
93 "jobs": [ 92 response, ok = requestObject.post(QUrl(self.TranslatorUrl), request)
94 {"kind": "default", "raw_en_sentence": s}
95 for s in sentences
96 ],
97 "lang": {
98 "user_preferred_langs": [
99 originalLanguage.upper(),
100 translationLanguage.upper(),
101 ],
102 "source_lang_user_selected": originalLanguageU,
103 "target_lang": translationLanguage.upper()
104 },
105 "priority": 1,
106 }
107 }
108 request = QByteArray(json.dumps(payload).encode("utf-8"))
109 response, ok = requestObject.post(QUrl(self.TranslatorUrl), request,
110 "json")
111 if ok: 93 if ok:
112 try: 94 try:
113 responseDict = json.loads(response) 95 responseDict = json.loads(response)
114 except ValueError: 96 except ValueError:
115 return self.tr("Invalid response received from DeepL"), False 97 return self.tr("Invalid response received from DeepL"), False
116 98
117 if "error" in responseDict: 99 if "translations" not in responseDict:
118 return self.tr("DeepL reported an error.\nMessage: {0}")\
119 .format(responseDict["error"]["message"]), False
120
121 if "result" not in responseDict:
122 return self.tr("DeepL call returned an unknown result"), False 100 return self.tr("DeepL call returned an unknown result"), False
123 101
124 if not responseDict["result"]["source_lang"] or \ 102 translations = responseDict["translations"]
125 not responseDict["result"]["target_lang"]:
126 return self.tr(
127 "Unsupported language code given (source: {0},"
128 " target: {1}).").format(
129 originalLanguage, translationLanguage), False
130
131 translations = responseDict["result"]["translations"]
132 if len(translations) == 0: 103 if len(translations) == 0:
133 return self.tr("<p>No translation found</p>"), True 104 return self.tr("<p>No translation found</p>"), True
134 105
135 # show sentence by sentence separated by a line 106 # show sentence by sentence separated by a line
136 translationStrings = [] 107 result = (
137 for translation in translations: 108 "<p>" +
138 translationStrings.append( 109 "<hr/>".join([t["text"] for t in translations]) +
139 "<br/>".join( 110 "</p>"
140 [ 111 )
141 s["postprocessed_sentence"] for s in sorted(
142 translation["beams"],
143 key=lambda b: -1 * b["score"])
144 ]
145 )
146 )
147 result = "<p>" + "<hr/>".join(translationStrings) + "</p>"
148 112
149 else: 113 else:
150 result = response 114 result = response
151 return result, ok 115 return result, ok

eric ide

mercurial