fix localization bug, add opption to select language translating from, and add error message to devtools deepl

This commit is contained in:
Akash Mozumdar 2021-01-21 07:07:09 -07:00
parent 084f26a72d
commit e00b831e3d
13 changed files with 81 additions and 46 deletions

View File

@ -1,7 +1,6 @@
#include "mainwindow.h" #include "mainwindow.h"
#include "module.h" #include "module.h"
#include <winhttp.h> #include <winhttp.h>
#include <QApplication>
extern const wchar_t* UPDATE_AVAILABLE; extern const wchar_t* UPDATE_AVAILABLE;

View File

@ -1,9 +1,9 @@
#include "network.h" #include "qtcommon.h"
#include <QStringList> #include "network.h"
extern const wchar_t* TRANSLATION_ERROR; extern const wchar_t* TRANSLATION_ERROR;
extern Synchronized<std::wstring> translateTo, authKey; extern Synchronized<std::wstring> translateTo, translateFrom, authKey;
const char* TRANSLATION_PROVIDER = "Bing Translate"; const char* TRANSLATION_PROVIDER = "Bing Translate";
const char* GET_API_KEY_FROM = "https://www.microsoft.com/en-us/translator/business/trial/#get-started"; const char* GET_API_KEY_FROM = "https://www.microsoft.com/en-us/translator/business/trial/#get-started";
@ -78,6 +78,7 @@ QStringList languages
"Welsh: cy", "Welsh: cy",
"Yucatec Maya: yua" "Yucatec Maya: yua"
}; };
std::wstring autoDetectLanguage = L"auto-detect";
bool translateSelectedOnly = false, rateLimitAll = true, rateLimitSelected = false, useCache = true; bool translateSelectedOnly = false, rateLimitAll = true, rateLimitSelected = false, useCache = true;
int tokenCount = 30, tokenRestoreDelay = 60000, maxSentenceSize = 1000; int tokenCount = 30, tokenRestoreDelay = 60000, maxSentenceSize = 1000;
@ -85,23 +86,26 @@ int tokenCount = 30, tokenRestoreDelay = 60000, maxSentenceSize = 1000;
std::pair<bool, std::wstring> Translate(const std::wstring& text) std::pair<bool, std::wstring> Translate(const std::wstring& text)
{ {
if (!authKey->empty()) if (!authKey->empty())
{
std::wstring translateFromComponent = translateFrom.Copy() == autoDetectLanguage ? L"" : L"&from=" + translateFrom.Copy();
if (HttpRequest httpRequest{ if (HttpRequest httpRequest{
L"Mozilla/5.0 Textractor", L"Mozilla/5.0 Textractor",
L"api.cognitive.microsofttranslator.com", L"api.cognitive.microsofttranslator.com",
L"POST", L"POST",
FormatString(L"/translate?api-version=3.0&to=%s", translateTo.Copy()).c_str(), FormatString(L"/translate?api-version=3.0&to=%s%s", translateTo.Copy(), translateFromComponent).c_str(),
FormatString(R"([{"text":"%s"}])", JSON::Escape(WideStringToString(text))), FormatString(R"([{"text":"%s"}])", JSON::Escape(WideStringToString(text))),
FormatString(L"Content-Type: application/json; charset=UTF-8\r\nOcp-Apim-Subscription-Key:%s", authKey.Copy()).c_str() FormatString(L"Content-Type: application/json; charset=UTF-8\r\nOcp-Apim-Subscription-Key:%s", authKey.Copy()).c_str()
}) })
if (auto translation = Copy(JSON::Parse(httpRequest.response)[0][L"translations"][0][L"text"].String())) return { true, translation.value() }; if (auto translation = Copy(JSON::Parse(httpRequest.response)[0][L"translations"][0][L"text"].String())) return { true, translation.value() };
else return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, httpRequest.response) }; else return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, httpRequest.response) };
else return { false, FormatString(L"%s (code=%u)", TRANSLATION_ERROR, httpRequest.errorCode) }; else return { false, FormatString(L"%s (code=%u)", TRANSLATION_ERROR, httpRequest.errorCode) };
}
if (HttpRequest httpRequest{ if (HttpRequest httpRequest{
L"Mozilla/5.0 Textractor", L"Mozilla/5.0 Textractor",
L"www.bing.com", L"www.bing.com",
L"POST", L"POST",
FormatString(L"/ttranslatev3?fromLang=auto-detect&to=%s&text=%s", translateTo.Copy(), Escape(text)).c_str() FormatString(L"/ttranslatev3?fromLang=%s&to=%s&text=%s", translateFrom.Copy(), translateTo.Copy(), Escape(text)).c_str()
}) })
if (auto translation = Copy(JSON::Parse(httpRequest.response)[0][L"translations"][0][L"text"].String())) return { true, translation.value() }; if (auto translation = Copy(JSON::Parse(httpRequest.response)[0][L"translations"][0][L"text"].String())) return { true, translation.value() };
else return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, httpRequest.response) }; else return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, httpRequest.response) };

View File

@ -5,7 +5,7 @@
extern const wchar_t* TRANSLATION_ERROR; extern const wchar_t* TRANSLATION_ERROR;
extern const char* USE_PREV_SENTENCE_CONTEXT; extern const char* USE_PREV_SENTENCE_CONTEXT;
extern Synchronized<std::wstring> translateTo, authKey; extern Synchronized<std::wstring> translateTo, translateFrom, authKey;
const char* TRANSLATION_PROVIDER = "DeepL Translate"; const char* TRANSLATION_PROVIDER = "DeepL Translate";
const char* GET_API_KEY_FROM = "https://www.deepl.com/pro.html"; const char* GET_API_KEY_FROM = "https://www.deepl.com/pro.html";
@ -23,6 +23,7 @@ QStringList languages
"Russian: RU", "Russian: RU",
"Spanish: ES", "Spanish: ES",
}; };
std::wstring autoDetectLanguage = L"auto";
bool translateSelectedOnly = true, rateLimitAll = true, rateLimitSelected = true, useCache = true; bool translateSelectedOnly = true, rateLimitAll = true, rateLimitSelected = true, useCache = true;
int tokenCount = 10, tokenRestoreDelay = 60000, maxSentenceSize = 1000; int tokenCount = 10, tokenRestoreDelay = 60000, maxSentenceSize = 1000;
@ -33,25 +34,28 @@ int keyType = CAT;
std::pair<bool, std::wstring> Translate(const std::wstring& text) std::pair<bool, std::wstring> Translate(const std::wstring& text)
{ {
if (!authKey->empty()) if (!authKey->empty())
{
std::string translateFromComponent = translateFrom.Copy() == autoDetectLanguage ? "" : "&source_lang=" + WideStringToString(translateFrom.Copy());
if (HttpRequest httpRequest{ if (HttpRequest httpRequest{
L"Mozilla/5.0 Textractor", L"Mozilla/5.0 Textractor",
L"api.deepl.com", L"api.deepl.com",
L"POST", L"POST",
keyType == CAT ? L"/v1/translate" : L"/v2/translate", keyType == CAT ? L"/v1/translate" : L"/v2/translate",
FormatString("text=%S&auth_key=%S&target_lang=%S", Escape(text), authKey.Copy(), translateTo.Copy()), FormatString("text=%S&auth_key=%S&target_lang=%S", Escape(text), authKey.Copy(), translateTo.Copy()) + translateFromComponent,
L"Content-Type: application/x-www-form-urlencoded" L"Content-Type: application/x-www-form-urlencoded"
}; httpRequest && (!httpRequest.response.empty() || (httpRequest = HttpRequest{ }; httpRequest && (!httpRequest.response.empty() || (httpRequest = HttpRequest{
L"Mozilla/5.0 Textractor", L"Mozilla/5.0 Textractor",
L"api.deepl.com", L"api.deepl.com",
L"POST", L"POST",
(keyType = !keyType) == CAT ? L"/v1/translate" : L"/v2/translate", (keyType = !keyType) == CAT ? L"/v1/translate" : L"/v2/translate",
FormatString("text=%S&auth_key=%S&target_lang=%S", Escape(text), authKey.Copy(), translateTo.Copy()), FormatString("text=%S&auth_key=%S&target_lang=%S", Escape(text), authKey.Copy(), translateTo.Copy()) + translateFromComponent,
L"Content-Type: application/x-www-form-urlencoded" L"Content-Type: application/x-www-form-urlencoded"
}))) })))
// Response formatted as JSON: translation starts with text":" and ends with "}] // Response formatted as JSON: translation starts with text":" and ends with "}]
if (auto translation = Copy(JSON::Parse(httpRequest.response)[L"translations"][0][L"text"].String())) return { true, translation.value() }; if (auto translation = Copy(JSON::Parse(httpRequest.response)[L"translations"][0][L"text"].String())) return { true, translation.value() };
else return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, httpRequest.response) }; else return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, httpRequest.response) };
else return { false, FormatString(L"%s (code=%u)", TRANSLATION_ERROR, httpRequest.errorCode) }; else return { false, FormatString(L"%s (code=%u)", TRANSLATION_ERROR, httpRequest.errorCode) };
}
// the following code was reverse engineered from the DeepL website; it's as close as I could make it but I'm not sure what parts of this could be removed and still have it work // the following code was reverse engineered from the DeepL website; it's as close as I could make it but I'm not sure what parts of this could be removed and still have it work
int64_t r = _time64(nullptr), n = std::count(text.begin(), text.end(), L'i') + 1; int64_t r = _time64(nullptr), n = std::count(text.begin(), text.end(), L'i') + 1;
@ -68,7 +72,7 @@ std::pair<bool, std::wstring> Translate(const std::wstring& text)
"timestamp": %lld, "timestamp": %lld,
"lang": { "lang": {
"target_lang": "%S", "target_lang": "%S",
"source_lang_user_selected": "auto" "source_lang_user_selected": "%S"
}, },
"jobs": [{ "jobs": [{
"raw_en_sentence": "%s", "raw_en_sentence": "%s",
@ -80,7 +84,7 @@ std::pair<bool, std::wstring> Translate(const std::wstring& text)
}] }]
} }
} }
)", id, r + (n - r % n), translateTo.Copy(), JSON::Escape(WideStringToString(text))); )", id, r + (n - r % n), translateTo.Copy(), translateFrom.Copy(), JSON::Escape(WideStringToString(text)));
// missing accept-encoding header since it fucks up HttpRequest // missing accept-encoding header since it fucks up HttpRequest
if (HttpRequest httpRequest{ if (HttpRequest httpRequest{
L"Mozilla/5.0 Textractor", L"Mozilla/5.0 Textractor",

View File

@ -3,7 +3,7 @@
#include <ShlObj.h> #include <ShlObj.h>
extern const wchar_t* TRANSLATION_ERROR; extern const wchar_t* TRANSLATION_ERROR;
extern Synchronized<std::wstring> translateTo; extern Synchronized<std::wstring> translateTo, translateFrom;
extern QFormLayout* display; extern QFormLayout* display;
extern Settings settings; extern Settings settings;
@ -34,6 +34,7 @@ QStringList languages
"Russian: ru", "Russian: ru",
"Spanish: es", "Spanish: es",
}; };
std::wstring autoDetectLanguage = L"auto";
BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{ {
@ -115,10 +116,16 @@ std::pair<bool, std::wstring> Translate(const std::wstring& text)
// DevTools can't handle concurrent translations yet // DevTools can't handle concurrent translations yet
static std::mutex translationMutex; static std::mutex translationMutex;
std::scoped_lock lock(translationMutex); std::scoped_lock lock(translationMutex);
DevTools::SendRequest(L"Page.navigate", FormatString(LR"({"url":"https://www.deepl.com/translator#any/%s/%s"})", translateTo.Copy(), Escape(text))); DevTools::SendRequest(L"Page.navigate", FormatString(LR"({"url":"https://www.deepl.com/translator#%s/%s/%s"})", translateFrom.Copy(), translateTo.Copy(), Escape(text)));
for (int retry = 0; ++retry < 100; Sleep(100)) for (int retry = 0; ++retry < 100; Sleep(100))
if (auto translation = Copy(DevTools::SendRequest( if (auto translation = Copy(
L"Runtime.evaluate", LR"({"expression":"document.querySelector('#target-dummydiv').innerHTML","returnByValue":true})")[L"result"][L"value"].String()) DevTools::SendRequest(L"Runtime.evaluate", LR"({"expression":"document.querySelector('#target-dummydiv').innerHTML","returnByValue":true})")[L"result"][L"value"].String()
) if (!translation->empty()) return { true, translation.value() }; )) if (!translation->empty()) return { true, translation.value() };
if (auto errorMessage = Copy(
DevTools::SendRequest(
L"Runtime.evaluate",
LR"({"expression":"document.querySelector('div.lmt__system_notification').innerHTML","returnByValue":true})"
)[L"result"][L"value"].String()
)) return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, errorMessage.value()) };
return { false, TRANSLATION_ERROR }; return { false, TRANSLATION_ERROR };
} }

View File

@ -42,11 +42,10 @@ QColor colorPrompt(QWidget* parent, QColor default, const QString& title, bool c
return color; return color;
} }
struct PrettyWindow : QDialog struct PrettyWindow : QDialog, Localizer
{ {
PrettyWindow(const char* name) PrettyWindow(const char* name)
{ {
Localize();
ui.setupUi(this); ui.setupUi(this);
ui.display->setGraphicsEffect(&outliner); ui.display->setGraphicsEffect(&outliner);
setWindowFlags(Qt::FramelessWindowHint); setWindowFlags(Qt::FramelessWindowHint);
@ -159,7 +158,7 @@ public:
PrettyWindow("Extra Window") PrettyWindow("Extra Window")
{ {
ui.display->setTextFormat(Qt::PlainText); ui.display->setTextFormat(Qt::PlainText);
if (settings.contains(WINDOW) && QGuiApplication::screenAt(settings.value(WINDOW).toRect().bottomRight())) setGeometry(settings.value(WINDOW).toRect()); if (settings.contains(WINDOW) && QApplication::screenAt(settings.value(WINDOW).toRect().bottomRight())) setGeometry(settings.value(WINDOW).toRect());
maxSentenceSize = settings.value(MAX_SENTENCE_SIZE, maxSentenceSize).toInt(); maxSentenceSize = settings.value(MAX_SENTENCE_SIZE, maxSentenceSize).toInt();
for (auto [name, default, slot] : Array<const char*, bool, void(ExtraWindow::*)(bool)>{ for (auto [name, default, slot] : Array<const char*, bool, void(ExtraWindow::*)(bool)>{

View File

@ -4,7 +4,7 @@
extern const wchar_t* TRANSLATION_ERROR; extern const wchar_t* TRANSLATION_ERROR;
extern Synchronized<std::wstring> translateTo, authKey; extern Synchronized<std::wstring> translateTo, translateFrom, authKey;
const char* TRANSLATION_PROVIDER = "Google Translate"; const char* TRANSLATION_PROVIDER = "Google Translate";
const char* GET_API_KEY_FROM = "https://codelabs.developers.google.com/codelabs/cloud-translation-intro"; const char* GET_API_KEY_FROM = "https://codelabs.developers.google.com/codelabs/cloud-translation-intro";
@ -120,6 +120,7 @@ QStringList languages
"Yoruba: yo", "Yoruba: yo",
"Zulu: zu" "Zulu: zu"
}; };
std::wstring autoDetectLanguage = L"auto";
bool translateSelectedOnly = false, rateLimitAll = true, rateLimitSelected = false, useCache = true; bool translateSelectedOnly = false, rateLimitAll = true, rateLimitSelected = false, useCache = true;
int tokenCount = 30, tokenRestoreDelay = 60000, maxSentenceSize = 1000; int tokenCount = 30, tokenRestoreDelay = 60000, maxSentenceSize = 1000;
@ -127,23 +128,28 @@ int tokenCount = 30, tokenRestoreDelay = 60000, maxSentenceSize = 1000;
std::pair<bool, std::wstring> Translate(const std::wstring& text) std::pair<bool, std::wstring> Translate(const std::wstring& text)
{ {
if (!authKey->empty()) if (!authKey->empty())
{
std::wstring translateFromComponent = translateFrom.Copy() == autoDetectLanguage ? L"" : L"&source=" + translateFrom.Copy();
if (HttpRequest httpRequest{ if (HttpRequest httpRequest{
L"Mozilla/5.0 Textractor", L"Mozilla/5.0 Textractor",
L"translation.googleapis.com", L"translation.googleapis.com",
L"POST", L"POST",
FormatString(L"/language/translate/v2?format=text&target=%s&key=%s", translateTo.Copy(), authKey.Copy()).c_str(), FormatString(L"/language/translate/v2?format=text&target=%s&key=%s%s", translateTo.Copy(), authKey.Copy(), translateFromComponent).c_str(),
FormatString(R"({"q":["%s"]})", JSON::Escape(WideStringToString(text))) FormatString(R"({"q":["%s"]})", JSON::Escape(WideStringToString(text)))
}) })
if (auto translation = Copy(JSON::Parse(httpRequest.response)[L"data"][L"translations"][0][L"translatedText"].String())) return { true, translation.value() }; if (auto translation = Copy(JSON::Parse(httpRequest.response)[L"data"][L"translations"][0][L"translatedText"].String())) return { true, translation.value() };
else return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, httpRequest.response) }; else return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, httpRequest.response) };
else return { false, FormatString(L"%s (code=%u)", TRANSLATION_ERROR, httpRequest.errorCode) }; else return { false, FormatString(L"%s (code=%u)", TRANSLATION_ERROR, httpRequest.errorCode) };
}
if (HttpRequest httpRequest{ if (HttpRequest httpRequest{
L"Mozilla/5.0 Textractor", L"Mozilla/5.0 Textractor",
L"translate.google.com", L"translate.google.com",
L"POST", L"POST",
L"/_/TranslateWebserverUi/data/batchexecute?rpcids=MkEWBc", L"/_/TranslateWebserverUi/data/batchexecute?rpcids=MkEWBc",
"f.req=" + Escape(WideStringToString(FormatString(LR"([[["MkEWBc","[[\"%s\",\"auto\",\"%s\",true],[null]]",null,"generic"]]])", JSON::Escape((JSON::Escape(text))), translateTo.Copy()))), "f.req=" + Escape(WideStringToString(
FormatString(LR"([[["MkEWBc","[[\"%s\",\"%s\",\"%s\",true],[null]]",null,"generic"]]])", JSON::Escape((JSON::Escape(text))), translateFrom.Copy(), translateTo.Copy())
)),
L"Content-Type: application/x-www-form-urlencoded" L"Content-Type: application/x-www-form-urlencoded"
}) })
{ {

View File

@ -41,13 +41,12 @@ bool logErrors = true;
Synchronized<std::string> script; Synchronized<std::string> script;
std::atomic<int> revCount = 0; std::atomic<int> revCount = 0;
class Window : public QDialog class Window : public QDialog, Localizer
{ {
public: public:
Window() Window()
: QDialog(nullptr, Qt::WindowMinMaxButtonsHint) : QDialog(nullptr, Qt::WindowMinMaxButtonsHint)
{ {
Localize();
connect(&loadButton, &QPushButton::clicked, this, &Window::LoadScript); connect(&loadButton, &QPushButton::clicked, this, &Window::LoadScript);
if (scriptEditor.toPlainText().isEmpty()) scriptEditor.setPlainText(LUA_INTRO); if (scriptEditor.toPlainText().isEmpty()) scriptEditor.setPlainText(LUA_INTRO);

View File

@ -16,13 +16,12 @@ std::wstring replace;
std::shared_mutex m; std::shared_mutex m;
DWORD (*GetSelectedProcessId)() = nullptr; DWORD (*GetSelectedProcessId)() = nullptr;
class Window : public QDialog class Window : public QDialog, Localizer
{ {
public: public:
Window() Window()
: QDialog(nullptr, Qt::WindowMinMaxButtonsHint) : QDialog(nullptr, Qt::WindowMinMaxButtonsHint)
{ {
Localize();
ui.setupUi(this); ui.setupUi(this);
connect(ui.regexEdit, &QLineEdit::textEdited, this, &Window::SetRegex); connect(ui.regexEdit, &QLineEdit::textEdited, this, &Window::SetRegex);

View File

@ -1,20 +1,18 @@
#include "qtcommon.h" #include "qtcommon.h"
#include "extension.h" #include "extension.h"
#include <fstream> #include <fstream>
#include <QApplication>
#include <QPlainTextEdit> #include <QPlainTextEdit>
extern const char* LOAD_SCRIPT; extern const char* LOAD_SCRIPT;
constexpr auto STYLE_SAVE_FILE = u8"Textractor.css"; constexpr auto STYLE_SAVE_FILE = u8"Textractor.css";
class Window : public QDialog class Window : public QDialog, Localizer
{ {
public: public:
Window() Window()
: QDialog(nullptr, Qt::WindowMinMaxButtonsHint) : QDialog(nullptr, Qt::WindowMinMaxButtonsHint)
{ {
Localize();
connect(&loadButton, &QPushButton::clicked, this, &Window::LoadScript); connect(&loadButton, &QPushButton::clicked, this, &Window::LoadScript);
if (scriptEditor.toPlainText().isEmpty()) scriptEditor.setPlainText("/*https://doc.qt.io/qt-5/stylesheet-syntax.html*/"); if (scriptEditor.toPlainText().isEmpty()) scriptEditor.setPlainText("/*https://doc.qt.io/qt-5/stylesheet-syntax.html*/");

View File

@ -11,13 +11,12 @@ extern const char* HEXADECIMAL;
std::unordered_map<int64_t, std::unordered_multiset<int64_t>> linkedTextHandles; std::unordered_map<int64_t, std::unordered_multiset<int64_t>> linkedTextHandles;
std::shared_mutex m; std::shared_mutex m;
class Window : public QDialog class Window : public QDialog, Localizer
{ {
public: public:
Window() Window()
: QDialog(nullptr, Qt::WindowMinMaxButtonsHint) : QDialog(nullptr, Qt::WindowMinMaxButtonsHint)
{ {
Localize();
connect(&linkButton, &QPushButton::clicked, this, &Window::Link); connect(&linkButton, &QPushButton::clicked, this, &Window::Link);
layout.addWidget(&linkList); layout.addWidget(&linkList);

View File

@ -8,6 +8,7 @@
extern const char* NATIVE_LANGUAGE; extern const char* NATIVE_LANGUAGE;
extern const char* TRANSLATE_TO; extern const char* TRANSLATE_TO;
extern const char* TRANSLATE_FROM;
extern const char* TRANSLATE_SELECTED_THREAD_ONLY; extern const char* TRANSLATE_SELECTED_THREAD_ONLY;
extern const char* RATE_LIMIT_ALL_THREADS; extern const char* RATE_LIMIT_ALL_THREADS;
extern const char* RATE_LIMIT_SELECTED_THREAD; extern const char* RATE_LIMIT_SELECTED_THREAD;
@ -21,16 +22,18 @@ extern const wchar_t* TOO_MANY_TRANS_REQUESTS;
extern const char* TRANSLATION_PROVIDER; extern const char* TRANSLATION_PROVIDER;
extern const char* GET_API_KEY_FROM; extern const char* GET_API_KEY_FROM;
extern QStringList languages; extern QStringList languages;
extern std::wstring autoDetectLanguage;
extern bool translateSelectedOnly, rateLimitAll, rateLimitSelected, useCache; extern bool translateSelectedOnly, rateLimitAll, rateLimitSelected, useCache;
extern int tokenCount, tokenRestoreDelay, maxSentenceSize; extern int tokenCount, tokenRestoreDelay, maxSentenceSize;
std::pair<bool, std::wstring> Translate(const std::wstring& text); std::pair<bool, std::wstring> Translate(const std::wstring& text);
// backwards compatibility
const char* LANGUAGE = u8"Language"; const char* LANGUAGE = u8"Language";
const std::string TRANSLATION_CACHE_FILE = FormatString("%s Translation Cache.txt", TRANSLATION_PROVIDER); const std::string TRANSLATION_CACHE_FILE = FormatString("%s Translation Cache.txt", TRANSLATION_PROVIDER);
QFormLayout* display; QFormLayout* display;
Settings settings; Settings settings;
Synchronized<std::wstring> translateTo = L"en", authKey; Synchronized<std::wstring> translateTo = L"en", translateFrom = L"auto", authKey;
namespace namespace
{ {
@ -46,27 +49,38 @@ namespace
} }
} }
class Window : public QDialog class Window : public QDialog, Localizer
{ {
public: public:
Window() : Window() :
QDialog(nullptr, Qt::WindowMinMaxButtonsHint) QDialog(nullptr, Qt::WindowMinMaxButtonsHint)
{ {
Localize();
display = new QFormLayout(this); display = new QFormLayout(this);
settings.beginGroup(TRANSLATION_PROVIDER); settings.beginGroup(TRANSLATION_PROVIDER);
auto languageCombo = new QComboBox(this); auto translateToCombo = new QComboBox(this);
languageCombo->addItems(languages); translateToCombo->addItems(languages);
int language = -1; int language = -1;
if (settings.contains(LANGUAGE)) language = languageCombo->findText(settings.value(LANGUAGE).toString(), Qt::MatchEndsWith); if (settings.contains(LANGUAGE)) language = translateToCombo->findText(settings.value(LANGUAGE).toString(), Qt::MatchEndsWith);
if (language < 0) language = languageCombo->findText(NATIVE_LANGUAGE, Qt::MatchStartsWith); if (settings.contains(TRANSLATE_TO)) language = translateToCombo->findText(settings.value(TRANSLATE_TO).toString(), Qt::MatchEndsWith);
if (language < 0) language = languageCombo->findText("English", Qt::MatchStartsWith); if (language < 0) language = translateToCombo->findText(NATIVE_LANGUAGE, Qt::MatchStartsWith);
languageCombo->setCurrentIndex(language); if (language < 0) language = translateToCombo->findText("English", Qt::MatchStartsWith);
saveLanguage(languageCombo->currentText()); translateToCombo->setCurrentIndex(language);
display->addRow(TRANSLATE_TO, languageCombo); SaveTranslateTo(translateToCombo->currentText());
connect(languageCombo, &QComboBox::currentTextChanged, this, &Window::saveLanguage); display->addRow(TRANSLATE_TO, translateToCombo);
connect(translateToCombo, &QComboBox::currentTextChanged, this, &Window::SaveTranslateTo);
languages.push_front("?: " + S(autoDetectLanguage));
auto translateFromCombo = new QComboBox(this);
translateFromCombo->addItems(languages);
language = -1;
if (settings.contains(TRANSLATE_FROM)) language = translateFromCombo->findText(settings.value(TRANSLATE_FROM).toString(), Qt::MatchEndsWith);
if (language < 0) language = translateFromCombo->findText(NATIVE_LANGUAGE, Qt::MatchStartsWith);
if (language < 0) language = translateFromCombo->findText("?", Qt::MatchStartsWith);
translateFromCombo->setCurrentIndex(language);
SaveTranslateFrom(translateFromCombo->currentText());
display->addRow(TRANSLATE_FROM, translateFromCombo);
connect(translateFromCombo, &QComboBox::currentTextChanged, this, &Window::SaveTranslateFrom);
for (auto [value, label] : Array<bool&, const char*>{ for (auto [value, label] : Array<bool&, const char*>{
{ translateSelectedOnly, TRANSLATE_SELECTED_THREAD_ONLY }, { translateSelectedOnly, TRANSLATE_SELECTED_THREAD_ONLY },
{ rateLimitAll, RATE_LIMIT_ALL_THREADS }, { rateLimitAll, RATE_LIMIT_ALL_THREADS },
@ -123,9 +137,13 @@ public:
} }
private: private:
void saveLanguage(QString language) void SaveTranslateTo(QString language)
{ {
settings.setValue(LANGUAGE, S(translateTo->assign(S(language.split(": ")[1])))); settings.setValue(TRANSLATE_TO, S(translateTo->assign(S(language.split(": ")[1]))));
}
void SaveTranslateFrom(QString language)
{
settings.setValue(TRANSLATE_FROM, S(translateFrom->assign(S(language.split(": ")[1]))));
} }
} window; } window;

View File

@ -9,6 +9,7 @@
#include <QSettings> #include <QSettings>
#include <QMainWindow> #include <QMainWindow>
#include <QDialog> #include <QDialog>
#include <QApplication>
#include <QLayout> #include <QLayout>
#include <QFormLayout> #include <QFormLayout>
#include <QLabel> #include <QLabel>
@ -26,6 +27,7 @@ constexpr auto WINDOW = u8"Window";
struct Settings : QSettings { Settings(QObject* parent = nullptr) : QSettings(CONFIG_FILE, QSettings::IniFormat, parent) {} }; struct Settings : QSettings { Settings(QObject* parent = nullptr) : QSettings(CONFIG_FILE, QSettings::IniFormat, parent) {} };
struct QTextFile : QFile { QTextFile(QString name, QIODevice::OpenMode mode) : QFile(name) { open(mode | QIODevice::Text); } }; struct QTextFile : QFile { QTextFile(QString name, QIODevice::OpenMode mode) : QFile(name) { open(mode | QIODevice::Text); } };
struct Localizer { Localizer() { Localize(); } };
inline std::wstring S(const QString& s) { return { s.toStdWString() }; } inline std::wstring S(const QString& s) { return { s.toStdWString() }; }
inline QString S(const std::string& s) { return QString::fromStdString(s); } inline QString S(const std::string& s) { return QString::fromStdString(s); }
inline QString S(const std::wstring& s) { return QString::fromStdWString(s); } inline QString S(const std::wstring& s) { return QString::fromStdWString(s); }

View File

@ -134,6 +134,7 @@ const char* READ_ERROR = u8"Textractor: Reader ERROR (likely an incorrect R-code
const char* HIJACK_ERROR = u8"Textractor: Hijack ERROR"; const char* HIJACK_ERROR = u8"Textractor: Hijack ERROR";
const char* COULD_NOT_FIND = u8"Textractor: could not find text"; const char* COULD_NOT_FIND = u8"Textractor: could not find text";
const char* TRANSLATE_TO = u8"Translate to"; const char* TRANSLATE_TO = u8"Translate to";
const char* TRANSLATE_FROM = u8"Translate from";
const char* TRANSLATE_SELECTED_THREAD_ONLY = u8"Translate selected text thread only"; const char* TRANSLATE_SELECTED_THREAD_ONLY = u8"Translate selected text thread only";
const char* RATE_LIMIT_ALL_THREADS = u8"Rate limit all text threads"; const char* RATE_LIMIT_ALL_THREADS = u8"Rate limit all text threads";
const char* RATE_LIMIT_SELECTED_THREAD = u8"Rate limit selected text thread"; const char* RATE_LIMIT_SELECTED_THREAD = u8"Rate limit selected text thread";