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 |
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 |