Feat: Replaced old translator module. The new translator can translate by using LibreTranslate or DeepL with an user provided API key

This commit is contained in:
Manuel Cortez 2024-05-15 13:56:30 -06:00
parent a1eb546f23
commit ee4f254825
No known key found for this signature in database
GPG Key ID: 9E0735CA15EFE790
9 changed files with 149 additions and 134 deletions

View File

@ -13,11 +13,13 @@ cx-Freeze==7.0.0
cx-Logging==3.2.0
decorator==5.1.1
demoji==1.1.0
deepl==1.18.0
future==1.0.0
idna==3.7
importlib-metadata==7.1.0
iniconfig==2.0.0
libloader @ git+https://github.com/accessibleapps/libloader@bc94811c095b2e57a036acd88660be9a33260267
libretranslatepy==2.1.4
lief==0.14.1
Markdown==3.6
Mastodon.py==1.8.1

View File

@ -28,4 +28,9 @@ type = integer(default=0)
server = string(default="")
port = integer(default=8080)
user = string(default="")
password = string(default="")
password = string(default="")
[translator]
engine=string(default="deepl")
translator_api_url=string(default="https://translate.nvda.es")
translator_api_key=string(default="")

View File

@ -1,26 +1,21 @@
# -*- coding: utf-8 -*-
import widgetUtils
import output
import config
from extra import SpellChecker
from extra.translator import TranslatorController
class basicMessage(object):
def translate(self, event=None):
pass
# dlg = translator.gui.translateDialog()
# if dlg.get_response() == widgetUtils.OK:
# text_to_translate = self.message.text.GetValue()
# language_dict = translator.translator.available_languages()
# for k in language_dict:
# if language_dict[k] == dlg.dest_lang.GetStringSelection():
# dst = k
# msg = translator.translator.translate(text=text_to_translate, target=dst)
# self.message.text.ChangeValue(msg)
# self.message.text.SetInsertionPoint(len(self.message.text.GetValue()))
# self.text_processor()
# self.message.text.SetFocus()
# output.speak(_(u"Translated"))
# else:
# return
t = TranslatorController(self.message.text.GetValue())
if t.response == False:
return
msg = t.translate()
self.message.text.ChangeValue(msg)
self.message.text.SetInsertionPoint(len(self.message.text.GetValue()))
self.text_processor()
self.message.text.SetFocus()
output.speak(_(u"Translated"))
def text_processor(self, *args, **kwargs):
pass

View File

@ -1,5 +1,2 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import unicode_literals
from . import translator
from . import wx_ui as gui
from .translator import TranslatorController

View File

@ -0,0 +1 @@
# -*- coding: utf-8 -*-

View File

@ -0,0 +1,14 @@
# -*- coding: utf-8 -*-
import config
from deepl import Translator
def translate(text: str, target_language: str) -> str:
key = config.app["translator"]["translator_api_key"]
t = Translator(key)
return t.translate_text(text, target_lang=target_language).text
def languages():
key = config.app["translator"]["translator_api_key"]
t = Translator(key)
langs = t.get_target_languages()
return langs

View File

@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
""" Modified Libretranslatepy module which adds an user agent for making requests against more instances. """
import json
from typing import Any, Dict
from urllib import request, parse
from libretranslatepy import LibreTranslateAPI
class CustomLibreTranslateAPI(LibreTranslateAPI):
USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
def _create_request(self, url: str, method: str, data: Dict[str, str]) -> request.Request:
url_params = parse.urlencode(data)
req = request.Request(url, method=method, data=url_params.encode())
req.add_header("User-Agent", self.USER_AGENT)
return req
def translate(self, q: str, source: str = "en", target: str = "es", timeout: int | None = None) -> Any:
url = self.url + "translate"
params: Dict[str, str] = {"q": q, "source": source, "target": target}
if self.api_key is not None:
params["api_key"] = self.api_key
req = self._create_request(url=url, method="POST", data=params)
response = request.urlopen(req, timeout=timeout)
response_str = response.read().decode()
return json.loads(response_str)["translatedText"]
def detect(self, q: str, timeout: int | None = None) -> Any:
url = self.url + "detect"
params: Dict[str, str] = {"q": q}
if self.api_key is not None:
params["api_key"] = self.api_key
req = self._create_request(url=url, method="POST", data=params)
response = request.urlopen(req, timeout=timeout)
response_str = response.read().decode()
return json.loads(response_str)
def languages(self, timeout: int | None = None) -> Any:
url = self.url + "languages"
params: Dict[str, str] = dict()
if self.api_key is not None:
params["api_key"] = self.api_key
req = self._create_request(url=url, method="GET", data=params)
response = request.urlopen(req, timeout=timeout)
response_str = response.read().decode()
return json.loads(response_str)

View File

@ -1,116 +1,61 @@
# -*- coding: utf-8 -*-
import logging
from googletrans import Translator, LANGUAGES
import threading
import wx
import config
from pubsub import pub
from . engines import libre_translate, deep_l
from .wx_ui import translateDialog
log = logging.getLogger("extras.translator")
# create a single translator instance
# see https://github.com/ssut/py-googletrans/issues/234
t = None
class TranslatorController(object):
def __init__(self, text):
super(TranslatorController, self).__init__()
self.text = text
self.languages = []
self.response = False
self.dialog = translateDialog()
pub.subscribe(self.on_engine_changed, "translator.engine_changed")
if config.app["translator"]["engine"] == "libretranslate":
self.dialog.engine_select.SetSelection(0)
elif config.app["translator"]["engine"] == "deepl":
self.dialog.engine_select.SetSelection(1)
threading.Thread(target=self.load_languages).start()
if self.dialog.ShowModal() == wx.ID_OK:
self.response = True
for k in self.language_dict:
if self.language_dict[k] == self.dialog.dest_lang.GetStringSelection():
self.target_language= k
pub.unsubscribe(self.on_engine_changed, "translator.engine_changed")
def translate(text="", target="en"):
global t
log.debug("Received translation request for language %s, text=%s" % (target, text))
if t == None:
t = Translator()
vars = dict(text=text, dest=target)
return t.translate(**vars).text
def load_languages(self):
self.language_dict = self.get_languages()
self.languages = [self.language_dict[k] for k in self.language_dict]
self.dialog.set_languages(self.languages)
supported_langs = None
def on_engine_changed(self, engine):
if engine == "LibreTranslate":
config.app["translator"]["engine"] = engine.lower()
elif engine == "DeepL":
config.app["translator"]["engine"] = engine.lower()
config.app.write()
threading.Thread(target=self.load_languages).start()
languages = {
"af": _(u"Afrikaans"),
"sq": _(u"Albanian"),
"am": _(u"Amharic"),
"ar": _(u"Arabic"),
"hy": _(u"Armenian"),
"az": _(u"Azerbaijani"),
"eu": _(u"Basque"),
"be": _(u"Belarusian"),
"bn": _(u"Bengali"),
"bh": _(u"Bihari"),
"bg": _(u"Bulgarian"),
"my": _(u"Burmese"),
"ca": _(u"Catalan"),
"chr": _(u"Cherokee"),
"zh": _(u"Chinese"),
"zh-CN": _(u"Chinese_simplified"),
"zh-TW": _(u"Chinese_traditional"),
"hr": _(u"Croatian"),
"cs": _(u"Czech"),
"da": _(u"Danish"),
"dv": _(u"Dhivehi"),
"nl": _(u"Dutch"),
"en": _(u"English"),
"eo": _(u"Esperanto"),
"et": _(u"Estonian"),
"tl": _(u"Filipino"),
"fi": _(u"Finnish"),
"fr": _(u"French"),
"gl": _(u"Galician"),
"ka": _(u"Georgian"),
"de": _(u"German"),
"el": _(u"Greek"),
"gn": _(u"Guarani"),
"gu": _(u"Gujarati"),
"iw": _(u"Hebrew"),
"hi": _(u"Hindi"),
"hu": _(u"Hungarian"),
"is": _(u"Icelandic"),
"id": _(u"Indonesian"),
"iu": _(u"Inuktitut"),
"ga": _(u"Irish"),
"it": _(u"Italian"),
"ja": _(u"Japanese"),
"kn": _(u"Kannada"),
"kk": _(u"Kazakh"),
"km": _(u"Khmer"),
"ko": _(u"Korean"),
"ku": _(u"Kurdish"),
"ky": _(u"Kyrgyz"),
"lo": _(u"Laothian"),
"lv": _(u"Latvian"),
"lt": _(u"Lithuanian"),
"mk": _(u"Macedonian"),
"ms": _(u"Malay"),
"ml": _(u"Malayalam"),
"mt": _(u"Maltese"),
"mr": _(u"Marathi"),
"mn": _(u"Mongolian"),
"ne": _(u"Nepali"),
"no": _(u"Norwegian"),
"or": _(u"Oriya"),
"ps": _(u"Pashto"),
"fa": _(u"Persian"),
"pl": _(u"Polish"),
"pt": _(u"Portuguese"),
"pa": _(u"Punjabi"),
"ro": _(u"Romanian"),
"ru": _(u"Russian"),
"sa": _(u"Sanskrit"),
"sr": _(u"Serbian"),
"sd": _(u"Sindhi"),
"si": _(u"Sinhalese"),
"sk": _(u"Slovak"),
"sl": _(u"Slovenian"),
"es": _(u"Spanish"),
"sw": _(u"Swahili"),
"sv": _(u"Swedish"),
"tg": _(u"Tajik"),
"ta": _(u"Tamil"),
"tl": _(u"Tagalog"),
"te": _(u"Telugu"),
"th": _(u"Thai"),
"bo": _(u"Tibetan"),
"tr": _(u"Turkish"),
"uk": _(u"Ukrainian"),
"ur": _(u"Urdu"),
"uz": _(u"Uzbek"),
"ug": _(u"Uighur"),
"vi": _(u"Vietnamese"),
"cy": _(u"Welsh"),
"yi": _(u"Yiddish")
}
def translate(self):
log.debug("Received translation request for language %s, text=%s" % (self.target_language, self.text))
if config.app["translator"].get("engine") == "libretranslate":
translator = libre_translate.CustomLibreTranslateAPI(config.app["translator"]["translator_api_url"])
vars = dict(q=self.text, target=self.target_language)
return translator.translate(**vars)
elif config.app["translator"]["engine"] == "deepl" and config.app["translator"]["translator_api_key"] != "":
return deep_l.translate(text=self.text, target_language=self.target_language)
def available_languages():
return dict(sorted(languages.items(), key=lambda x: x[1]))
def get_languages(self):
languages = {}
if config.app["translator"].get("engine") == "libretranslate":
translator = libre_translate.CustomLibreTranslateAPI(config.app["translator"]["translator_api_url"])
languages = {l.get("code"): l.get("name") for l in translator.languages()}
elif config.app["translator"]["engine"] == "deepl" and config.app["translator"]["translator_api_key"] != "":
languages = {language.code: language.name for language in deep_l.languages()}
return dict(sorted(languages.items(), key=lambda x: x[1]))

View File

@ -16,23 +16,26 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
############################################################
from . import translator
import wx
from pubsub import pub
from wxUI.dialogs import baseDialog
class translateDialog(baseDialog.BaseWXDialog):
def __init__(self):
languages = []
language_dict = translator.available_languages()
for k in language_dict:
languages.append(language_dict[k])
super(translateDialog, self).__init__(None, -1, title=_(u"Translate message"))
self.engines = ["LibreTranslate", "DeepL"]
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
staticEngine = wx.StaticText(panel, -1, _(u"Translation engine"))
self.engine_select = wx.ComboBox(panel, -1, choices=self.engines, style=wx.CB_READONLY)
self.engine_select.Bind(wx.EVT_COMBOBOX, lambda event: pub.sendMessage("translator.engine_changed", engine=self.engine_select.GetValue()))
staticDest = wx.StaticText(panel, -1, _(u"Target language"))
self.dest_lang = wx.ComboBox(panel, -1, choices=languages, style = wx.CB_READONLY)
self.dest_lang = wx.ComboBox(panel, -1, style = wx.CB_READONLY)
self.dest_lang.SetFocus()
self.dest_lang.SetSelection(0)
engineSizer = wx.BoxSizer(wx.HORIZONTAL)
engineSizer.Add(staticEngine)
engineSizer.Add(self.engine_select)
listSizer = wx.BoxSizer(wx.HORIZONTAL)
listSizer.Add(staticDest)
listSizer.Add(self.dest_lang)
@ -40,6 +43,14 @@ class translateDialog(baseDialog.BaseWXDialog):
ok.SetDefault()
cancel = wx.Button(panel, wx.ID_CANCEL)
self.SetEscapeId(wx.ID_CANCEL)
sizer.Add(engineSizer, 0, wx.EXPAND | wx.ALL, 5)
sizer.Add(listSizer, 0, wx.EXPAND | wx.ALL, 5)
sizer.Add(ok, 0, wx.ALIGN_CENTER | wx.ALL, 5)
sizer.Add(cancel, 0, wx.ALIGN_CENTER | wx.ALL, 5)
panel.SetSizer(sizer)
def set_languages(self, languages):
wx.CallAfter(self.dest_lang.SetItems, languages)
def get(self, control):
return getattr(self, control).GetSelection()