|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2017 - 2022 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing the DeepL translation engine. |
|
8 """ |
|
9 |
|
10 import json |
|
11 |
|
12 from PyQt6.QtCore import QUrl, QByteArray, QTimer |
|
13 |
|
14 import Utilities |
|
15 |
|
16 from .TranslationEngine import TranslationEngine |
|
17 |
|
18 |
|
19 class DeepLEngine(TranslationEngine): |
|
20 """ |
|
21 Class implementing the translation engine for the DeepL |
|
22 translation service. |
|
23 """ |
|
24 TranslatorUrls = { |
|
25 "pro": "https://api.deepl.com/v2/translate", |
|
26 "free": "https://api-free.deepl.com/v2/translate", |
|
27 } |
|
28 MaxTranslationTextLen = 30 * 1024 |
|
29 |
|
30 def __init__(self, plugin, parent=None): |
|
31 """ |
|
32 Constructor |
|
33 |
|
34 @param plugin reference to the plugin object |
|
35 @type TranslatorPlugin |
|
36 @param parent reference to the parent object |
|
37 @type QObject |
|
38 """ |
|
39 super().__init__(plugin, parent) |
|
40 |
|
41 QTimer.singleShot(0, self.availableTranslationsLoaded.emit) |
|
42 |
|
43 def engineName(self): |
|
44 """ |
|
45 Public method to return the name of the engine. |
|
46 |
|
47 @return engine name |
|
48 @rtype str |
|
49 """ |
|
50 return "deepl" |
|
51 |
|
52 def supportedLanguages(self): |
|
53 """ |
|
54 Public method to get the supported languages. |
|
55 |
|
56 @return list of supported language codes |
|
57 @rtype list of str |
|
58 """ |
|
59 return ["bg", "cs", "da", "de", "el", "en", "es", "et", "fi", "fr", |
|
60 "hu", "id", "it", "ja", "lt", "lv", "nl", "pl", "pt", "ro", |
|
61 "ru", "sk", "sl", "sv", "tr", "zh"] |
|
62 |
|
63 def getTranslation(self, requestObject, text, originalLanguage, |
|
64 translationLanguage): |
|
65 """ |
|
66 Public method to translate the given text. |
|
67 |
|
68 @param requestObject reference to the request object |
|
69 @type TranslatorRequest |
|
70 @param text text to be translated |
|
71 @type str |
|
72 @param originalLanguage language code of the original |
|
73 @type str |
|
74 @param translationLanguage language code of the translation |
|
75 @type str |
|
76 @return tuple of translated text and flag indicating success |
|
77 @rtype tuple of (str, bool) |
|
78 """ |
|
79 if len(text) > self.MaxTranslationTextLen: |
|
80 return ( |
|
81 self.tr("DeepL: Text to be translated exceeds the translation" |
|
82 " limit of {0} characters.") |
|
83 .format(self.MaxTranslationTextLen), |
|
84 False |
|
85 ) |
|
86 |
|
87 apiKey = self.plugin.getPreferences("DeeplKey") |
|
88 if not apiKey: |
|
89 return self.tr("A valid DeepL Pro key is required."), False |
|
90 |
|
91 params = QByteArray( |
|
92 "auth_key={0}&source_lang={1}&target_lang={2}&text=".format( |
|
93 apiKey, originalLanguage.upper(), translationLanguage.upper()) |
|
94 .encode("utf-8")) |
|
95 encodedText = ( |
|
96 QByteArray(Utilities.html_encode(text).encode("utf-8")) |
|
97 .toPercentEncoding() |
|
98 ) |
|
99 request = params + encodedText |
|
100 translatorUrl = ( |
|
101 DeepLEngine.TranslatorUrls["free"] |
|
102 if apiKey.endswith(":fx") else |
|
103 DeepLEngine.TranslatorUrls["pro"] |
|
104 ) |
|
105 response, ok = requestObject.post(QUrl(translatorUrl), request) |
|
106 if ok: |
|
107 try: |
|
108 responseDict = json.loads(response) |
|
109 except ValueError: |
|
110 return self.tr("Invalid response received from DeepL"), False |
|
111 |
|
112 if "translations" not in responseDict: |
|
113 return self.tr("DeepL call returned an unknown result"), False |
|
114 |
|
115 translations = responseDict["translations"] |
|
116 if len(translations) == 0: |
|
117 return self.tr("<p>DeepL: No translation found</p>"), True |
|
118 |
|
119 # show sentence by sentence separated by a line |
|
120 result = ( |
|
121 "<p>" + |
|
122 "<hr/>".join([t["text"] for t in translations]) + |
|
123 "</p>" |
|
124 ) |
|
125 |
|
126 else: |
|
127 result = response |
|
128 return result, ok |