From ac95d873f1e87d7306ebd9fbe3bab08f35feba83 Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Mon, 28 Jun 2021 22:24:59 -0600 Subject: [PATCH] improve translation extension ui, add language detection for papago and systran, add more languages to systran, improve deepl translation and bing error handling --- extensions/bingtranslate.cpp | 278 +++++++++++++------ extensions/deepltranslate.cpp | 145 ++++++---- extensions/devtoolsdeepltranslate.cpp | 139 +++++++--- extensions/devtoolspapagotranslate.cpp | 66 +++-- extensions/devtoolssystrantranslate.cpp | 133 +++++++-- extensions/googletranslate.cpp | 348 ++++++++++++++++-------- extensions/translatewrapper.cpp | 56 ++-- extensions/translatewrapper.h | 6 + text.cpp | 2 +- 9 files changed, 815 insertions(+), 358 deletions(-) create mode 100644 extensions/translatewrapper.h diff --git a/extensions/bingtranslate.cpp b/extensions/bingtranslate.cpp index 3a1ca9d..7cc7964 100644 --- a/extensions/bingtranslate.cpp +++ b/extensions/bingtranslate.cpp @@ -1,100 +1,212 @@ #include "qtcommon.h" +#include "translatewrapper.h" #include "network.h" extern const wchar_t* TRANSLATION_ERROR; -extern Synchronized translateTo, translateFrom, authKey; - const char* TRANSLATION_PROVIDER = "Bing Translate"; const char* GET_API_KEY_FROM = "https://www.microsoft.com/en-us/translator/business/trial/#get-started"; -QStringList languages +extern const QStringList languagesTo { - "Afrikaans: af", - "Arabic: ar", - "Bangla: bn", - "Bosnian: bs", - "Bulgarian: bg", - "Cantonese (traditional): yue", - "Catalan: ca", - "Chinese (simplified): zh-Hans", - "Chinese (traditional): zh-Hant", - "Croatian: hr", - "Czech: cs", - "Danish: da", - "Dutch: nl", - "English: en", - "Estonian: et", - "Fijian: fj", - "Filipino: fil", - "Finnish: fi", - "French: fr", - "German: de", - "Greek: el", - "Haitian Creole: ht", - "Hebrew: he", - "Hindi: hi", - "Hmong Daw: mww", - "Hungarian: hu", - "Icelandic: is", - "Indonesian: id", - "Irish: ga", - "Italian: it", - "Japanese: ja", - "Kannada: kn", - "Klingon: tlh", - "Korean: ko", - "Latvian: lv", - "Lithuanian: lt", - "Malagasy: mg", - "Malay: ms", - "Malayalam: ml", - "Maltese: mt", - "Maori: mi", - "Norwegian: nb", - "Persian: fa", - "Polish: pl", - "Portuguese (Brazil): pt", - "Portuguese (Portugal): pt-pt", - "Punjabi: pa", - "Romanian: ro", - "Russian: ru", - "Samoan: sm", - "Serbian (Cyrillic): sr-Cyrl", - "Serbian (Latin): sr-Latn", - "Slovak: sk", - "Slovenian: sl", - "Spanish: es", - "Swahili: sw", - "Swedish: sv", - "Tahitian: ty", - "Tamil: ta", - "Telugu: te", - "Thai: th", - "Tongan: to", - "Turkish: tr", - "Ukrainian: uk", - "Urdu: ur", - "Vietnamese: vi", - "Welsh: cy", - "Yucatec Maya: yua" + "Afrikaans", + "Albanian", + "Amharic", + "Arabic", + "Armenian", + "Assamese", + "Azerbaijani", + "Bangla", + "Bosnian (Latin)", + "Bulgarian", + "Cantonese (Traditional)", + "Catalan", + "Chinese (Simplified)", + "Chinese (Traditional)", + "Croatian", + "Czech", + "Danish", + "Dari", + "Dutch", + "English", + "Estonian", + "Fijian", + "Filipino", + "Finnish", + "French", + "French (Canada)", + "German", + "Greek", + "Gujarati", + "Haitian Creole", + "Hebrew", + "Hindi", + "Hmong Daw", + "Hungarian", + "Icelandic", + "Indonesian", + "Inuktitut", + "Irish", + "Italian", + "Japanese", + "Kannada", + "Kazakh", + "Khmer", + "Klingon", + "Korean", + "Kurdish (Central)", + "Kurdish (Northern)", + "Lao", + "Latvian", + "Lithuanian", + "Malagasy", + "Malay", + "Malayalam", + "Maltese", + "Maori", + "Marathi", + "Myanmar", + "Nepali", + "Norwegian", + "Odia", + "Pashto", + "Persian", + "Polish", + "Portuguese (Brazil)", + "Portuguese (Portugal)", + "Punjabi", + "Queretaro Otomi", + "Romanian", + "Russian", + "Samoan", + "Serbian (Cyrillic)", + "Serbian (Latin)", + "Slovak", + "Slovenian", + "Spanish", + "Swahili", + "Swedish", + "Tahitian", + "Tamil", + "Telugu", + "Thai", + "Tigrinya", + "Tongan", + "Turkish", + "Ukrainian", + "Urdu", + "Vietnamese", + "Welsh", + "Yucatec Maya" +}, languagesFrom = languagesTo; +extern const std::unordered_map codes +{ + { { L"Afrikaans" }, { L"af" } }, + { { L"Albanian" }, { L"sq" } }, + { { L"Amharic" }, { L"am" } }, + { { L"Arabic" }, { L"ar" } }, + { { L"Armenian" }, { L"hy" } }, + { { L"Assamese" }, { L"as" } }, + { { L"Azerbaijani" }, { L"az" } }, + { { L"Bangla" }, { L"bn" } }, + { { L"Bosnian (Latin)" }, { L"bs" } }, + { { L"Bulgarian" }, { L"bg" } }, + { { L"Cantonese (Traditional)" }, { L"yue" } }, + { { L"Catalan" }, { L"ca" } }, + { { L"Chinese (Simplified)" }, { L"zh-Hans" } }, + { { L"Chinese (Traditional)" }, { L"zh-Hant" } }, + { { L"Croatian" }, { L"hr" } }, + { { L"Czech" }, { L"cs" } }, + { { L"Danish" }, { L"da" } }, + { { L"Dari" }, { L"prs" } }, + { { L"Dutch" }, { L"nl" } }, + { { L"English" }, { L"en" } }, + { { L"Estonian" }, { L"et" } }, + { { L"Fijian" }, { L"fj" } }, + { { L"Filipino" }, { L"fil" } }, + { { L"Finnish" }, { L"fi" } }, + { { L"French" }, { L"fr" } }, + { { L"French (Canada)" }, { L"fr-ca" } }, + { { L"German" }, { L"de" } }, + { { L"Greek" }, { L"el" } }, + { { L"Gujarati" }, { L"gu" } }, + { { L"Haitian Creole" }, { L"ht" } }, + { { L"Hebrew" }, { L"he" } }, + { { L"Hindi" }, { L"hi" } }, + { { L"Hmong Daw" }, { L"mww" } }, + { { L"Hungarian" }, { L"hu" } }, + { { L"Icelandic" }, { L"is" } }, + { { L"Indonesian" }, { L"id" } }, + { { L"Inuktitut" }, { L"iu" } }, + { { L"Irish" }, { L"ga" } }, + { { L"Italian" }, { L"it" } }, + { { L"Japanese" }, { L"ja" } }, + { { L"Kannada" }, { L"kn" } }, + { { L"Kazakh" }, { L"kk" } }, + { { L"Khmer" }, { L"km" } }, + { { L"Klingon" }, { L"tlh-Latn" } }, + { { L"Korean" }, { L"ko" } }, + { { L"Kurdish (Central)" }, { L"ku" } }, + { { L"Kurdish (Northern)" }, { L"kmr" } }, + { { L"Lao" }, { L"lo" } }, + { { L"Latvian" }, { L"lv" } }, + { { L"Lithuanian" }, { L"lt" } }, + { { L"Malagasy" }, { L"mg" } }, + { { L"Malay" }, { L"ms" } }, + { { L"Malayalam" }, { L"ml" } }, + { { L"Maltese" }, { L"mt" } }, + { { L"Maori" }, { L"mi" } }, + { { L"Marathi" }, { L"mr" } }, + { { L"Myanmar" }, { L"my" } }, + { { L"Nepali" }, { L"ne" } }, + { { L"Norwegian" }, { L"nb" } }, + { { L"Odia" }, { L"or" } }, + { { L"Pashto" }, { L"ps" } }, + { { L"Persian" }, { L"fa" } }, + { { L"Polish" }, { L"pl" } }, + { { L"Portuguese (Brazil)" }, { L"pt" } }, + { { L"Portuguese (Portugal)" }, { L"pt-pt" } }, + { { L"Punjabi" }, { L"pa" } }, + { { L"Queretaro Otomi" }, { L"otq" } }, + { { L"Romanian" }, { L"ro" } }, + { { L"Russian" }, { L"ru" } }, + { { L"Samoan" }, { L"sm" } }, + { { L"Serbian (Cyrillic)" }, { L"sr-Cyrl" } }, + { { L"Serbian (Latin)" }, { L"sr-Latn" } }, + { { L"Slovak" }, { L"sk" } }, + { { L"Slovenian" }, { L"sl" } }, + { { L"Spanish" }, { L"es" } }, + { { L"Swahili" }, { L"sw" } }, + { { L"Swedish" }, { L"sv" } }, + { { L"Tahitian" }, { L"ty" } }, + { { L"Tamil" }, { L"ta" } }, + { { L"Telugu" }, { L"te" } }, + { { L"Thai" }, { L"th" } }, + { { L"Tigrinya" }, { L"ti" } }, + { { L"Tongan" }, { L"to" } }, + { { L"Turkish" }, { L"tr" } }, + { { L"Ukrainian" }, { L"uk" } }, + { { L"Urdu" }, { L"ur" } }, + { { L"Vietnamese" }, { L"vi" } }, + { { L"Welsh" }, { L"cy" } }, + { { L"Yucatec Maya" }, { L"yua" } }, + { { L"?" }, { L"auto-detect" } } }; -std::wstring autoDetectLanguage = L"auto-detect"; bool translateSelectedOnly = false, rateLimitAll = true, rateLimitSelected = false, useCache = true, useFilter = true; int tokenCount = 30, tokenRestoreDelay = 60000, maxSentenceSize = 1000; -std::pair Translate(const std::wstring& text) +std::pair Translate(const std::wstring& text, TranslationParam tlp) { - if (!authKey->empty()) + if (!tlp.authKey.empty()) { - std::wstring translateFromComponent = translateFrom.Copy() == autoDetectLanguage ? L"" : L"&from=" + translateFrom.Copy(); + std::wstring translateFromComponent = tlp.translateFrom == L"?" ? L"" : L"&from=" + codes.at(tlp.translateFrom); if (HttpRequest httpRequest{ L"Mozilla/5.0 Textractor", L"api.cognitive.microsofttranslator.com", L"POST", - FormatString(L"/translate?api-version=3.0&to=%s%s", translateTo.Copy(), translateFromComponent).c_str(), + FormatString(L"/translate?api-version=3.0&to=%s%s", codes.at(tlp.translateTo), translateFromComponent).c_str(), 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", tlp.authKey).c_str() }) 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) }; @@ -102,15 +214,17 @@ std::pair Translate(const std::wstring& text) } static Synchronized token; - if (token->empty()) if (HttpRequest httpRequest{ L"Mozilla/5.0 Textractor", L"www.bing.com", L"GET", L"translator" }) - if (auto tokenPos = httpRequest.response.find(L"[" + std::to_wstring(time(nullptr) / 10)); tokenPos != std::string::npos) - token->assign(FormatString(L"&key=%s&token=%s", httpRequest.response.substr(tokenPos + 1, 13), httpRequest.response.substr(tokenPos + 16, 32))); - if (token->empty()) return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, L"token missing") }; + if (token->empty()) + if (HttpRequest httpRequest{ L"Mozilla/5.0 Textractor", L"www.bing.com", L"GET", L"translator" }) + if (auto tokenPos = httpRequest.response.find(L"[" + std::to_wstring(time(nullptr) / 100)); tokenPos != std::string::npos) + token->assign(FormatString(L"&key=%s&token=%s", httpRequest.response.substr(tokenPos + 1, 13), httpRequest.response.substr(tokenPos + 16, 32))); + else return { false, FormatString(L"%s: %s\ntoken not found", TRANSLATION_ERROR, httpRequest.response) }; + else return { false, FormatString(L"%s: could not acquire token", TRANSLATION_ERROR) }; if (HttpRequest httpRequest{ L"Mozilla/5.0 Textractor", L"www.bing.com", L"POST", - FormatString(L"/ttranslatev3?fromLang=%s&to=%s&text=%s%s", translateFrom.Copy(), translateTo.Copy(), Escape(text), token.Copy()).c_str() + FormatString(L"/ttranslatev3?fromLang=%s&to=%s&text=%s%s", codes.at(tlp.translateFrom), codes.at(tlp.translateTo), Escape(text), token.Copy()).c_str() }) 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 (token=%s): %s", TRANSLATION_ERROR, std::exchange(token.Acquire().contents, L""), httpRequest.response) }; diff --git a/extensions/deepltranslate.cpp b/extensions/deepltranslate.cpp index 0ea254c..06e55ca 100644 --- a/extensions/deepltranslate.cpp +++ b/extensions/deepltranslate.cpp @@ -1,75 +1,126 @@ #include "qtcommon.h" +#include "translatewrapper.h" #include "network.h" #include extern const wchar_t* TRANSLATION_ERROR; -extern Synchronized translateTo, translateFrom, authKey; - const char* TRANSLATION_PROVIDER = "DeepL Translate"; const char* GET_API_KEY_FROM = "https://www.deepl.com/pro.html"; -QStringList languages +extern const QStringList languagesTo { - "Bulgarian: BG", - "Chinese: ZH", - "Czech: CS", - "Danish: DA", - "Dutch: NL", - "English: EN", - "Estonian: ET", - "Finnish: FI", - "French: FR", - "German: DE", - "Greek: EL", - "Hungarian: HU", - "Italian: IT", - "Japanese: JA", - "Latvian: LV", - "Lithuanian: LT", - "Polish: PL", - "Portuguese: PT", - "Romanian: RO", - "Russian: RU", - "Slovak: SK", - "Slovenian: SL", - "Spanish: ES", - "Swedish: SV" + "Bulgarian", + "Chinese (Simplified)", + "Czech", + "Danish", + "Dutch", + "English (American)", + "English (British)", + "Estonian", + "Finnish", + "French", + "German", + "Greek", + "Hungarian", + "Italian", + "Japanese", + "Latvian", + "Lithuanian", + "Polish", + "Portuguese (Brazil)", + "Portuguese (Portugal)", + "Romanian", + "Russian", + "Slovak", + "Slovenian", + "Spanish", + "Swedish" +}, +languagesFrom +{ + "Bulgarian", + "Chinese", + "Czech", + "Danish", + "Dutch", + "English", + "Estonian", + "Finnish", + "French", + "German", + "Greek", + "Hungarian", + "Italian", + "Japanese", + "Latvian", + "Lithuanian", + "Polish", + "Portuguese", + "Romanian", + "Russian", + "Slovak", + "Slovenian", + "Spanish", + "Swedish" +}; +extern const std::unordered_map codes +{ + { { L"Bulgarian" }, { L"BG" } }, + { { L"Chinese" }, { L"ZH" } }, + { { L"Chinese (Simplified)" }, { L"ZH" } }, + { { L"Czech" }, { L"CS" } }, + { { L"Danish" }, { L"DA" } }, + { { L"Dutch" }, { L"NL" } }, + { { L"English" }, { L"EN" } }, + { { L"English (American)" }, { L"EN-US" } }, + { { L"English (British)" }, { L"EN-GB" } }, + { { L"Estonian" }, { L"ET" } }, + { { L"Finnish" }, { L"FI" } }, + { { L"French" }, { L"FR" } }, + { { L"German" }, { L"DE" } }, + { { L"Greek" }, { L"EL" } }, + { { L"Hungarian" }, { L"HU" } }, + { { L"Italian" }, { L"IT" } }, + { { L"Japanese" }, { L"JA" } }, + { { L"Latvian" }, { L"LV" } }, + { { L"Lithuanian" }, { L"LT" } }, + { { L"Polish" }, { L"PL" } }, + { { L"Portuguese" }, { L"PT" } }, + { { L"Portuguese (Brazil)" }, { L"PT-BR" } }, + { { L"Portuguese (Portugal)" }, { L"PT-PT" } }, + { { L"Romanian" }, { L"RO" } }, + { { L"Russian" }, { L"RU" } }, + { { L"Slovak" }, { L"SK" } }, + { { L"Slovenian" }, { L"SL" } }, + { { L"Spanish" }, { L"ES" } }, + { { L"Swedish" }, { L"SV" } }, + { { L"?" }, { L"auto" } } }; -std::wstring autoDetectLanguage = L"auto"; bool translateSelectedOnly = true, rateLimitAll = true, rateLimitSelected = true, useCache = true, useFilter = true; int tokenCount = 10, tokenRestoreDelay = 60000, maxSentenceSize = 1000; enum KeyType { CAT, REST }; int keyType = REST; -enum PlanLevel { FREE, PAID }; -int planLevel = PAID; -std::pair Translate(const std::wstring& text) +std::pair Translate(const std::wstring& text, TranslationParam tlp) { - if (!authKey->empty()) + if (!tlp.authKey.empty()) { - std::string translateFromComponent = translateFrom.Copy() == autoDetectLanguage ? "" : "&source_lang=" + WideStringToString(translateFrom.Copy()); + std::string translateFromComponent = tlp.translateFrom == L"?" ? "" : "&source_lang=" + WideStringToString(codes.at(tlp.translateFrom)); if (HttpRequest httpRequest{ L"Mozilla/5.0 Textractor", - planLevel == PAID ? L"api.deepl.com" : L"api-free.deepl.com", + tlp.authKey.find(L":fx") == std::string::npos ? L"api.deepl.com" : L"api-free.deepl.com", L"POST", keyType == CAT ? L"/v1/translate" : L"/v2/translate", - FormatString("text=%S&auth_key=%S&target_lang=%S", Escape(text), authKey.Copy(), translateTo.Copy()) + translateFromComponent, + FormatString("text=%S&auth_key=%S&target_lang=%S", Escape(text), tlp.authKey, codes.at(tlp.translateTo)) + translateFromComponent, L"Content-Type: application/x-www-form-urlencoded" - }; httpRequest && (!httpRequest.response.empty() || (httpRequest = HttpRequest{ + }; httpRequest && (httpRequest.response.find(L"translations") != std::string::npos || (httpRequest = HttpRequest{ L"Mozilla/5.0 Textractor", - planLevel == PAID ? L"api.deepl.com" : L"api-free.deepl.com", + tlp.authKey.find(L":fx") == std::string::npos ? L"api.deepl.com" : L"api-free.deepl.com", L"POST", (keyType = !keyType) == CAT ? L"/v1/translate" : L"/v2/translate", - FormatString("text=%S&auth_key=%S&target_lang=%S", Escape(text), authKey.Copy(), translateTo.Copy()) + translateFromComponent, - L"Content-Type: application/x-www-form-urlencoded" - })) && (httpRequest.response.find(L"Wrong endpoint. Use") == std::string::npos || (httpRequest = HttpRequest{ - L"Mozilla/5.0 Textractor", - (planLevel = !planLevel) == PAID ? L"api.deepl.com" : L"api-free.deepl.com", - L"POST", - keyType == CAT ? L"/v1/translate" : L"/v2/translate", - FormatString("text=%S&auth_key=%S&target_lang=%S", Escape(text), authKey.Copy(), translateTo.Copy()) + translateFromComponent, + FormatString("text=%S&auth_key=%S&target_lang=%S", Escape(text), tlp.authKey, codes.at(tlp.translateTo)) + translateFromComponent, L"Content-Type: application/x-www-form-urlencoded" }))) // Response formatted as JSON: translation starts with text":" and ends with "}] @@ -92,7 +143,7 @@ std::pair Translate(const std::wstring& text) "priority": -1, "timestamp": %lld, "lang": { - "target_lang": "%S", + "target_lang": "%.2S", "source_lang_user_selected": "%S" }, "jobs": [{ @@ -105,7 +156,7 @@ std::pair Translate(const std::wstring& text) }] } } - )", id, r + (n - r % n), translateTo.Copy(), translateFrom.Copy(), JSON::Escape(WideStringToString(text))); + )", id, r + (n - r % n), codes.at(tlp.translateTo), codes.at(tlp.translateFrom), JSON::Escape(WideStringToString(text))); // missing accept-encoding header since it fucks up HttpRequest if (HttpRequest httpRequest{ L"Mozilla/5.0 Textractor", diff --git a/extensions/devtoolsdeepltranslate.cpp b/extensions/devtoolsdeepltranslate.cpp index 7293e8a..c11ddcb 100644 --- a/extensions/devtoolsdeepltranslate.cpp +++ b/extensions/devtoolsdeepltranslate.cpp @@ -1,45 +1,105 @@ #include "qtcommon.h" +#include "translatewrapper.h" #include "devtools.h" extern const wchar_t* ERROR_START_CHROME; extern const wchar_t* TRANSLATION_ERROR; -extern Synchronized translateTo, translateFrom; - const char* TRANSLATION_PROVIDER = "DevTools DeepL Translate"; const char* GET_API_KEY_FROM = nullptr; + +extern const QStringList languagesTo +{ + "Bulgarian", + "Chinese (Simplified)", + "Czech", + "Danish", + "Dutch", + "English (American)", + "English (British)", + "Estonian", + "Finnish", + "French", + "German", + "Greek", + "Hungarian", + "Italian", + "Japanese", + "Latvian", + "Lithuanian", + "Polish", + "Portuguese", + "Portuguese (Brazilian)", + "Romanian", + "Russian", + "Slovak", + "Slovenian", + "Spanish", + "Swedish" +}, +languagesFrom = +{ + "Bulgarian", + "Chinese", + "Czech", + "Danish", + "Dutch", + "English", + "Estonian", + "Finnish", + "French", + "German", + "Greek", + "Hungarian", + "Italian", + "Japanese", + "Latvian", + "Lithuanian", + "Polish", + "Portuguese", + "Romanian", + "Russian", + "Slovak", + "Slovenian", + "Spanish", + "Swedish" +}; +extern const std::unordered_map codes +{ + { { L"Bulgarian" }, { L"Bulgarian" } }, + { { L"Chinese" }, { L"Chinese" } }, + { { L"Chinese (Simplified)" }, { L"Chinese (simplified)" } }, + { { L"Czech" }, { L"Czech" } }, + { { L"Danish" }, { L"Danish" } }, + { { L"Dutch" }, { L"Dutch" } }, + { { L"English" }, { L"English" } }, + { { L"English (American)" }, { L"English (American)" } }, + { { L"English (British)" }, { L"English (British)" } }, + { { L"Estonian" }, { L"Estonian" } }, + { { L"Finnish" }, { L"Finnish" } }, + { { L"French" }, { L"French" } }, + { { L"German" }, { L"German" } }, + { { L"Greek" }, { L"Greek" } }, + { { L"Hungarian" }, { L"Hungarian" } }, + { { L"Italian" }, { L"Italian" } }, + { { L"Japanese" }, { L"Japanese" } }, + { { L"Latvian" }, { L"Latvian" } }, + { { L"Lithuanian" }, { L"Lithuanian" } }, + { { L"Polish" }, { L"Polish" } }, + { { L"Portuguese" }, { L"Portuguese" } }, + { { L"Portuguese (Brazilian)" }, { L"Portuguese (Brazilian)" } }, + { { L"Romanian" }, { L"Romanian" } }, + { { L"Russian" }, { L"Russian" } }, + { { L"Slovak" }, { L"Slovak" } }, + { { L"Slovenian" }, { L"Slovenian" } }, + { { L"Spanish" }, { L"Spanish" } }, + { { L"Swedish" }, { L"Swedish" } }, + { { L"?" }, { L"Any language (detect)" } } +}; + bool translateSelectedOnly = true, rateLimitAll = false, rateLimitSelected = false, useCache = true, useFilter = true; int tokenCount = 30, tokenRestoreDelay = 60000, maxSentenceSize = 2500; -QStringList languages -{ - "Bulgarian: BG", - "Chinese: ZH", - "Czech: CS", - "Danish: DA", - "Dutch: NL", - "English: EN", - "Estonian: ET", - "Finnish: FI", - "French: FR", - "German: DE", - "Greek: EL", - "Hungarian: HU", - "Italian: IT", - "Japanese: JA", - "Latvian: LV", - "Lithuanian: LT", - "Polish: PL", - "Portuguese: PT", - "Romanian: RO", - "Russian: RU", - "Slovak: SK", - "Slovenian: SL", - "Spanish: ES", - "Swedish: SV" -}; -std::wstring autoDetectLanguage = L"auto"; - BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) @@ -58,19 +118,22 @@ BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved return TRUE; } -std::pair Translate(const std::wstring& text) +std::pair Translate(const std::wstring& text, TranslationParam tlp) { if (!DevTools::Connected()) return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, ERROR_START_CHROME) }; // DevTools can't handle concurrent translations yet static std::mutex translationMutex; std::scoped_lock lock(translationMutex); - DevTools::SendRequest("Page.navigate", FormatString(LR"({"url":"https://www.deepl.com/en/translator#%s/%s/%s"})", translateTo.Copy(), translateTo.Copy(), Escape(text))); + DevTools::SendRequest("Page.navigate", FormatString(LR"({"url":"https://www.deepl.com/en/translator#en/en/%s"})", Escape(text))); + for (int retry = 0; ++retry < 20; Sleep(100)) + if (Copy(DevTools::SendRequest("Runtime.evaluate", LR"({"expression":"document.readyState"})")[L"result"][L"value"].String()) == L"complete") break; - if (translateFrom.Copy() != autoDetectLanguage) - DevTools::SendRequest("Runtime.evaluate", FormatString(LR"({"expression":" - document.querySelector('.lmt__language_select--source').querySelector('button').click(); - document.evaluate(`//button[contains(text(),'%s')]`,document.querySelector('.lmt__language_select__menu'),null,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue.click(); - "})", S(std::find_if(languages.begin(), languages.end(), [end = S(translateFrom.Copy())](const QString& language) { return language.endsWith(end); })->split(":")[0]))); + DevTools::SendRequest("Runtime.evaluate", FormatString(LR"({"expression":" + document.querySelector('.lmt__language_select--source').querySelector('button').click(); + document.evaluate(`//button[text()='%s']`,document.querySelector('.lmt__language_select__menu'),null,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue.click(); + document.querySelector('.lmt__language_select--target').querySelector('button').click(); + document.evaluate(`//button[text()='%s']`,document.querySelector('.lmt__language_select__menu'),null,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue.click(); + "})", codes.at(tlp.translateFrom), codes.at(tlp.translateTo))); for (int retry = 0; ++retry < 100; Sleep(100)) if (auto translation = Copy(DevTools::SendRequest("Runtime.evaluate", diff --git a/extensions/devtoolspapagotranslate.cpp b/extensions/devtoolspapagotranslate.cpp index cce6473..5ccc5f2 100644 --- a/extensions/devtoolspapagotranslate.cpp +++ b/extensions/devtoolspapagotranslate.cpp @@ -1,36 +1,54 @@ #include "qtcommon.h" +#include "translatewrapper.h" #include "devtools.h" extern const wchar_t* ERROR_START_CHROME; extern const wchar_t* TRANSLATION_ERROR; -extern Synchronized translateTo, translateFrom; - const char* TRANSLATION_PROVIDER = "DevTools Papago Translate"; const char* GET_API_KEY_FROM = nullptr; + +extern const QStringList languagesTo +{ + "Chinese (Simplified)", + "Chinese (Traditional)", + "English", + "French", + "German", + "Hindi", + "Indonesian", + "Italian", + "Japanese", + "Korean", + "Portuguese", + "Russian", + "Spanish", + "Thai", + "Vietnamese", +}, languagesFrom = languagesTo; +extern const std::unordered_map codes +{ + { { L"Chinese (Simplified)" }, { L"zh-CN" } }, + { { L"Chinese (Traditional)" }, { L"zt-TW" } }, + { { L"English" }, { L"en" } }, + { { L"French" }, { L"fr" } }, + { { L"German" }, { L"de" } }, + { { L"Hindi" }, { L"hi" } }, + { { L"Indonesian" }, { L"id" } }, + { { L"Italian" }, { L"it" } }, + { { L"Japanese" }, { L"ja" } }, + { { L"Korean" }, { L"ko" } }, + { { L"Portuguese" }, { L"pt" } }, + { { L"Russian" }, { L"ru" } }, + { { L"Spanish" }, { L"es" } }, + { { L"Thai" }, { L"th" } }, + { { L"Vietnamese" }, { L"vi" } }, + { { L"?" }, { L"auto" } } +}; + bool translateSelectedOnly = true, rateLimitAll = false, rateLimitSelected = false, useCache = true, useFilter = true; int tokenCount = 30, tokenRestoreDelay = 60000, maxSentenceSize = 2500; -QStringList languages -{ - "Chinese (simplified): zh-CN", - "Chinese (traditional): zt-TW", - "English: en", - "French: fr", - "German: de", - "Hindi: hi", - "Indonesian: id", - "Italian: it", - "Japanese: ja", - "Korean: ko", - "Portuguese: pt", - "Russian: ru", - "Spanish: es", - "Thai: th", - "Vietnamese: vi", -}; -std::wstring autoDetectLanguage = L"auto"; - BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) @@ -49,13 +67,13 @@ BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved return TRUE; } -std::pair Translate(const std::wstring& text) +std::pair Translate(const std::wstring& text, TranslationParam tlp) { if (!DevTools::Connected()) return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, ERROR_START_CHROME) }; // DevTools can't handle concurrent translations yet static std::mutex translationMutex; std::scoped_lock lock(translationMutex); - DevTools::SendRequest("Page.navigate", FormatString(LR"({"url":"https://papago.naver.com/?sk=%s&tk=%s&st=%s"})", translateFrom.Copy(), translateTo.Copy(), Escape(text))); + DevTools::SendRequest("Page.navigate", FormatString(LR"({"url":"https://papago.naver.com/?sk=%s&tk=%s&st=%s"})", codes.at(tlp.translateFrom), codes.at(tlp.translateTo), Escape(text))); for (int retry = 0; ++retry < 100; Sleep(100)) if (auto translation = Copy(DevTools::SendRequest("Runtime.evaluate", LR"({"expression":"document.querySelector('#txtTarget').textContent.trim() ","returnByValue":true})" diff --git a/extensions/devtoolssystrantranslate.cpp b/extensions/devtoolssystrantranslate.cpp index d1fcacd..9e4d2b1 100644 --- a/extensions/devtoolssystrantranslate.cpp +++ b/extensions/devtoolssystrantranslate.cpp @@ -1,34 +1,120 @@ #include "qtcommon.h" +#include "translatewrapper.h" #include "devtools.h" extern const wchar_t* ERROR_START_CHROME; extern const wchar_t* TRANSLATION_ERROR; -extern Synchronized translateTo, translateFrom; -extern QFormLayout* display; -extern Settings settings; - const char* TRANSLATION_PROVIDER = "DevTools Systran Translate"; const char* GET_API_KEY_FROM = nullptr; + +extern const QStringList languagesTo +{ + "Albanian", + "Arabic", + "Bengali", + "Bulgarian", + "Burmese", + "Catalan", + "Chinese (Simplified)", + "Chinese (Traditional)", + "Croatian", + "Czech", + "Danish", + "Dutch", + "English", + "Estonian", + "Finnish", + "French", + "German", + "Greek", + "Hebrew", + "Hindi", + "Hungarian", + "Indonesian", + "Italian", + "Japanese", + "Korean", + "Latvian", + "Lithuanian", + "Malay", + "Norwegian", + "Pashto", + "Persian", + "Polish", + "Portuguese", + "Romanian", + "Russian", + "Serbian", + "Slovak", + "Slovenian", + "Somali", + "Spanish", + "Swedish", + "Tagalog", + "Tamil", + "Thai", + "Turkish", + "Ukrainian", + "Urdu", + "Vietnamese" +}, languagesFrom = languagesTo; +extern const std::unordered_map codes +{ + { { L"Albanian" }, { L"sq" } }, + { { L"Arabic" }, { L"ar" } }, + { { L"Bengali" }, { L"bn" } }, + { { L"Bulgarian" }, { L"bg" } }, + { { L"Burmese" }, { L"my" } }, + { { L"Catalan" }, { L"ca" } }, + { { L"Chinese (Simplified)" }, { L"zh" } }, + { { L"Chinese (Traditional)" }, { L"zt" } }, + { { L"Croatian" }, { L"hr" } }, + { { L"Czech" }, { L"cs" } }, + { { L"Danish" }, { L"da" } }, + { { L"Dutch" }, { L"nl" } }, + { { L"English" }, { L"en" } }, + { { L"Estonian" }, { L"et" } }, + { { L"Finnish" }, { L"fi" } }, + { { L"French" }, { L"fr" } }, + { { L"German" }, { L"de" } }, + { { L"Greek" }, { L"el" } }, + { { L"Hebrew" }, { L"he" } }, + { { L"Hindi" }, { L"hi" } }, + { { L"Hungarian" }, { L"hu" } }, + { { L"Indonesian" }, { L"id" } }, + { { L"Italian" }, { L"it" } }, + { { L"Japanese" }, { L"ja" } }, + { { L"Korean" }, { L"ko" } }, + { { L"Latvian" }, { L"lv" } }, + { { L"Lithuanian" }, { L"lt" } }, + { { L"Malay" }, { L"ms" } }, + { { L"Norwegian" }, { L"no" } }, + { { L"Pashto" }, { L"ps" } }, + { { L"Persian" }, { L"fa" } }, + { { L"Polish" }, { L"pl" } }, + { { L"Portuguese" }, { L"pt" } }, + { { L"Romanian" }, { L"ro" } }, + { { L"Russian" }, { L"ru" } }, + { { L"Serbian" }, { L"sr" } }, + { { L"Slovak" }, { L"sk" } }, + { { L"Slovenian" }, { L"sl" } }, + { { L"Somali" }, { L"so" } }, + { { L"Spanish" }, { L"es" } }, + { { L"Swedish" }, { L"sv" } }, + { { L"Tagalog" }, { L"tl" } }, + { { L"Tamil" }, { L"ta" } }, + { { L"Thai" }, { L"th" } }, + { { L"Turkish" }, { L"tr" } }, + { { L"Ukrainian" }, { L"uk" } }, + { { L"Urdu" }, { L"ur" } }, + { { L"Vietnamese" }, { L"vi" } }, + { { L"?" }, { L"autodetect" } } +}; + bool translateSelectedOnly = true, rateLimitAll = false, rateLimitSelected = false, useCache = true, useFilter = true; int tokenCount = 30, tokenRestoreDelay = 60000, maxSentenceSize = 2500; -QStringList languages -{ - "Chinese: zh", - "Chinese (traditional): zt", - "English: en", - "French: fr", - "German: de", - "Italian: it", - "Japanese: ja", - "Korean: ko", - "Portuguese: pt", - "Spanish: es", - "Thai: th", -}; -std::wstring autoDetectLanguage = L"auto"; - BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) @@ -47,14 +133,17 @@ BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved return TRUE; } -std::pair Translate(const std::wstring& text) +std::pair Translate(const std::wstring& text, TranslationParam tlp) { if (!DevTools::Connected()) return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, ERROR_START_CHROME) }; // DevTools can't handle concurrent translations yet static std::mutex translationMutex; std::scoped_lock lock(translationMutex); - DevTools::SendRequest("Page.navigate", FormatString(LR"({"url":"https://translate.systran.net/?&source=%s&target=%s&input=%s"})", translateFrom.Copy(), translateTo.Copy(), Escape(text))); + DevTools::SendRequest( + "Page.navigate", + FormatString(LR"({"url":"https://translate.systran.net/?source=%s&target=%s&input=%s"})", codes.at(tlp.translateFrom), codes.at(tlp.translateTo), Escape(text)) + ); for (int retry = 0; ++retry < 100; Sleep(100)) if (auto translation = Copy(DevTools::SendRequest("Runtime.evaluate", LR"({"expression":"document.querySelector('#outputEditor').textContent.trim() ","returnByValue":true})" diff --git a/extensions/googletranslate.cpp b/extensions/googletranslate.cpp index c1e8749..482d3c0 100644 --- a/extensions/googletranslate.cpp +++ b/extensions/googletranslate.cpp @@ -1,140 +1,250 @@ #include "qtcommon.h" +#include "translatewrapper.h" #include "network.h" -#include extern const wchar_t* TRANSLATION_ERROR; -extern Synchronized translateTo, translateFrom, authKey; - const char* TRANSLATION_PROVIDER = "Google Translate"; const char* GET_API_KEY_FROM = "https://codelabs.developers.google.com/codelabs/cloud-translation-intro"; -QStringList languages +extern const QStringList languagesTo { - "Afrikaans: af", - "Albanian: sq", - "Amharic: am", - "Arabic: ar", - "Armenian: hy", - "Azerbaijani: az", - "Basque: eu", - "Belarusian: be", - "Bengali: bn", - "Bosnian: bs", - "Bulgarian: bg", - "Catalan: ca", - "Cebuano: ceb", - "Chichewa: ny", - "Chinese (simplified): zh", - "Chinese (traditional): zh-TW", - "Corsican: co", - "Croatian: hr", - "Czech: cs", - "Danish: da", - "Dutch: nl", - "English: en", - "Esperanto: eo", - "Estonian: et", - "Filipino: tl", - "Finnish: fi", - "French: fr", - "Frisian: fy", - "Galician: gl", - "Georgian: ka", - "German: de", - "Greek: el", - "Gujarati: gu", - "Haitian Creole: ht", - "Hausa: ha", - "Hawaiian: haw", - "Hebrew: iw", - "Hindi: hi", - "Hmong: hmn", - "Hungarian: hu", - "Icelandic: is", - "Igbo: ig", - "Indonesian: id", - "Irish: ga", - "Italian: it", - "Japanese: ja", - "Javanese: jw", - "Kannada: kn", - "Kazakh: kk", - "Khmer: km", - "Kinyarwanda: rw", - "Korean: ko", - "Kurdish (Kurmanji): ku", - "Kyrgyz: ky", - "Lao: lo", - "Latin: la", - "Latvian: lv", - "Lithuanian: lt", - "Luxembourgish: lb", - "Macedonian: mk", - "Malagasy: mg", - "Malay: ms", - "Malayalam: ml", - "Maltese: mt", - "Maori: mi", - "Marathi: mr", - "Mongolian: mn", - "Myanmar (Burmese): my", - "Nepali: ne", - "Norwegian: no", - "Odia (Oriya): or", - "Pashto: ps", - "Persian: fa", - "Polish: pl", - "Portuguese: pt", - "Punjabi: pa", - "Romanian: ro", - "Russian: ru", - "Samoan: sm", - "Scots Gaelic: gd", - "Serbian: sr", - "Sesotho: st", - "Shona: sn", - "Sindhi: sd", - "Sinhala: si", - "Slovak: sk", - "Slovenian: sl", - "Somali: so", - "Spanish: es", - "Sundanese: su", - "Swahili: sw", - "Swedish: sv", - "Tajik: tg", - "Tamil: ta", - "Tatar: tt", - "Telugu: te", - "Thai: th", - "Turkish: tr", - "Turkmen: tk", - "Ukrainian: uk", - "Urdu: ur", - "Uyghur: ug", - "Uzbek: uz", - "Vietnamese: vi", - "Welsh: cy", - "Xhosa: xh", - "Yiddish: yi", - "Yoruba: yo", - "Zulu: zu" + "Afrikaans", + "Albanian", + "Amharic", + "Arabic", + "Armenian", + "Azerbaijani", + "Basque", + "Belarusian", + "Bengali", + "Bosnian", + "Bulgarian", + "Catalan", + "Cebuano", + "Chichewa", + "Chinese (Simplified)", + "Chinese (Traditional)", + "Corsican", + "Croatian", + "Czech", + "Danish", + "Dutch", + "English", + "Esperanto", + "Estonian", + "Filipino", + "Finnish", + "French", + "Frisian", + "Galician", + "Georgian", + "German", + "Greek", + "Gujarati", + "Haitian Creole", + "Hausa", + "Hawaiian", + "Hebrew", + "Hindi", + "Hmong", + "Hungarian", + "Icelandic", + "Igbo", + "Indonesian", + "Irish", + "Italian", + "Japanese", + "Javanese", + "Kannada", + "Kazakh", + "Khmer", + "Kinyarwanda", + "Korean", + "Kurdish (Kurmanji)", + "Kyrgyz", + "Lao", + "Latin", + "Latvian", + "Lithuanian", + "Luxembourgish", + "Macedonian", + "Malagasy", + "Malay", + "Malayalam", + "Maltese", + "Maori", + "Marathi", + "Mongolian", + "Myanmar (Burmese)", + "Nepali", + "Norwegian", + "Odia (Oriya)", + "Pashto", + "Persian", + "Polish", + "Portuguese", + "Punjabi", + "Romanian", + "Russian", + "Samoan", + "Scots Gaelic", + "Serbian", + "Sesotho", + "Shona", + "Sindhi", + "Sinhala", + "Slovak", + "Slovenian", + "Somali", + "Spanish", + "Sundanese", + "Swahili", + "Swedish", + "Tajik", + "Tamil", + "Tatar", + "Telugu", + "Thai", + "Turkish", + "Turkmen", + "Ukrainian", + "Urdu", + "Uyghur", + "Uzbek", + "Vietnamese", + "Welsh", + "Xhosa", + "Yiddish", + "Yoruba", + "Zulu", +}, languagesFrom = languagesTo; +extern const std::unordered_map codes +{ + { { L"Afrikaans" }, { L"af" } }, + { { L"Albanian" }, { L"sq" } }, + { { L"Amharic" }, { L"am" } }, + { { L"Arabic" }, { L"ar" } }, + { { L"Armenian" }, { L"hy" } }, + { { L"Azerbaijani" }, { L"az" } }, + { { L"Basque" }, { L"eu" } }, + { { L"Belarusian" }, { L"be" } }, + { { L"Bengali" }, { L"bn" } }, + { { L"Bosnian" }, { L"bs" } }, + { { L"Bulgarian" }, { L"bg" } }, + { { L"Catalan" }, { L"ca" } }, + { { L"Cebuano" }, { L"ceb" } }, + { { L"Chichewa" }, { L"ny" } }, + { { L"Chinese (Simplified)" }, { L"zh-CN" } }, + { { L"Chinese (Traditional)" }, { L"zh-TW" } }, + { { L"Corsican" }, { L"co" } }, + { { L"Croatian" }, { L"hr" } }, + { { L"Czech" }, { L"cs" } }, + { { L"Danish" }, { L"da" } }, + { { L"Dutch" }, { L"nl" } }, + { { L"English" }, { L"en" } }, + { { L"Esperanto" }, { L"eo" } }, + { { L"Estonian" }, { L"et" } }, + { { L"Filipino" }, { L"tl" } }, + { { L"Finnish" }, { L"fi" } }, + { { L"French" }, { L"fr" } }, + { { L"Frisian" }, { L"fy" } }, + { { L"Galician" }, { L"gl" } }, + { { L"Georgian" }, { L"ka" } }, + { { L"German" }, { L"de" } }, + { { L"Greek" }, { L"el" } }, + { { L"Gujarati" }, { L"gu" } }, + { { L"Haitian Creole" }, { L"ht" } }, + { { L"Hausa" }, { L"ha" } }, + { { L"Hawaiian" }, { L"haw" } }, + { { L"Hebrew" }, { L"iw" } }, + { { L"Hindi" }, { L"hi" } }, + { { L"Hmong" }, { L"hmn" } }, + { { L"Hungarian" }, { L"hu" } }, + { { L"Icelandic" }, { L"is" } }, + { { L"Igbo" }, { L"ig" } }, + { { L"Indonesian" }, { L"id" } }, + { { L"Irish" }, { L"ga" } }, + { { L"Italian" }, { L"it" } }, + { { L"Japanese" }, { L"ja" } }, + { { L"Javanese" }, { L"jw" } }, + { { L"Kannada" }, { L"kn" } }, + { { L"Kazakh" }, { L"kk" } }, + { { L"Khmer" }, { L"km" } }, + { { L"Kinyarwanda" }, { L"rw" } }, + { { L"Korean" }, { L"ko" } }, + { { L"Kurdish (Kurmanji)" }, { L"ku" } }, + { { L"Kyrgyz" }, { L"ky" } }, + { { L"Lao" }, { L"lo" } }, + { { L"Latin" }, { L"la" } }, + { { L"Latvian" }, { L"lv" } }, + { { L"Lithuanian" }, { L"lt" } }, + { { L"Luxembourgish" }, { L"lb" } }, + { { L"Macedonian" }, { L"mk" } }, + { { L"Malagasy" }, { L"mg" } }, + { { L"Malay" }, { L"ms" } }, + { { L"Malayalam" }, { L"ml" } }, + { { L"Maltese" }, { L"mt" } }, + { { L"Maori" }, { L"mi" } }, + { { L"Marathi" }, { L"mr" } }, + { { L"Mongolian" }, { L"mn" } }, + { { L"Myanmar (Burmese)" }, { L"my" } }, + { { L"Nepali" }, { L"ne" } }, + { { L"Norwegian" }, { L"no" } }, + { { L"Odia (Oriya)" }, { L"or" } }, + { { L"Pashto" }, { L"ps" } }, + { { L"Persian" }, { L"fa" } }, + { { L"Polish" }, { L"pl" } }, + { { L"Portuguese" }, { L"pt" } }, + { { L"Punjabi" }, { L"pa" } }, + { { L"Romanian" }, { L"ro" } }, + { { L"Russian" }, { L"ru" } }, + { { L"Samoan" }, { L"sm" } }, + { { L"Scots Gaelic" }, { L"gd" } }, + { { L"Serbian" }, { L"sr" } }, + { { L"Sesotho" }, { L"st" } }, + { { L"Shona" }, { L"sn" } }, + { { L"Sindhi" }, { L"sd" } }, + { { L"Sinhala" }, { L"si" } }, + { { L"Slovak" }, { L"sk" } }, + { { L"Slovenian" }, { L"sl" } }, + { { L"Somali" }, { L"so" } }, + { { L"Spanish" }, { L"es" } }, + { { L"Sundanese" }, { L"su" } }, + { { L"Swahili" }, { L"sw" } }, + { { L"Swedish" }, { L"sv" } }, + { { L"Tajik" }, { L"tg" } }, + { { L"Tamil" }, { L"ta" } }, + { { L"Tatar" }, { L"tt" } }, + { { L"Telugu" }, { L"te" } }, + { { L"Thai" }, { L"th" } }, + { { L"Turkish" }, { L"tr" } }, + { { L"Turkmen" }, { L"tk" } }, + { { L"Ukrainian" }, { L"uk" } }, + { { L"Urdu" }, { L"ur" } }, + { { L"Uyghur" }, { L"ug" } }, + { { L"Uzbek" }, { L"uz" } }, + { { L"Vietnamese" }, { L"vi" } }, + { { L"Welsh" }, { L"cy" } }, + { { L"Xhosa" }, { L"xh" } }, + { { L"Yiddish" }, { L"yi" } }, + { { L"Yoruba" }, { L"yo" } }, + { { L"Zulu" }, { L"zu" } }, + { { L"?" }, { L"auto" } } }; -std::wstring autoDetectLanguage = L"auto"; bool translateSelectedOnly = false, rateLimitAll = true, rateLimitSelected = false, useCache = true, useFilter = true; int tokenCount = 30, tokenRestoreDelay = 60000, maxSentenceSize = 1000; -std::pair Translate(const std::wstring& text) +std::pair Translate(const std::wstring& text, TranslationParam tlp) { - if (!authKey->empty()) + if (!tlp.authKey.empty()) { - std::wstring translateFromComponent = translateFrom.Copy() == autoDetectLanguage ? L"" : L"&source=" + translateFrom.Copy(); + std::wstring translateFromComponent = tlp.translateFrom == L"?" ? L"" : L"&source=" + codes.at(tlp.translateFrom); if (HttpRequest httpRequest{ L"Mozilla/5.0 Textractor", L"translation.googleapis.com", L"POST", - FormatString(L"/language/translate/v2?format=text&target=%s&key=%s%s", translateTo.Copy(), authKey.Copy(), translateFromComponent).c_str(), + FormatString(L"/language/translate/v2?format=text&target=%s&key=%s%s", codes.at(tlp.translateTo), tlp.authKey, translateFromComponent).c_str(), 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() }; @@ -146,7 +256,7 @@ std::pair Translate(const std::wstring& text) L"Mozilla/5.0 Textractor", L"translate.google.com", L"GET", - FormatString(L"/m?sl=%s&tl=%s&q=%s", translateFrom.Copy(), translateTo.Copy(), Escape(text)).c_str() + FormatString(L"/m?sl=%s&tl=%s&q=%s", codes.at(tlp.translateFrom), codes.at(tlp.translateTo), Escape(text)).c_str() }) { auto start = httpRequest.response.find(L"result-container\">"), end = httpRequest.response.find(L'<', start); diff --git a/extensions/translatewrapper.cpp b/extensions/translatewrapper.cpp index 65f2cc6..f7d8ef3 100644 --- a/extensions/translatewrapper.cpp +++ b/extensions/translatewrapper.cpp @@ -1,5 +1,6 @@ #include "qtcommon.h" #include "extension.h" +#include "translatewrapper.h" #include "blockmarkup.h" #include "network.h" #include @@ -22,22 +23,19 @@ extern const wchar_t* TOO_MANY_TRANS_REQUESTS; extern const char* TRANSLATION_PROVIDER; extern const char* GET_API_KEY_FROM; -extern QStringList languages; -extern std::wstring autoDetectLanguage; +extern const QStringList languagesTo, languagesFrom; extern bool translateSelectedOnly, rateLimitAll, rateLimitSelected, useCache, useFilter; extern int tokenCount, tokenRestoreDelay, maxSentenceSize; -std::pair Translate(const std::wstring& text); +std::pair Translate(const std::wstring& text, TranslationParam tlp); -// backwards compatibility -const char* LANGUAGE = u8"Language"; const std::string TRANSLATION_CACHE_FILE = FormatString("%s Translation Cache.txt", TRANSLATION_PROVIDER); QFormLayout* display; Settings settings; -Synchronized translateTo = L"en", translateFrom = L"auto", authKey; namespace { + Synchronized tlp; Synchronized> translationCache; int savedSize; void SaveCache() @@ -60,23 +58,22 @@ public: settings.beginGroup(TRANSLATION_PROVIDER); auto translateToCombo = new QComboBox(this); - translateToCombo->addItems(languages); - int language = -1; - if (settings.contains(LANGUAGE)) language = translateToCombo->findText(settings.value(LANGUAGE).toString(), Qt::MatchEndsWith); - if (settings.contains(TRANSLATE_TO)) language = translateToCombo->findText(settings.value(TRANSLATE_TO).toString(), Qt::MatchEndsWith); - if (language < 0) language = translateToCombo->findText(NATIVE_LANGUAGE, Qt::MatchStartsWith); - if (language < 0) language = translateToCombo->findText("English", Qt::MatchStartsWith); - translateToCombo->setCurrentIndex(language); + translateToCombo->addItems(languagesTo); + int i = -1; + if (settings.contains(TRANSLATE_TO)) i = translateToCombo->findText(settings.value(TRANSLATE_TO).toString()); + if (i < 0) i = translateToCombo->findText(NATIVE_LANGUAGE, Qt::MatchStartsWith); + if (i < 0) i = translateToCombo->findText("English", Qt::MatchStartsWith); + translateToCombo->setCurrentIndex(i); SaveTranslateTo(translateToCombo->currentText()); 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("?", Qt::MatchStartsWith); - translateFromCombo->setCurrentIndex(language); + translateFromCombo->addItem("?"); + translateFromCombo->addItems(languagesFrom); + i = -1; + if (settings.contains(TRANSLATE_FROM)) i = translateFromCombo->findText(settings.value(TRANSLATE_FROM).toString()); + if (i < 0) i = 0; + translateFromCombo->setCurrentIndex(i); SaveTranslateFrom(translateFromCombo->currentText()); display->addRow(TRANSLATE_FROM, translateFromCombo); connect(translateFromCombo, &QComboBox::currentTextChanged, this, &Window::SaveTranslateFrom); @@ -110,8 +107,8 @@ public: if (GET_API_KEY_FROM) { auto keyEdit = new QLineEdit(settings.value(API_KEY).toString(), this); - authKey->assign(S(keyEdit->text())); - QObject::connect(keyEdit, &QLineEdit::textChanged, [](QString key) { settings.setValue(API_KEY, S(authKey->assign(S(key)))); }); + tlp->authKey = S(keyEdit->text()); + QObject::connect(keyEdit, &QLineEdit::textChanged, [](QString key) { settings.setValue(API_KEY, S(tlp->authKey = S(key))); }); auto keyLabel = new QLabel(QString("%2").arg(GET_API_KEY_FROM, API_KEY), this); keyLabel->setOpenExternalLinks(true); display->addRow(keyLabel, keyEdit); @@ -139,11 +136,11 @@ public: private: void SaveTranslateTo(QString language) { - settings.setValue(TRANSLATE_TO, S(translateTo->assign(S(language.split(": ")[1])))); + settings.setValue(TRANSLATE_TO, S(tlp->translateTo = S(language))); } void SaveTranslateFrom(QString language) { - settings.setValue(TRANSLATE_FROM, S(translateFrom->assign(S(language.split(": ")[1])))); + settings.setValue(TRANSLATE_FROM, S(tlp->translateFrom = S(language))); } } window; @@ -186,7 +183,7 @@ bool ProcessSentence(std::wstring& sentence, SentenceInfo sentenceInfo) if (auto it = translationCache->find(sentence); it != translationCache->end()) translation = it->second + L"\x200b"; // dumb hack to not try to translate if stored empty translation } if (translation.empty() && (!translateSelectedOnly || sentenceInfo["current select"])) - if (rateLimiter.Request() || !rateLimitAll || (!rateLimitSelected && sentenceInfo["current select"])) std::tie(cache, translation) = Translate(sentence); + if (rateLimiter.Request() || !rateLimitAll || (!rateLimitSelected && sentenceInfo["current select"])) std::tie(cache, translation) = Translate(sentence, tlp.Copy()); else translation = TOO_MANY_TRANS_REQUESTS; if (useFilter) Trim(translation); if (cache) translationCache->try_emplace(sentence, translation); @@ -197,4 +194,13 @@ bool ProcessSentence(std::wstring& sentence, SentenceInfo sentenceInfo) return true; } -TEST(assert(Translate(L"こんにちは").second.find(L"ello") != std::string::npos)); +extern const std::unordered_map codes; +TEST( + { + assert(Translate(L"こんにちは", { L"English", L"?", L"" }).second.find(L"ello") == 1 || strstr(TRANSLATION_PROVIDER, "DevTools")); + + for (auto languages : { languagesFrom, languagesTo }) for (auto language : languages) + assert(codes.count(S(language))); + assert(codes.count(L"?")); + } +); diff --git a/extensions/translatewrapper.h b/extensions/translatewrapper.h new file mode 100644 index 0000000..9d34a38 --- /dev/null +++ b/extensions/translatewrapper.h @@ -0,0 +1,6 @@ +#pragma once + +struct TranslationParam +{ + std::wstring translateTo, translateFrom, authKey; +}; diff --git a/text.cpp b/text.cpp index fc9ca82..f62b995 100644 --- a/text.cpp +++ b/text.cpp @@ -324,7 +324,7 @@ Clic y arrastra los bordes de la ventana para moverla, o en la esquina inferior #endif // SPANISH #ifdef SIMPLIFIED_CHINESE - NATIVE_LANGUAGE = "Chinese (simplified)"; + NATIVE_LANGUAGE = "Chinese (Simplified)"; ATTACH = u8"附加到游戏"; LAUNCH = u8"启动游戏"; CONFIG = u8"配置游戏";