diff --git a/GUI/extenwindow.cpp b/GUI/extenwindow.cpp index 2999a16..157236b 100644 --- a/GUI/extenwindow.cpp +++ b/GUI/extenwindow.cpp @@ -110,7 +110,7 @@ namespace QAction addExtension(ADD_EXTENSION), removeExtension(REMOVE_EXTENSION); if (auto action = QMenu::exec({ &addExtension, &removeExtension }, ui.extenList->mapToGlobal(point), nullptr, This)) if (action == &removeExtension) Delete(); - else if (QString extenFile = QFileDialog::getOpenFileName(This, ADD_EXTENSION, ".", EXTENSIONS + QString(" (*.xdll)\nLibraries (*.dll)")); !extenFile.isEmpty()) Add(extenFile); + else if (QString extenFile = QFileDialog::getOpenFileName(This, ADD_EXTENSION, ".", EXTENSIONS + QString(" (*.xdll);;Libraries (*.dll)")); !extenFile.isEmpty()) Add(extenFile); } } diff --git a/GUI/host/textthread.cpp b/GUI/host/textthread.cpp index c3adc2b..b55d165 100644 --- a/GUI/host/textthread.cpp +++ b/GUI/host/textthread.cpp @@ -104,7 +104,7 @@ void TextThread::Flush() for (auto& sentence : sentences) { totalSize += sentence.size(); - sentence.erase(std::remove(sentence.begin(), sentence.end(), L'\0'), sentence.end()); + sentence.erase(std::remove(sentence.begin(), sentence.end(), 0), sentence.end()); if (Output(*this, sentence)) storage->append(sentence); } diff --git a/extensions/CMakeLists.txt b/extensions/CMakeLists.txt index cddff5b..e4a5232 100644 --- a/extensions/CMakeLists.txt +++ b/extensions/CMakeLists.txt @@ -47,6 +47,8 @@ target_link_libraries(Regex\ Filter Qt5::Widgets) target_link_libraries(Styler Qt5::Widgets) target_link_libraries(Thread\ Linker Qt5::Widgets) +add_custom_target(Cleaner ALL COMMAND del *.xdll WORKING_DIRECTORY ${CMAKE_FINAL_OUTPUT_DIRECTORY}) + if (NOT EXISTS ${CMAKE_FINAL_OUTPUT_DIRECTORY}/Qt5WebSockets.dll AND NOT EXISTS ${CMAKE_FINAL_OUTPUT_DIRECTORY}/Qt5WebSocketsd.dll) add_custom_command(TARGET DevTools\ DeepL\ Translate POST_BUILD diff --git a/extensions/bingtranslate.cpp b/extensions/bingtranslate.cpp index b91474b..4c9eb79 100644 --- a/extensions/bingtranslate.cpp +++ b/extensions/bingtranslate.cpp @@ -80,7 +80,7 @@ QStringList languages }; std::wstring autoDetectLanguage = L"auto-detect"; -bool translateSelectedOnly = false, rateLimitAll = true, rateLimitSelected = false, useCache = true; +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) diff --git a/extensions/deepltranslate.cpp b/extensions/deepltranslate.cpp index 63dfed3..c875b5e 100644 --- a/extensions/deepltranslate.cpp +++ b/extensions/deepltranslate.cpp @@ -11,7 +11,7 @@ const char* TRANSLATION_PROVIDER = "DeepL Translate"; const char* GET_API_KEY_FROM = "https://www.deepl.com/pro.html"; QStringList languages { - "Chinese (simplified): ZH", + "Chinese: ZH", "Dutch: NL", "English: EN", "French: FR", @@ -25,7 +25,7 @@ QStringList languages }; std::wstring autoDetectLanguage = L"auto"; -bool translateSelectedOnly = true, rateLimitAll = true, rateLimitSelected = true, useCache = true; +bool translateSelectedOnly = true, rateLimitAll = true, rateLimitSelected = true, useCache = true, useFilter = true; int tokenCount = 10, tokenRestoreDelay = 60000, maxSentenceSize = 1000; enum KeyType { CAT, REST }; diff --git a/extensions/devtools.cpp b/extensions/devtools.cpp index a2bc204..156f99c 100644 --- a/extensions/devtools.cpp +++ b/extensions/devtools.cpp @@ -14,8 +14,7 @@ namespace auto _ = ([] { QObject::connect(&webSocket, &QWebSocket::stateChanged, - [](QAbstractSocket::SocketState state) { OnStatusChanged(QMetaEnum::fromType().valueToKey(state)); } - ); + [](QAbstractSocket::SocketState state) { OnStatusChanged(QMetaEnum::fromType().valueToKey(state)); }); QObject::connect(&webSocket, &QWebSocket::textMessageReceived, [](QString message) { auto result = JSON::Parse(S(message)); @@ -84,7 +83,7 @@ namespace DevTools if (GetExitCodeProcess(processInfo.hProcess, &exitCode) && exitCode == STILL_ACTIVE) { TerminateProcess(processInfo.hProcess, 0); - WaitForSingleObject(processInfo.hProcess, 100); + WaitForSingleObject(processInfo.hProcess, 2000); CloseHandle(processInfo.hProcess); CloseHandle(processInfo.hThread); } @@ -98,11 +97,11 @@ namespace DevTools return webSocket.state() == QAbstractSocket::ConnectedState; } - JSON::Value SendRequest(const std::wstring& method, const std::wstring& params) + JSON::Value SendRequest(const char* method, const std::wstring& params) { concurrency::task_completion_event> response; int id = idCounter += 1; - auto message = FormatString(LR"({"id":%d,"method":"%s","params":%s})", id, method, params); + auto message = FormatString(LR"({"id":%d,"method":"%S","params":%s})", id, method, params); { std::scoped_lock lock(devToolsMutex); if (webSocket.state() != QAbstractSocket::ConnectedState) return {}; diff --git a/extensions/devtools.h b/extensions/devtools.h index 81c996f..b6267ab 100644 --- a/extensions/devtools.h +++ b/extensions/devtools.h @@ -6,5 +6,5 @@ namespace DevTools void Start(const std::wstring& path, std::function statusChanged, bool headless); void Close(); bool Connected(); - JSON::Value SendRequest(const std::wstring& method, const std::wstring& params = L"{}"); + JSON::Value SendRequest(const char* method, const std::wstring& params = L"{}"); } diff --git a/extensions/devtoolsdeepltranslate.cpp b/extensions/devtoolsdeepltranslate.cpp index affad8b..46b7edd 100644 --- a/extensions/devtoolsdeepltranslate.cpp +++ b/extensions/devtoolsdeepltranslate.cpp @@ -1,17 +1,10 @@ #include "qtcommon.h" #include "devtools.h" +#include +#include #include extern const wchar_t* TRANSLATION_ERROR; -extern Synchronized translateTo, translateFrom; -extern QFormLayout* display; -extern Settings settings; - -const char* TRANSLATION_PROVIDER = "DevTools DeepL Translate"; -const char* GET_API_KEY_FROM = nullptr; -bool translateSelectedOnly = true, rateLimitAll = false, rateLimitSelected = false, useCache = true; -int tokenCount = 30, tokenRestoreDelay = 60000, maxSentenceSize = 2500; - extern const char* CHROME_LOCATION; extern const char* START_DEVTOOLS; extern const char* STOP_DEVTOOLS; @@ -20,9 +13,18 @@ extern const char* DEVTOOLS_STATUS; extern const char* AUTO_START; extern const wchar_t* ERROR_START_CHROME; +extern Synchronized translateTo, translateFrom; +extern QFormLayout* display; +extern Settings settings; + +const char* TRANSLATION_PROVIDER = "DevTools DeepL Translate"; +const char* GET_API_KEY_FROM = nullptr; +bool translateSelectedOnly = true, rateLimitAll = false, rateLimitSelected = false, useCache = true, useFilter = true; +int tokenCount = 30, tokenRestoreDelay = 60000, maxSentenceSize = 2500; + QStringList languages { - "Chinese (simplified): zh", + "Chinese: zh", "Dutch: nl", "English: en", "French: fr", @@ -51,6 +53,18 @@ BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved if (std::filesystem::exists(programFiles)) chromePath = S(programFiles); } auto chromePathEdit = new QLineEdit(chromePath); + static struct : QObject + { + bool eventFilter(QObject* object, QEvent* event) + { + if (auto mouseEvent = dynamic_cast(event)) + if (mouseEvent->button() == Qt::LeftButton) + if (QString chromePath = QFileDialog::getOpenFileName(nullptr, TRANSLATION_PROVIDER, "/", "Chrome (*.exe)"); !chromePath.isEmpty()) + ((QLineEdit*)object)->setText(chromePath); + return false; + } + } chromeSelector; + chromePathEdit->installEventFilter(&chromeSelector); QObject::connect(chromePathEdit, &QLineEdit::textChanged, [chromePathEdit](QString path) { settings.setValue(CHROME_LOCATION, path); }); display->addRow(CHROME_LOCATION, chromePathEdit); auto statusLabel = new QLabel("Stopped"); @@ -58,7 +72,8 @@ BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved auto headlessCheck = new QCheckBox(); headlessCheck->setChecked(settings.value(HEADLESS_MODE, true).toBool()); QObject::connect(headlessCheck, &QCheckBox::clicked, [](bool headless) { settings.setValue(HEADLESS_MODE, headless); }); - QObject::connect(startButton, &QPushButton::clicked, [statusLabel, chromePathEdit, headlessCheck] { + QObject::connect(startButton, &QPushButton::clicked, [statusLabel, chromePathEdit, headlessCheck] + { DevTools::Start( S(chromePathEdit->text()), [statusLabel](QString status) @@ -79,8 +94,10 @@ BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved }) if (auto userAgent = Copy(JSON::Parse(httpRequest.response)[L"User-Agent"].String())) if (userAgent->find(L"Headless") != std::string::npos) - DevTools::SendRequest(L"Network.setUserAgentOverride", - FormatString(LR"({"userAgent":"%s"})", userAgent->replace(userAgent->find(L"Headless"), 8, L""))); + DevTools::SendRequest( + "Network.setUserAgentOverride", + FormatString(LR"({"userAgent":"%s"})", userAgent->replace(userAgent->find(L"Headless"), 8, L"")) + ); }).detach(); }, headlessCheck->isChecked() @@ -91,14 +108,14 @@ BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved buttons->addWidget(startButton); buttons->addWidget(stopButton); display->addRow(HEADLESS_MODE, headlessCheck); - auto autoStartButton = new QCheckBox(); - autoStartButton->setChecked(settings.value(AUTO_START, false).toBool()); - QObject::connect(autoStartButton, &QCheckBox::clicked, [](bool autoStart) { settings.setValue(AUTO_START, autoStart); }); - display->addRow(AUTO_START, autoStartButton); + auto autoStartCheck = new QCheckBox(); + autoStartCheck->setChecked(settings.value(AUTO_START, false).toBool()); + QObject::connect(autoStartCheck, &QCheckBox::clicked, [](bool autoStart) { settings.setValue(AUTO_START, autoStart); }); + display->addRow(AUTO_START, autoStartCheck); display->addRow(buttons); statusLabel->setFrameStyle(QFrame::Panel | QFrame::Sunken); display->addRow(DEVTOOLS_STATUS, statusLabel); - if (autoStartButton->isChecked()) QMetaObject::invokeMethod(startButton, &QPushButton::click, Qt::QueuedConnection); + if (autoStartCheck->isChecked()) QMetaObject::invokeMethod(startButton, &QPushButton::click, Qt::QueuedConnection); } break; case DLL_PROCESS_DETACH: @@ -116,16 +133,20 @@ std::pair Translate(const std::wstring& text) // DevTools can't handle concurrent translations yet static std::mutex translationMutex; std::scoped_lock lock(translationMutex); - DevTools::SendRequest(L"Page.navigate", FormatString(LR"({"url":"https://www.deepl.com/translator#%s/%s/%s"})", translateFrom.Copy(), translateTo.Copy(), Escape(text))); + DevTools::SendRequest("Page.navigate", FormatString(LR"({"url":"https://www.deepl.com/translator#any/%s/%s"})", translateTo.Copy(), Escape(text))); + + 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]))); + for (int retry = 0; ++retry < 100; Sleep(100)) - if (auto translation = Copy( - DevTools::SendRequest(L"Runtime.evaluate", LR"({"expression":"document.querySelector('#target-dummydiv').innerHTML","returnByValue":true})")[L"result"][L"value"].String() - )) if (!translation->empty()) return { true, translation.value() }; - if (auto errorMessage = Copy( - DevTools::SendRequest( - L"Runtime.evaluate", - LR"({"expression":"document.querySelector('div.lmt__system_notification').innerHTML","returnByValue":true})" - )[L"result"][L"value"].String() - )) return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, errorMessage.value()) }; + if (auto translation = Copy(DevTools::SendRequest("Runtime.evaluate", + LR"({"expression":"document.querySelector('#target-dummydiv').innerHTML.trim() ","returnByValue":true})" + )[L"result"][L"value"].String())) if (!translation->empty()) return { true, translation.value() }; + if (auto errorMessage = Copy(DevTools::SendRequest("Runtime.evaluate", + LR"({"expression":"document.querySelector('div.lmt__system_notification').innerHTML","returnByValue":true})" + )[L"result"][L"value"].String())) return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, errorMessage.value()) }; return { false, TRANSLATION_ERROR }; } diff --git a/extensions/googletranslate.cpp b/extensions/googletranslate.cpp index bf7f948..d1c2951 100644 --- a/extensions/googletranslate.cpp +++ b/extensions/googletranslate.cpp @@ -122,7 +122,7 @@ QStringList languages }; std::wstring autoDetectLanguage = L"auto"; -bool translateSelectedOnly = false, rateLimitAll = true, rateLimitSelected = false, useCache = true; +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) @@ -145,34 +145,12 @@ std::pair Translate(const std::wstring& text) if (HttpRequest httpRequest{ L"Mozilla/5.0 Textractor", L"translate.google.com", - L"POST", - L"/_/TranslateWebserverUi/data/batchexecute?rpcids=MkEWBc", - "f.req=" + Escape(WideStringToString( - FormatString(LR"([[["MkEWBc","[[\"%s\",\"%s\",\"%s\",true],[null]]",null,"generic"]]])", JSON::Escape((JSON::Escape(text))), translateFrom.Copy(), translateTo.Copy()) - )), - L"Content-Type: application/x-www-form-urlencoded" + L"GET", + FormatString(L"/m?sl=%s&tl=%s&q=%s", translateFrom.Copy(), translateTo.Copy(), Escape(text)).c_str() }) { - if (auto start = httpRequest.response.find(L"[["); start != std::string::npos) - { - if (auto blob = Copy(JSON::Parse(httpRequest.response.substr(start))[0][2].String())) if (auto translations = Copy(JSON::Parse(blob.value())[1][0].Array())) - { - std::wstring translation; - if (translations->size() == 1) - { - if (translations = Copy(translations.value()[0][5].Array())) - for (const auto& sentence : translations.value()) - if (sentence[0].String()) (translation += *sentence[0].String()) += L" "; - } - else - { - for (const auto& conjugation : translations.value()) - if (auto sentence = conjugation[0].String()) if (auto gender = conjugation[2].String()) translation += FormatString(L"%s %s\n", *sentence, *gender); - } - if (!translation.empty()) return { true, translation }; - return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, blob.value()) }; - } - } + auto start = httpRequest.response.find(L"result-container\">") + 18, end = httpRequest.response.find(L'<', start); + if (start != end) return { true, HTML::Unescape(httpRequest.response.substr(start, end - start)) }; return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, httpRequest.response) }; } else return { false, FormatString(L"%s (code=%u)", TRANSLATION_ERROR, httpRequest.errorCode) }; diff --git a/extensions/network.h b/extensions/network.h index 2e21650..061e90c 100644 --- a/extensions/network.h +++ b/extensions/network.h @@ -32,6 +32,36 @@ struct HttpRequest std::wstring Escape(const std::wstring& text); std::string Escape(const std::string& text); +namespace HTML +{ + template + std::basic_string Unescape(std::basic_string text) + { + constexpr C + lt[] = { '&', 'l', 't', ';' }, + gt[] = { '&', 'g', 't', ';' }, + apos1[] = { '&', 'a', 'p', 'o', 's', ';' }, + apos2[] = { '&', '#', '3', '9', ';' }, + apos3[] = { '&', '#', 'x', '2', '7', ';' }, + apos4[] = { '&', '#', 'X', '2', '7', ';' }, + quot[] = { '&', 'q', 'u', 'o', 't', ';' }, + amp[] = { '&', 'a', 'm', 'p', ';' }; + for (int i = 0; i < text.size(); ++i) + if (text[i] == '&') + for (auto [original, length, replacement] : Array{ + { lt, std::size(lt), '<' }, + { gt, std::size(gt), '>' }, + { apos1, std::size(apos1), '\'' }, + { apos2, std::size(apos2), '\'' }, + { apos3, std::size(apos3), '\'' }, + { apos4, std::size(apos4), '\'' }, + { quot, std::size(quot), '"' }, + { amp, std::size(amp), '&' } + }) if (std::char_traits::compare(text.data() + i, original, length) == 0) text.replace(i, length, 1, replacement); + return text; + } +} + namespace JSON { template @@ -136,7 +166,7 @@ namespace JSON if (SkipWhitespace()) return {}; - static C nullStr[] = { 'n', 'u', 'l', 'l' }, trueStr[] = { 't', 'r', 'u', 'e' }, falseStr[] = { 'f', 'a', 'l', 's', 'e' }; + constexpr C nullStr[] = { 'n', 'u', 'l', 'l' }, trueStr[] = { 't', 'r', 'u', 'e' }, falseStr[] = { 'f', 'a', 'l', 's', 'e' }; if (ch == nullStr[0]) if (std::char_traits::compare(text.data() + i, nullStr, std::size(nullStr)) == 0) return i += std::size(nullStr), nullptr; else return {}; diff --git a/extensions/removerepeatchar.cpp b/extensions/removerepeatchar.cpp index 9643a8e..6da1f2c 100644 --- a/extensions/removerepeatchar.cpp +++ b/extensions/removerepeatchar.cpp @@ -6,7 +6,7 @@ bool ProcessSentence(std::wstring& sentence, SentenceInfo sentenceInfo) std::vector repeatNumbers(sentence.size() + 1, 0); int repeatNumber = 1; - wchar_t prevChar = L'\0'; + wchar_t prevChar = 0; for (auto nextChar : sentence) { if (nextChar == prevChar) diff --git a/extensions/translatewrapper.cpp b/extensions/translatewrapper.cpp index f5ce104..65f2cc6 100644 --- a/extensions/translatewrapper.cpp +++ b/extensions/translatewrapper.cpp @@ -13,6 +13,7 @@ extern const char* TRANSLATE_SELECTED_THREAD_ONLY; extern const char* RATE_LIMIT_ALL_THREADS; extern const char* RATE_LIMIT_SELECTED_THREAD; extern const char* USE_TRANS_CACHE; +extern const char* FILTER_GARBAGE; extern const char* RATE_LIMIT_TOKEN_COUNT; extern const char* RATE_LIMIT_TOKEN_RESTORE_DELAY; extern const char* MAX_SENTENCE_SIZE; @@ -23,7 +24,7 @@ extern const char* TRANSLATION_PROVIDER; extern const char* GET_API_KEY_FROM; extern QStringList languages; extern std::wstring autoDetectLanguage; -extern bool translateSelectedOnly, rateLimitAll, rateLimitSelected, useCache; +extern bool translateSelectedOnly, rateLimitAll, rateLimitSelected, useCache, useFilter; extern int tokenCount, tokenRestoreDelay, maxSentenceSize; std::pair Translate(const std::wstring& text); @@ -84,6 +85,7 @@ public: { rateLimitAll, RATE_LIMIT_ALL_THREADS }, { rateLimitSelected, RATE_LIMIT_SELECTED_THREAD }, { useCache, USE_TRANS_CACHE }, + { useFilter, FILTER_GARBAGE } }) { value = settings.value(label, value).toBool(); @@ -165,7 +167,7 @@ bool ProcessSentence(std::wstring& sentence, SentenceInfo sentenceInfo) Synchronized> tokens; } rateLimiter; - auto StripWhitespace = [](std::wstring& text) + auto Trim = [](std::wstring& text) { text.erase(text.begin(), std::find_if_not(text.begin(), text.end(), iswspace)); text.erase(std::find_if_not(text.rbegin(), text.rend(), iswspace).base(), text.end()); @@ -173,7 +175,11 @@ bool ProcessSentence(std::wstring& sentence, SentenceInfo sentenceInfo) bool cache = false; std::wstring translation; - StripWhitespace(sentence); + if (useFilter) + { + Trim(sentence); + sentence.erase(std::find_if(sentence.begin(), sentence.end(), [](wchar_t ch) { return ch < ' ' && ch != '\n'; }), sentence.end()); + } if (useCache) { auto translationCache = ::translationCache.Acquire(); @@ -182,7 +188,7 @@ bool ProcessSentence(std::wstring& sentence, SentenceInfo sentenceInfo) if (translation.empty() && (!translateSelectedOnly || sentenceInfo["current select"])) if (rateLimiter.Request() || !rateLimitAll || (!rateLimitSelected && sentenceInfo["current select"])) std::tie(cache, translation) = Translate(sentence); else translation = TOO_MANY_TRANS_REQUESTS; - StripWhitespace(translation); + if (useFilter) Trim(translation); if (cache) translationCache->try_emplace(sentence, translation); if (cache && translationCache->size() > savedSize + 50) SaveCache(); diff --git a/text.cpp b/text.cpp index a7ebd8e..a536109 100644 --- a/text.cpp +++ b/text.cpp @@ -57,7 +57,7 @@ Negatives for data_offset/split_offset refer to registers -C for RAX, -14 for RBX, -1C for RCX, -24 for RDX, and so on for RSP, RBP, RSI, RDI, R8-R15 * means dereference pointer+deref_offset)"; const char* SAVE_SETTINGS = u8"Save settings"; -const char* EXTEN_WINDOW_INSTRUCTIONS = u8R"(Right click the list to add/remove extensions +const char* EXTEN_WINDOW_INSTRUCTIONS = u8R"(Right click the list to add or remove extensions Drag and drop extensions within the list to reorder them (Extensions are used from top to bottom: order DOES matter))"; const char* ADD_EXTENSION = u8"Add extension"; @@ -97,14 +97,14 @@ const char* MAX_HISTORY_SIZE = u8"Max history size"; const char* CONFIG_JP_LOCALE = u8"Launch with JP locale"; const wchar_t* CONSOLE = L"Console"; const wchar_t* CLIPBOARD = L"Clipboard"; -const wchar_t* ABOUT = L"Textractor " ARCH L" v" VERSION LR"( made by me: Artikash (email: akashmozumdar@gmail.com) +const wchar_t* ABOUT = L"Textractor " ARCH L" v" VERSION LR"( made by Artikash (email: akashmozumdar@gmail.com) Project homepage: https://github.com/Artikash/Textractor Tutorial video: https://tinyurl.com/textractor-tutorial FAQ: https://github.com/Artikash/Textractor/wiki/FAQ -Please contact me with any problems, feature requests, or questions relating to Textractor +Please contact Artikash with any problems, feature requests, or questions relating to Textractor You can do so via the project homepage (issues section) or via email Source code available under GPLv3 at project homepage -If you like this project, please tell everyone about it :))"; +If you like this project, please tell everyone it's time to put down AGTH :))"; const wchar_t* CL_OPTIONS = LR"(usage: Textractor [-p{process id|"process name"}]... example: Textractor -p4466 -p"My Game.exe" tries to inject processes with id 4466 or with name My Game.exe)"; const wchar_t* UPDATE_AVAILABLE = L"Update available: download it from https://github.com/Artikash/Textractor/releases"; @@ -136,6 +136,7 @@ const char* HIJACK_ERROR = u8"Textractor: Hijack ERROR"; const char* COULD_NOT_FIND = u8"Textractor: could not find text"; const char* TRANSLATE_TO = u8"Translate to"; const char* TRANSLATE_FROM = u8"Translate from"; +const char* FILTER_GARBAGE = u8"Filter garbage characters"; const char* TRANSLATE_SELECTED_THREAD_ONLY = u8"Translate selected text thread only"; const char* RATE_LIMIT_ALL_THREADS = u8"Rate limit all text threads"; const char* RATE_LIMIT_SELECTED_THREAD = u8"Rate limit selected text thread"; @@ -146,7 +147,7 @@ const wchar_t* TOO_MANY_TRANS_REQUESTS = L"Rate limit exceeded: refuse to make m const wchar_t* TRANSLATION_ERROR = L"Error while translating"; const char* USE_PREV_SENTENCE_CONTEXT = u8"Use previous sentence as context"; const char* API_KEY = u8"API key"; -const char* CHROME_LOCATION = "Google Chrome file location"; +const char* CHROME_LOCATION = u8"Google Chrome file location"; const char* START_DEVTOOLS = u8"Start DevTools"; const char* STOP_DEVTOOLS = u8"Stop DevTools"; const char* HEADLESS_MODE = u8"Headless mode";