improve translation extension ui, add language detection for papago and systran, add more languages to systran, improve deepl translation and bing error handling

This commit is contained in:
Akash Mozumdar 2021-06-28 22:24:59 -06:00
parent 444974ee8a
commit ac95d873f1
9 changed files with 815 additions and 358 deletions

View File

@ -1,100 +1,212 @@
#include "qtcommon.h" #include "qtcommon.h"
#include "translatewrapper.h"
#include "network.h" #include "network.h"
extern const wchar_t* TRANSLATION_ERROR; extern const wchar_t* TRANSLATION_ERROR;
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";
QStringList languages extern const QStringList languagesTo
{ {
"Afrikaans: af", "Afrikaans",
"Arabic: ar", "Albanian",
"Bangla: bn", "Amharic",
"Bosnian: bs", "Arabic",
"Bulgarian: bg", "Armenian",
"Cantonese (traditional): yue", "Assamese",
"Catalan: ca", "Azerbaijani",
"Chinese (simplified): zh-Hans", "Bangla",
"Chinese (traditional): zh-Hant", "Bosnian (Latin)",
"Croatian: hr", "Bulgarian",
"Czech: cs", "Cantonese (Traditional)",
"Danish: da", "Catalan",
"Dutch: nl", "Chinese (Simplified)",
"English: en", "Chinese (Traditional)",
"Estonian: et", "Croatian",
"Fijian: fj", "Czech",
"Filipino: fil", "Danish",
"Finnish: fi", "Dari",
"French: fr", "Dutch",
"German: de", "English",
"Greek: el", "Estonian",
"Haitian Creole: ht", "Fijian",
"Hebrew: he", "Filipino",
"Hindi: hi", "Finnish",
"Hmong Daw: mww", "French",
"Hungarian: hu", "French (Canada)",
"Icelandic: is", "German",
"Indonesian: id", "Greek",
"Irish: ga", "Gujarati",
"Italian: it", "Haitian Creole",
"Japanese: ja", "Hebrew",
"Kannada: kn", "Hindi",
"Klingon: tlh", "Hmong Daw",
"Korean: ko", "Hungarian",
"Latvian: lv", "Icelandic",
"Lithuanian: lt", "Indonesian",
"Malagasy: mg", "Inuktitut",
"Malay: ms", "Irish",
"Malayalam: ml", "Italian",
"Maltese: mt", "Japanese",
"Maori: mi", "Kannada",
"Norwegian: nb", "Kazakh",
"Persian: fa", "Khmer",
"Polish: pl", "Klingon",
"Portuguese (Brazil): pt", "Korean",
"Portuguese (Portugal): pt-pt", "Kurdish (Central)",
"Punjabi: pa", "Kurdish (Northern)",
"Romanian: ro", "Lao",
"Russian: ru", "Latvian",
"Samoan: sm", "Lithuanian",
"Serbian (Cyrillic): sr-Cyrl", "Malagasy",
"Serbian (Latin): sr-Latn", "Malay",
"Slovak: sk", "Malayalam",
"Slovenian: sl", "Maltese",
"Spanish: es", "Maori",
"Swahili: sw", "Marathi",
"Swedish: sv", "Myanmar",
"Tahitian: ty", "Nepali",
"Tamil: ta", "Norwegian",
"Telugu: te", "Odia",
"Thai: th", "Pashto",
"Tongan: to", "Persian",
"Turkish: tr", "Polish",
"Ukrainian: uk", "Portuguese (Brazil)",
"Urdu: ur", "Portuguese (Portugal)",
"Vietnamese: vi", "Punjabi",
"Welsh: cy", "Queretaro Otomi",
"Yucatec Maya: yua" "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<std::wstring, std::wstring> 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; bool translateSelectedOnly = false, rateLimitAll = true, rateLimitSelected = false, useCache = true, useFilter = true;
int tokenCount = 30, tokenRestoreDelay = 60000, maxSentenceSize = 1000; 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, 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{ 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%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(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() }; 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) };
@ -102,15 +214,17 @@ std::pair<bool, std::wstring> Translate(const std::wstring& text)
} }
static Synchronized<std::wstring> token; static Synchronized<std::wstring> token;
if (token->empty()) if (HttpRequest httpRequest{ L"Mozilla/5.0 Textractor", L"www.bing.com", L"GET", L"translator" }) if (token->empty())
if (auto tokenPos = httpRequest.response.find(L"[" + std::to_wstring(time(nullptr) / 10)); tokenPos != std::string::npos) if (HttpRequest httpRequest{ L"Mozilla/5.0 Textractor", L"www.bing.com", L"GET", L"translator" })
token->assign(FormatString(L"&key=%s&token=%s", httpRequest.response.substr(tokenPos + 1, 13), httpRequest.response.substr(tokenPos + 16, 32))); if (auto tokenPos = httpRequest.response.find(L"[" + std::to_wstring(time(nullptr) / 100)); tokenPos != std::string::npos)
if (token->empty()) return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, L"token missing") }; 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{ 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=%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() }; 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) }; else return { false, FormatString(L"%s (token=%s): %s", TRANSLATION_ERROR, std::exchange(token.Acquire().contents, L""), httpRequest.response) };

View File

@ -1,75 +1,126 @@
#include "qtcommon.h" #include "qtcommon.h"
#include "translatewrapper.h"
#include "network.h" #include "network.h"
#include <random> #include <random>
extern const wchar_t* TRANSLATION_ERROR; extern const wchar_t* TRANSLATION_ERROR;
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";
QStringList languages extern const QStringList languagesTo
{ {
"Bulgarian: BG", "Bulgarian",
"Chinese: ZH", "Chinese (Simplified)",
"Czech: CS", "Czech",
"Danish: DA", "Danish",
"Dutch: NL", "Dutch",
"English: EN", "English (American)",
"Estonian: ET", "English (British)",
"Finnish: FI", "Estonian",
"French: FR", "Finnish",
"German: DE", "French",
"Greek: EL", "German",
"Hungarian: HU", "Greek",
"Italian: IT", "Hungarian",
"Japanese: JA", "Italian",
"Latvian: LV", "Japanese",
"Lithuanian: LT", "Latvian",
"Polish: PL", "Lithuanian",
"Portuguese: PT", "Polish",
"Romanian: RO", "Portuguese (Brazil)",
"Russian: RU", "Portuguese (Portugal)",
"Slovak: SK", "Romanian",
"Slovenian: SL", "Russian",
"Spanish: ES", "Slovak",
"Swedish: SV" "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<std::wstring, std::wstring> 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; bool translateSelectedOnly = true, rateLimitAll = true, rateLimitSelected = true, useCache = true, useFilter = true;
int tokenCount = 10, tokenRestoreDelay = 60000, maxSentenceSize = 1000; int tokenCount = 10, tokenRestoreDelay = 60000, maxSentenceSize = 1000;
enum KeyType { CAT, REST }; enum KeyType { CAT, REST };
int keyType = REST; int keyType = REST;
enum PlanLevel { FREE, PAID };
int planLevel = PAID;
std::pair<bool, std::wstring> Translate(const std::wstring& text) std::pair<bool, std::wstring> 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{ if (HttpRequest httpRequest{
L"Mozilla/5.0 Textractor", 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", 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()) + 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" 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", 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", 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()) + 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.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,
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 "}]
@ -92,7 +143,7 @@ std::pair<bool, std::wstring> Translate(const std::wstring& text)
"priority": -1, "priority": -1,
"timestamp": %lld, "timestamp": %lld,
"lang": { "lang": {
"target_lang": "%S", "target_lang": "%.2S",
"source_lang_user_selected": "%S" "source_lang_user_selected": "%S"
}, },
"jobs": [{ "jobs": [{
@ -105,7 +156,7 @@ std::pair<bool, std::wstring> 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 // 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

@ -1,45 +1,105 @@
#include "qtcommon.h" #include "qtcommon.h"
#include "translatewrapper.h"
#include "devtools.h" #include "devtools.h"
extern const wchar_t* ERROR_START_CHROME; extern const wchar_t* ERROR_START_CHROME;
extern const wchar_t* TRANSLATION_ERROR; extern const wchar_t* TRANSLATION_ERROR;
extern Synchronized<std::wstring> translateTo, translateFrom;
const char* TRANSLATION_PROVIDER = "DevTools DeepL Translate"; const char* TRANSLATION_PROVIDER = "DevTools DeepL Translate";
const char* GET_API_KEY_FROM = nullptr; 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<std::wstring, std::wstring> 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; bool translateSelectedOnly = true, rateLimitAll = false, rateLimitSelected = false, useCache = true, useFilter = true;
int tokenCount = 30, tokenRestoreDelay = 60000, maxSentenceSize = 2500; 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) BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{ {
switch (ul_reason_for_call) switch (ul_reason_for_call)
@ -58,19 +118,22 @@ BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved
return TRUE; return TRUE;
} }
std::pair<bool, std::wstring> Translate(const std::wstring& text) std::pair<bool, std::wstring> Translate(const std::wstring& text, TranslationParam tlp)
{ {
if (!DevTools::Connected()) return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, ERROR_START_CHROME) }; if (!DevTools::Connected()) return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, ERROR_START_CHROME) };
// 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("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":"
DevTools::SendRequest("Runtime.evaluate", FormatString(LR"({"expression":" document.querySelector('.lmt__language_select--source').querySelector('button').click();
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.evaluate(`//button[contains(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();
"})", S(std::find_if(languages.begin(), languages.end(), [end = S(translateFrom.Copy())](const QString& language) { return language.endsWith(end); })->split(":")[0]))); 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)) for (int retry = 0; ++retry < 100; Sleep(100))
if (auto translation = Copy(DevTools::SendRequest("Runtime.evaluate", if (auto translation = Copy(DevTools::SendRequest("Runtime.evaluate",

View File

@ -1,36 +1,54 @@
#include "qtcommon.h" #include "qtcommon.h"
#include "translatewrapper.h"
#include "devtools.h" #include "devtools.h"
extern const wchar_t* ERROR_START_CHROME; extern const wchar_t* ERROR_START_CHROME;
extern const wchar_t* TRANSLATION_ERROR; extern const wchar_t* TRANSLATION_ERROR;
extern Synchronized<std::wstring> translateTo, translateFrom;
const char* TRANSLATION_PROVIDER = "DevTools Papago Translate"; const char* TRANSLATION_PROVIDER = "DevTools Papago Translate";
const char* GET_API_KEY_FROM = nullptr; 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<std::wstring, std::wstring> 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; bool translateSelectedOnly = true, rateLimitAll = false, rateLimitSelected = false, useCache = true, useFilter = true;
int tokenCount = 30, tokenRestoreDelay = 60000, maxSentenceSize = 2500; 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) BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{ {
switch (ul_reason_for_call) switch (ul_reason_for_call)
@ -49,13 +67,13 @@ BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved
return TRUE; return TRUE;
} }
std::pair<bool, std::wstring> Translate(const std::wstring& text) std::pair<bool, std::wstring> Translate(const std::wstring& text, TranslationParam tlp)
{ {
if (!DevTools::Connected()) return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, ERROR_START_CHROME) }; if (!DevTools::Connected()) return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, ERROR_START_CHROME) };
// 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("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)) for (int retry = 0; ++retry < 100; Sleep(100))
if (auto translation = Copy(DevTools::SendRequest("Runtime.evaluate", if (auto translation = Copy(DevTools::SendRequest("Runtime.evaluate",
LR"({"expression":"document.querySelector('#txtTarget').textContent.trim() ","returnByValue":true})" LR"({"expression":"document.querySelector('#txtTarget').textContent.trim() ","returnByValue":true})"

View File

@ -1,34 +1,120 @@
#include "qtcommon.h" #include "qtcommon.h"
#include "translatewrapper.h"
#include "devtools.h" #include "devtools.h"
extern const wchar_t* ERROR_START_CHROME; extern const wchar_t* ERROR_START_CHROME;
extern const wchar_t* TRANSLATION_ERROR; extern const wchar_t* TRANSLATION_ERROR;
extern Synchronized<std::wstring> translateTo, translateFrom;
extern QFormLayout* display;
extern Settings settings;
const char* TRANSLATION_PROVIDER = "DevTools Systran Translate"; const char* TRANSLATION_PROVIDER = "DevTools Systran Translate";
const char* GET_API_KEY_FROM = nullptr; 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<std::wstring, std::wstring> 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; bool translateSelectedOnly = true, rateLimitAll = false, rateLimitSelected = false, useCache = true, useFilter = true;
int tokenCount = 30, tokenRestoreDelay = 60000, maxSentenceSize = 2500; 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) BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{ {
switch (ul_reason_for_call) switch (ul_reason_for_call)
@ -47,14 +133,17 @@ BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved
return TRUE; return TRUE;
} }
std::pair<bool, std::wstring> Translate(const std::wstring& text) std::pair<bool, std::wstring> Translate(const std::wstring& text, TranslationParam tlp)
{ {
if (!DevTools::Connected()) return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, ERROR_START_CHROME) }; if (!DevTools::Connected()) return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, ERROR_START_CHROME) };
// 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("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)) for (int retry = 0; ++retry < 100; Sleep(100))
if (auto translation = Copy(DevTools::SendRequest("Runtime.evaluate", if (auto translation = Copy(DevTools::SendRequest("Runtime.evaluate",
LR"({"expression":"document.querySelector('#outputEditor').textContent.trim() ","returnByValue":true})" LR"({"expression":"document.querySelector('#outputEditor').textContent.trim() ","returnByValue":true})"

View File

@ -1,140 +1,250 @@
#include "qtcommon.h" #include "qtcommon.h"
#include "translatewrapper.h"
#include "network.h" #include "network.h"
#include <ctime>
extern const wchar_t* TRANSLATION_ERROR; extern const wchar_t* TRANSLATION_ERROR;
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";
QStringList languages extern const QStringList languagesTo
{ {
"Afrikaans: af", "Afrikaans",
"Albanian: sq", "Albanian",
"Amharic: am", "Amharic",
"Arabic: ar", "Arabic",
"Armenian: hy", "Armenian",
"Azerbaijani: az", "Azerbaijani",
"Basque: eu", "Basque",
"Belarusian: be", "Belarusian",
"Bengali: bn", "Bengali",
"Bosnian: bs", "Bosnian",
"Bulgarian: bg", "Bulgarian",
"Catalan: ca", "Catalan",
"Cebuano: ceb", "Cebuano",
"Chichewa: ny", "Chichewa",
"Chinese (simplified): zh", "Chinese (Simplified)",
"Chinese (traditional): zh-TW", "Chinese (Traditional)",
"Corsican: co", "Corsican",
"Croatian: hr", "Croatian",
"Czech: cs", "Czech",
"Danish: da", "Danish",
"Dutch: nl", "Dutch",
"English: en", "English",
"Esperanto: eo", "Esperanto",
"Estonian: et", "Estonian",
"Filipino: tl", "Filipino",
"Finnish: fi", "Finnish",
"French: fr", "French",
"Frisian: fy", "Frisian",
"Galician: gl", "Galician",
"Georgian: ka", "Georgian",
"German: de", "German",
"Greek: el", "Greek",
"Gujarati: gu", "Gujarati",
"Haitian Creole: ht", "Haitian Creole",
"Hausa: ha", "Hausa",
"Hawaiian: haw", "Hawaiian",
"Hebrew: iw", "Hebrew",
"Hindi: hi", "Hindi",
"Hmong: hmn", "Hmong",
"Hungarian: hu", "Hungarian",
"Icelandic: is", "Icelandic",
"Igbo: ig", "Igbo",
"Indonesian: id", "Indonesian",
"Irish: ga", "Irish",
"Italian: it", "Italian",
"Japanese: ja", "Japanese",
"Javanese: jw", "Javanese",
"Kannada: kn", "Kannada",
"Kazakh: kk", "Kazakh",
"Khmer: km", "Khmer",
"Kinyarwanda: rw", "Kinyarwanda",
"Korean: ko", "Korean",
"Kurdish (Kurmanji): ku", "Kurdish (Kurmanji)",
"Kyrgyz: ky", "Kyrgyz",
"Lao: lo", "Lao",
"Latin: la", "Latin",
"Latvian: lv", "Latvian",
"Lithuanian: lt", "Lithuanian",
"Luxembourgish: lb", "Luxembourgish",
"Macedonian: mk", "Macedonian",
"Malagasy: mg", "Malagasy",
"Malay: ms", "Malay",
"Malayalam: ml", "Malayalam",
"Maltese: mt", "Maltese",
"Maori: mi", "Maori",
"Marathi: mr", "Marathi",
"Mongolian: mn", "Mongolian",
"Myanmar (Burmese): my", "Myanmar (Burmese)",
"Nepali: ne", "Nepali",
"Norwegian: no", "Norwegian",
"Odia (Oriya): or", "Odia (Oriya)",
"Pashto: ps", "Pashto",
"Persian: fa", "Persian",
"Polish: pl", "Polish",
"Portuguese: pt", "Portuguese",
"Punjabi: pa", "Punjabi",
"Romanian: ro", "Romanian",
"Russian: ru", "Russian",
"Samoan: sm", "Samoan",
"Scots Gaelic: gd", "Scots Gaelic",
"Serbian: sr", "Serbian",
"Sesotho: st", "Sesotho",
"Shona: sn", "Shona",
"Sindhi: sd", "Sindhi",
"Sinhala: si", "Sinhala",
"Slovak: sk", "Slovak",
"Slovenian: sl", "Slovenian",
"Somali: so", "Somali",
"Spanish: es", "Spanish",
"Sundanese: su", "Sundanese",
"Swahili: sw", "Swahili",
"Swedish: sv", "Swedish",
"Tajik: tg", "Tajik",
"Tamil: ta", "Tamil",
"Tatar: tt", "Tatar",
"Telugu: te", "Telugu",
"Thai: th", "Thai",
"Turkish: tr", "Turkish",
"Turkmen: tk", "Turkmen",
"Ukrainian: uk", "Ukrainian",
"Urdu: ur", "Urdu",
"Uyghur: ug", "Uyghur",
"Uzbek: uz", "Uzbek",
"Vietnamese: vi", "Vietnamese",
"Welsh: cy", "Welsh",
"Xhosa: xh", "Xhosa",
"Yiddish: yi", "Yiddish",
"Yoruba: yo", "Yoruba",
"Zulu: zu" "Zulu",
}, languagesFrom = languagesTo;
extern const std::unordered_map<std::wstring, std::wstring> 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; bool translateSelectedOnly = false, rateLimitAll = true, rateLimitSelected = false, useCache = true, useFilter = true;
int tokenCount = 30, tokenRestoreDelay = 60000, maxSentenceSize = 1000; 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, 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{ 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%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))) 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() };
@ -146,7 +256,7 @@ std::pair<bool, std::wstring> Translate(const std::wstring& text)
L"Mozilla/5.0 Textractor", L"Mozilla/5.0 Textractor",
L"translate.google.com", L"translate.google.com",
L"GET", 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); auto start = httpRequest.response.find(L"result-container\">"), end = httpRequest.response.find(L'<', start);

View File

@ -1,5 +1,6 @@
#include "qtcommon.h" #include "qtcommon.h"
#include "extension.h" #include "extension.h"
#include "translatewrapper.h"
#include "blockmarkup.h" #include "blockmarkup.h"
#include "network.h" #include "network.h"
#include <map> #include <map>
@ -22,22 +23,19 @@ 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 const QStringList languagesTo, languagesFrom;
extern std::wstring autoDetectLanguage;
extern bool translateSelectedOnly, rateLimitAll, rateLimitSelected, useCache, useFilter; extern bool translateSelectedOnly, rateLimitAll, rateLimitSelected, useCache, useFilter;
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, TranslationParam tlp);
// backwards compatibility
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", translateFrom = L"auto", authKey;
namespace namespace
{ {
Synchronized<TranslationParam> tlp;
Synchronized<std::map<std::wstring, std::wstring>> translationCache; Synchronized<std::map<std::wstring, std::wstring>> translationCache;
int savedSize; int savedSize;
void SaveCache() void SaveCache()
@ -60,23 +58,22 @@ public:
settings.beginGroup(TRANSLATION_PROVIDER); settings.beginGroup(TRANSLATION_PROVIDER);
auto translateToCombo = new QComboBox(this); auto translateToCombo = new QComboBox(this);
translateToCombo->addItems(languages); translateToCombo->addItems(languagesTo);
int language = -1; int i = -1;
if (settings.contains(LANGUAGE)) language = translateToCombo->findText(settings.value(LANGUAGE).toString(), Qt::MatchEndsWith); if (settings.contains(TRANSLATE_TO)) i = translateToCombo->findText(settings.value(TRANSLATE_TO).toString());
if (settings.contains(TRANSLATE_TO)) language = translateToCombo->findText(settings.value(TRANSLATE_TO).toString(), Qt::MatchEndsWith); if (i < 0) i = translateToCombo->findText(NATIVE_LANGUAGE, Qt::MatchStartsWith);
if (language < 0) language = translateToCombo->findText(NATIVE_LANGUAGE, Qt::MatchStartsWith); if (i < 0) i = translateToCombo->findText("English", Qt::MatchStartsWith);
if (language < 0) language = translateToCombo->findText("English", Qt::MatchStartsWith); translateToCombo->setCurrentIndex(i);
translateToCombo->setCurrentIndex(language);
SaveTranslateTo(translateToCombo->currentText()); SaveTranslateTo(translateToCombo->currentText());
display->addRow(TRANSLATE_TO, translateToCombo); display->addRow(TRANSLATE_TO, translateToCombo);
connect(translateToCombo, &QComboBox::currentTextChanged, this, &Window::SaveTranslateTo); connect(translateToCombo, &QComboBox::currentTextChanged, this, &Window::SaveTranslateTo);
languages.push_front("?: " + S(autoDetectLanguage));
auto translateFromCombo = new QComboBox(this); auto translateFromCombo = new QComboBox(this);
translateFromCombo->addItems(languages); translateFromCombo->addItem("?");
language = -1; translateFromCombo->addItems(languagesFrom);
if (settings.contains(TRANSLATE_FROM)) language = translateFromCombo->findText(settings.value(TRANSLATE_FROM).toString(), Qt::MatchEndsWith); i = -1;
if (language < 0) language = translateFromCombo->findText("?", Qt::MatchStartsWith); if (settings.contains(TRANSLATE_FROM)) i = translateFromCombo->findText(settings.value(TRANSLATE_FROM).toString());
translateFromCombo->setCurrentIndex(language); if (i < 0) i = 0;
translateFromCombo->setCurrentIndex(i);
SaveTranslateFrom(translateFromCombo->currentText()); SaveTranslateFrom(translateFromCombo->currentText());
display->addRow(TRANSLATE_FROM, translateFromCombo); display->addRow(TRANSLATE_FROM, translateFromCombo);
connect(translateFromCombo, &QComboBox::currentTextChanged, this, &Window::SaveTranslateFrom); connect(translateFromCombo, &QComboBox::currentTextChanged, this, &Window::SaveTranslateFrom);
@ -110,8 +107,8 @@ public:
if (GET_API_KEY_FROM) if (GET_API_KEY_FROM)
{ {
auto keyEdit = new QLineEdit(settings.value(API_KEY).toString(), this); auto keyEdit = new QLineEdit(settings.value(API_KEY).toString(), this);
authKey->assign(S(keyEdit->text())); tlp->authKey = S(keyEdit->text());
QObject::connect(keyEdit, &QLineEdit::textChanged, [](QString key) { settings.setValue(API_KEY, S(authKey->assign(S(key)))); }); QObject::connect(keyEdit, &QLineEdit::textChanged, [](QString key) { settings.setValue(API_KEY, S(tlp->authKey = S(key))); });
auto keyLabel = new QLabel(QString("<a href=\"%1\">%2</a>").arg(GET_API_KEY_FROM, API_KEY), this); auto keyLabel = new QLabel(QString("<a href=\"%1\">%2</a>").arg(GET_API_KEY_FROM, API_KEY), this);
keyLabel->setOpenExternalLinks(true); keyLabel->setOpenExternalLinks(true);
display->addRow(keyLabel, keyEdit); display->addRow(keyLabel, keyEdit);
@ -139,11 +136,11 @@ public:
private: private:
void SaveTranslateTo(QString language) 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) 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; } 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 (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 (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; else translation = TOO_MANY_TRANS_REQUESTS;
if (useFilter) Trim(translation); if (useFilter) Trim(translation);
if (cache) translationCache->try_emplace(sentence, translation); if (cache) translationCache->try_emplace(sentence, translation);
@ -197,4 +194,13 @@ bool ProcessSentence(std::wstring& sentence, SentenceInfo sentenceInfo)
return true; return true;
} }
TEST(assert(Translate(L"こんにちは").second.find(L"ello") != std::string::npos)); extern const std::unordered_map<std::wstring, std::wstring> 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"?"));
}
);

View File

@ -0,0 +1,6 @@
#pragma once
struct TranslationParam
{
std::wstring translateTo, translateFrom, authKey;
};

View File

@ -324,7 +324,7 @@ Clic y arrastra los bordes de la ventana para moverla, o en la esquina inferior
#endif // SPANISH #endif // SPANISH
#ifdef SIMPLIFIED_CHINESE #ifdef SIMPLIFIED_CHINESE
NATIVE_LANGUAGE = "Chinese (simplified)"; NATIVE_LANGUAGE = "Chinese (Simplified)";
ATTACH = u8"附加到游戏"; ATTACH = u8"附加到游戏";
LAUNCH = u8"启动游戏"; LAUNCH = u8"启动游戏";
CONFIG = u8"配置游戏"; CONFIG = u8"配置游戏";