fix google translate and devtools deepl translate, add chrome file selector, add garbage filter

This commit is contained in:
Akash Mozumdar 2021-03-09 21:32:56 -07:00
parent 5537679442
commit 8543d49192
13 changed files with 114 additions and 77 deletions

View File

@ -110,7 +110,7 @@ namespace
QAction addExtension(ADD_EXTENSION), removeExtension(REMOVE_EXTENSION); QAction addExtension(ADD_EXTENSION), removeExtension(REMOVE_EXTENSION);
if (auto action = QMenu::exec({ &addExtension, &removeExtension }, ui.extenList->mapToGlobal(point), nullptr, This)) if (auto action = QMenu::exec({ &addExtension, &removeExtension }, ui.extenList->mapToGlobal(point), nullptr, This))
if (action == &removeExtension) Delete(); 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);
} }
} }

View File

@ -104,7 +104,7 @@ void TextThread::Flush()
for (auto& sentence : sentences) for (auto& sentence : sentences)
{ {
totalSize += sentence.size(); 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); if (Output(*this, sentence)) storage->append(sentence);
} }

View File

@ -47,6 +47,8 @@ target_link_libraries(Regex\ Filter Qt5::Widgets)
target_link_libraries(Styler Qt5::Widgets) target_link_libraries(Styler Qt5::Widgets)
target_link_libraries(Thread\ Linker 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) 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 add_custom_command(TARGET DevTools\ DeepL\ Translate
POST_BUILD POST_BUILD

View File

@ -80,7 +80,7 @@ QStringList languages
}; };
std::wstring autoDetectLanguage = L"auto-detect"; 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; 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)

View File

@ -11,7 +11,7 @@ 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 QStringList languages
{ {
"Chinese (simplified): ZH", "Chinese: ZH",
"Dutch: NL", "Dutch: NL",
"English: EN", "English: EN",
"French: FR", "French: FR",
@ -25,7 +25,7 @@ QStringList languages
}; };
std::wstring autoDetectLanguage = L"auto"; 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; int tokenCount = 10, tokenRestoreDelay = 60000, maxSentenceSize = 1000;
enum KeyType { CAT, REST }; enum KeyType { CAT, REST };

View File

@ -14,8 +14,7 @@ namespace
auto _ = ([] auto _ = ([]
{ {
QObject::connect(&webSocket, &QWebSocket::stateChanged, QObject::connect(&webSocket, &QWebSocket::stateChanged,
[](QAbstractSocket::SocketState state) { OnStatusChanged(QMetaEnum::fromType<QAbstractSocket::SocketState>().valueToKey(state)); } [](QAbstractSocket::SocketState state) { OnStatusChanged(QMetaEnum::fromType<QAbstractSocket::SocketState>().valueToKey(state)); });
);
QObject::connect(&webSocket, &QWebSocket::textMessageReceived, [](QString message) QObject::connect(&webSocket, &QWebSocket::textMessageReceived, [](QString message)
{ {
auto result = JSON::Parse(S(message)); auto result = JSON::Parse(S(message));
@ -84,7 +83,7 @@ namespace DevTools
if (GetExitCodeProcess(processInfo.hProcess, &exitCode) && exitCode == STILL_ACTIVE) if (GetExitCodeProcess(processInfo.hProcess, &exitCode) && exitCode == STILL_ACTIVE)
{ {
TerminateProcess(processInfo.hProcess, 0); TerminateProcess(processInfo.hProcess, 0);
WaitForSingleObject(processInfo.hProcess, 100); WaitForSingleObject(processInfo.hProcess, 2000);
CloseHandle(processInfo.hProcess); CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread); CloseHandle(processInfo.hThread);
} }
@ -98,11 +97,11 @@ namespace DevTools
return webSocket.state() == QAbstractSocket::ConnectedState; return webSocket.state() == QAbstractSocket::ConnectedState;
} }
JSON::Value<wchar_t> SendRequest(const std::wstring& method, const std::wstring& params) JSON::Value<wchar_t> SendRequest(const char* method, const std::wstring& params)
{ {
concurrency::task_completion_event<JSON::Value<wchar_t>> response; concurrency::task_completion_event<JSON::Value<wchar_t>> response;
int id = idCounter += 1; 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); std::scoped_lock lock(devToolsMutex);
if (webSocket.state() != QAbstractSocket::ConnectedState) return {}; if (webSocket.state() != QAbstractSocket::ConnectedState) return {};

View File

@ -6,5 +6,5 @@ namespace DevTools
void Start(const std::wstring& path, std::function<void(QString)> statusChanged, bool headless); void Start(const std::wstring& path, std::function<void(QString)> statusChanged, bool headless);
void Close(); void Close();
bool Connected(); bool Connected();
JSON::Value<wchar_t> SendRequest(const std::wstring& method, const std::wstring& params = L"{}"); JSON::Value<wchar_t> SendRequest(const char* method, const std::wstring& params = L"{}");
} }

View File

@ -1,17 +1,10 @@
#include "qtcommon.h" #include "qtcommon.h"
#include "devtools.h" #include "devtools.h"
#include <QFileDialog>
#include <QMouseEvent>
#include <ShlObj.h> #include <ShlObj.h>
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 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* CHROME_LOCATION;
extern const char* START_DEVTOOLS; extern const char* START_DEVTOOLS;
extern const char* STOP_DEVTOOLS; extern const char* STOP_DEVTOOLS;
@ -20,9 +13,18 @@ extern const char* DEVTOOLS_STATUS;
extern const char* AUTO_START; extern const char* AUTO_START;
extern const wchar_t* ERROR_START_CHROME; extern const wchar_t* ERROR_START_CHROME;
extern Synchronized<std::wstring> 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 QStringList languages
{ {
"Chinese (simplified): zh", "Chinese: zh",
"Dutch: nl", "Dutch: nl",
"English: en", "English: en",
"French: fr", "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); if (std::filesystem::exists(programFiles)) chromePath = S(programFiles);
} }
auto chromePathEdit = new QLineEdit(chromePath); auto chromePathEdit = new QLineEdit(chromePath);
static struct : QObject
{
bool eventFilter(QObject* object, QEvent* event)
{
if (auto mouseEvent = dynamic_cast<QMouseEvent*>(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); }); QObject::connect(chromePathEdit, &QLineEdit::textChanged, [chromePathEdit](QString path) { settings.setValue(CHROME_LOCATION, path); });
display->addRow(CHROME_LOCATION, chromePathEdit); display->addRow(CHROME_LOCATION, chromePathEdit);
auto statusLabel = new QLabel("Stopped"); 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(); auto headlessCheck = new QCheckBox();
headlessCheck->setChecked(settings.value(HEADLESS_MODE, true).toBool()); headlessCheck->setChecked(settings.value(HEADLESS_MODE, true).toBool());
QObject::connect(headlessCheck, &QCheckBox::clicked, [](bool headless) { settings.setValue(HEADLESS_MODE, headless); }); 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( DevTools::Start(
S(chromePathEdit->text()), S(chromePathEdit->text()),
[statusLabel](QString status) [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 (auto userAgent = Copy(JSON::Parse(httpRequest.response)[L"User-Agent"].String()))
if (userAgent->find(L"Headless") != std::string::npos) if (userAgent->find(L"Headless") != std::string::npos)
DevTools::SendRequest(L"Network.setUserAgentOverride", DevTools::SendRequest(
FormatString(LR"({"userAgent":"%s"})", userAgent->replace(userAgent->find(L"Headless"), 8, L""))); "Network.setUserAgentOverride",
FormatString(LR"({"userAgent":"%s"})", userAgent->replace(userAgent->find(L"Headless"), 8, L""))
);
}).detach(); }).detach();
}, },
headlessCheck->isChecked() headlessCheck->isChecked()
@ -91,14 +108,14 @@ BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved
buttons->addWidget(startButton); buttons->addWidget(startButton);
buttons->addWidget(stopButton); buttons->addWidget(stopButton);
display->addRow(HEADLESS_MODE, headlessCheck); display->addRow(HEADLESS_MODE, headlessCheck);
auto autoStartButton = new QCheckBox(); auto autoStartCheck = new QCheckBox();
autoStartButton->setChecked(settings.value(AUTO_START, false).toBool()); autoStartCheck->setChecked(settings.value(AUTO_START, false).toBool());
QObject::connect(autoStartButton, &QCheckBox::clicked, [](bool autoStart) { settings.setValue(AUTO_START, autoStart); }); QObject::connect(autoStartCheck, &QCheckBox::clicked, [](bool autoStart) { settings.setValue(AUTO_START, autoStart); });
display->addRow(AUTO_START, autoStartButton); display->addRow(AUTO_START, autoStartCheck);
display->addRow(buttons); display->addRow(buttons);
statusLabel->setFrameStyle(QFrame::Panel | QFrame::Sunken); statusLabel->setFrameStyle(QFrame::Panel | QFrame::Sunken);
display->addRow(DEVTOOLS_STATUS, statusLabel); 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; break;
case DLL_PROCESS_DETACH: case DLL_PROCESS_DETACH:
@ -116,16 +133,20 @@ std::pair<bool, std::wstring> Translate(const std::wstring& text)
// DevTools can't handle concurrent translations yet // DevTools can't handle concurrent translations yet
static std::mutex translationMutex; static std::mutex translationMutex;
std::scoped_lock lock(translationMutex); std::scoped_lock lock(translationMutex);
DevTools::SendRequest(L"Page.navigate", FormatString(LR"({"url":"https://www.deepl.com/translator#%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)) for (int retry = 0; ++retry < 100; Sleep(100))
if (auto translation = Copy( if (auto translation = Copy(DevTools::SendRequest("Runtime.evaluate",
DevTools::SendRequest(L"Runtime.evaluate", LR"({"expression":"document.querySelector('#target-dummydiv').innerHTML","returnByValue":true})")[L"result"][L"value"].String() LR"({"expression":"document.querySelector('#target-dummydiv').innerHTML.trim() ","returnByValue":true})"
)) if (!translation->empty()) return { true, translation.value() }; )[L"result"][L"value"].String())) if (!translation->empty()) return { true, translation.value() };
if (auto errorMessage = Copy( if (auto errorMessage = Copy(DevTools::SendRequest("Runtime.evaluate",
DevTools::SendRequest(
L"Runtime.evaluate",
LR"({"expression":"document.querySelector('div.lmt__system_notification').innerHTML","returnByValue":true})" LR"({"expression":"document.querySelector('div.lmt__system_notification').innerHTML","returnByValue":true})"
)[L"result"][L"value"].String() )[L"result"][L"value"].String())) return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, errorMessage.value()) };
)) return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, errorMessage.value()) };
return { false, TRANSLATION_ERROR }; return { false, TRANSLATION_ERROR };
} }

View File

@ -122,7 +122,7 @@ QStringList languages
}; };
std::wstring autoDetectLanguage = L"auto"; 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; 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)
@ -145,34 +145,12 @@ std::pair<bool, std::wstring> Translate(const std::wstring& text)
if (HttpRequest httpRequest{ if (HttpRequest httpRequest{
L"Mozilla/5.0 Textractor", L"Mozilla/5.0 Textractor",
L"translate.google.com", L"translate.google.com",
L"POST", L"GET",
L"/_/TranslateWebserverUi/data/batchexecute?rpcids=MkEWBc", FormatString(L"/m?sl=%s&tl=%s&q=%s", translateFrom.Copy(), translateTo.Copy(), Escape(text)).c_str()
"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"
}) })
{ {
if (auto start = httpRequest.response.find(L"[["); start != std::string::npos) 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)) };
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()) };
}
}
return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, httpRequest.response) }; return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, httpRequest.response) };
} }
else return { false, FormatString(L"%s (code=%u)", TRANSLATION_ERROR, httpRequest.errorCode) }; else return { false, FormatString(L"%s (code=%u)", TRANSLATION_ERROR, httpRequest.errorCode) };

View File

@ -32,6 +32,36 @@ struct HttpRequest
std::wstring Escape(const std::wstring& text); std::wstring Escape(const std::wstring& text);
std::string Escape(const std::string& text); std::string Escape(const std::string& text);
namespace HTML
{
template <typename C>
std::basic_string<C> Unescape(std::basic_string<C> 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<const C*, size_t, C>{
{ 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<C>::compare(text.data() + i, original, length) == 0) text.replace(i, length, 1, replacement);
return text;
}
}
namespace JSON namespace JSON
{ {
template <typename C> template <typename C>
@ -136,7 +166,7 @@ namespace JSON
if (SkipWhitespace()) return {}; 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 (ch == nullStr[0])
if (std::char_traits<C>::compare(text.data() + i, nullStr, std::size(nullStr)) == 0) return i += std::size(nullStr), nullptr; if (std::char_traits<C>::compare(text.data() + i, nullStr, std::size(nullStr)) == 0) return i += std::size(nullStr), nullptr;
else return {}; else return {};

View File

@ -6,7 +6,7 @@ bool ProcessSentence(std::wstring& sentence, SentenceInfo sentenceInfo)
std::vector<int> repeatNumbers(sentence.size() + 1, 0); std::vector<int> repeatNumbers(sentence.size() + 1, 0);
int repeatNumber = 1; int repeatNumber = 1;
wchar_t prevChar = L'\0'; wchar_t prevChar = 0;
for (auto nextChar : sentence) for (auto nextChar : sentence)
{ {
if (nextChar == prevChar) if (nextChar == prevChar)

View File

@ -13,6 +13,7 @@ extern const char* TRANSLATE_SELECTED_THREAD_ONLY;
extern const char* RATE_LIMIT_ALL_THREADS; extern const char* RATE_LIMIT_ALL_THREADS;
extern const char* RATE_LIMIT_SELECTED_THREAD; extern const char* RATE_LIMIT_SELECTED_THREAD;
extern const char* USE_TRANS_CACHE; extern const char* USE_TRANS_CACHE;
extern const char* FILTER_GARBAGE;
extern const char* RATE_LIMIT_TOKEN_COUNT; extern const char* RATE_LIMIT_TOKEN_COUNT;
extern const char* RATE_LIMIT_TOKEN_RESTORE_DELAY; extern const char* RATE_LIMIT_TOKEN_RESTORE_DELAY;
extern const char* MAX_SENTENCE_SIZE; extern const char* MAX_SENTENCE_SIZE;
@ -23,7 +24,7 @@ extern const char* TRANSLATION_PROVIDER;
extern const char* GET_API_KEY_FROM; extern const char* GET_API_KEY_FROM;
extern QStringList languages; extern QStringList languages;
extern std::wstring autoDetectLanguage; extern std::wstring autoDetectLanguage;
extern bool translateSelectedOnly, rateLimitAll, rateLimitSelected, useCache; 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);
@ -84,6 +85,7 @@ public:
{ rateLimitAll, RATE_LIMIT_ALL_THREADS }, { rateLimitAll, RATE_LIMIT_ALL_THREADS },
{ rateLimitSelected, RATE_LIMIT_SELECTED_THREAD }, { rateLimitSelected, RATE_LIMIT_SELECTED_THREAD },
{ useCache, USE_TRANS_CACHE }, { useCache, USE_TRANS_CACHE },
{ useFilter, FILTER_GARBAGE }
}) })
{ {
value = settings.value(label, value).toBool(); value = settings.value(label, value).toBool();
@ -165,7 +167,7 @@ bool ProcessSentence(std::wstring& sentence, SentenceInfo sentenceInfo)
Synchronized<std::vector<DWORD>> tokens; Synchronized<std::vector<DWORD>> tokens;
} rateLimiter; } 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(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()); 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; bool cache = false;
std::wstring translation; 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) if (useCache)
{ {
auto translationCache = ::translationCache.Acquire(); auto translationCache = ::translationCache.Acquire();
@ -182,7 +188,7 @@ bool ProcessSentence(std::wstring& sentence, SentenceInfo sentenceInfo)
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);
else translation = TOO_MANY_TRANS_REQUESTS; else translation = TOO_MANY_TRANS_REQUESTS;
StripWhitespace(translation); if (useFilter) Trim(translation);
if (cache) translationCache->try_emplace(sentence, translation); if (cache) translationCache->try_emplace(sentence, translation);
if (cache && translationCache->size() > savedSize + 50) SaveCache(); if (cache && translationCache->size() > savedSize + 50) SaveCache();

View File

@ -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 -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)"; * means dereference pointer+deref_offset)";
const char* SAVE_SETTINGS = u8"Save settings"; 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 Drag and drop extensions within the list to reorder them
(Extensions are used from top to bottom: order DOES matter))"; (Extensions are used from top to bottom: order DOES matter))";
const char* ADD_EXTENSION = u8"Add extension"; 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 char* CONFIG_JP_LOCALE = u8"Launch with JP locale";
const wchar_t* CONSOLE = L"Console"; const wchar_t* CONSOLE = L"Console";
const wchar_t* CLIPBOARD = L"Clipboard"; 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 Project homepage: https://github.com/Artikash/Textractor
Tutorial video: https://tinyurl.com/textractor-tutorial Tutorial video: https://tinyurl.com/textractor-tutorial
FAQ: https://github.com/Artikash/Textractor/wiki/FAQ 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 You can do so via the project homepage (issues section) or via email
Source code available under GPLv3 at project homepage 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"}]... 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)"; 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"; 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* COULD_NOT_FIND = u8"Textractor: could not find text";
const char* TRANSLATE_TO = u8"Translate to"; const char* TRANSLATE_TO = u8"Translate to";
const char* TRANSLATE_FROM = u8"Translate from"; const char* TRANSLATE_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* TRANSLATE_SELECTED_THREAD_ONLY = u8"Translate selected text thread only";
const char* RATE_LIMIT_ALL_THREADS = u8"Rate limit all text threads"; const char* RATE_LIMIT_ALL_THREADS = u8"Rate limit all text threads";
const char* RATE_LIMIT_SELECTED_THREAD = u8"Rate limit selected text thread"; const char* RATE_LIMIT_SELECTED_THREAD = u8"Rate limit selected text thread";
@ -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 wchar_t* TRANSLATION_ERROR = L"Error while translating";
const char* USE_PREV_SENTENCE_CONTEXT = u8"Use previous sentence as context"; const char* USE_PREV_SENTENCE_CONTEXT = u8"Use previous sentence as context";
const char* API_KEY = u8"API key"; 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* START_DEVTOOLS = u8"Start DevTools";
const char* STOP_DEVTOOLS = u8"Stop DevTools"; const char* STOP_DEVTOOLS = u8"Stop DevTools";
const char* HEADLESS_MODE = u8"Headless mode"; const char* HEADLESS_MODE = u8"Headless mode";