forked from Public-Mirror/Textractor
reorganize translation
This commit is contained in:
parent
e7fff79f8f
commit
76804dd0aa
@ -4,11 +4,11 @@ find_qt5(Core Widgets)
|
||||
|
||||
cmake_policy(SET CMP0037 OLD)
|
||||
|
||||
add_library(Bing\ Translate MODULE bingtranslate.cpp extensionimpl.cpp)
|
||||
add_library(Bing\ Translate MODULE bingtranslate.cpp translatewrapper.cpp network.cpp extensionimpl.cpp)
|
||||
add_library(Copy\ to\ Clipboard MODULE copyclipboard.cpp extensionimpl.cpp)
|
||||
add_library(Extra\ Newlines MODULE extranewlines.cpp extensionimpl.cpp)
|
||||
add_library(Extra\ Window MODULE extrawindow.cpp extensionimpl.cpp)
|
||||
add_library(Google\ Translate MODULE googletranslate.cpp extensionimpl.cpp)
|
||||
add_library(Google\ Translate MODULE googletranslate.cpp translatewrapper.cpp network.cpp extensionimpl.cpp)
|
||||
add_library(Lua MODULE lua.cpp extensionimpl.cpp)
|
||||
add_library(Regex\ Filter MODULE regexfilter.cpp extensionimpl.cpp)
|
||||
add_library(Remove\ Repetition MODULE removerepeat.cpp extensionimpl.cpp)
|
||||
|
@ -1,13 +1,10 @@
|
||||
#include "extension.h"
|
||||
#include "network.h"
|
||||
#include <QInputDialog>
|
||||
#include <QTimer>
|
||||
#include <QStringList>
|
||||
|
||||
extern const char* SELECT_LANGUAGE;
|
||||
extern const wchar_t* TOO_MANY_TRANS_REQUESTS;
|
||||
extern const wchar_t* TRANSLATION_ERROR;
|
||||
extern const char* BING_PROMPT;
|
||||
|
||||
const char* TRANSLATION_PROVIDER = "Bing";
|
||||
QStringList languages
|
||||
{
|
||||
"English: en",
|
||||
@ -56,85 +53,29 @@ QStringList languages
|
||||
"Vietnamese: vi",
|
||||
"Welsh: cy"
|
||||
};
|
||||
Synchronized<std::wstring> translateTo = L"en";
|
||||
|
||||
std::wstring translateTo = L"en";
|
||||
|
||||
BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
|
||||
std::pair<bool, std::wstring> Translate(const std::wstring& text)
|
||||
{
|
||||
switch (ul_reason_for_call)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
{
|
||||
QTimer::singleShot(0, []
|
||||
{
|
||||
translateTo = QInputDialog::getItem(
|
||||
nullptr,
|
||||
SELECT_LANGUAGE,
|
||||
BING_PROMPT,
|
||||
languages,
|
||||
0, false, nullptr,
|
||||
Qt::WindowCloseButtonHint
|
||||
).split(" ")[1].toStdWString();
|
||||
});
|
||||
}
|
||||
break;
|
||||
case DLL_PROCESS_DETACH:
|
||||
{
|
||||
}
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
std::wstring translateFrom;
|
||||
if (HttpRequest httpRequest{
|
||||
L"Mozilla/5.0 Textractor",
|
||||
L"www.bing.com",
|
||||
L"POST",
|
||||
FormatString(L"/tdetect?text=%s", Escape(text)).c_str(),
|
||||
WINHTTP_FLAG_ESCAPE_DISABLE | WINHTTP_FLAG_SECURE,
|
||||
}) translateFrom = httpRequest.response;
|
||||
else return { false, FormatString(L"%s (code=%u)", TRANSLATION_ERROR, httpRequest.errorCode) };
|
||||
|
||||
if (HttpRequest httpRequest{
|
||||
L"Mozilla/5.0 Textractor",
|
||||
L"www.bing.com",
|
||||
L"POST",
|
||||
FormatString(L"/ttranslate?from=%s&to=%s&text=%s", translateFrom, translateTo->c_str(), Escape(text)).c_str(),
|
||||
WINHTTP_FLAG_ESCAPE_DISABLE | WINHTTP_FLAG_SECURE,
|
||||
})
|
||||
// Response formatted as JSON: translation starts with :" and ends with "}
|
||||
if (std::wsmatch results; std::regex_search(httpRequest.response, results, std::wregex(L":\"(.+)\"\\}"))) return { true, results[1] };
|
||||
else return { false, TRANSLATION_ERROR };
|
||||
else return { false, FormatString(L"%s (code=%u)", TRANSLATION_ERROR, httpRequest.errorCode) };
|
||||
}
|
||||
|
||||
// This function detects language and returns it if translateFrom is empty
|
||||
std::wstring Translate(const std::wstring& text, std::wstring translateFrom, std::wstring translateTo)
|
||||
{
|
||||
static std::atomic<HINTERNET> internet = NULL;
|
||||
if (!internet) internet = WinHttpOpen(L"Mozilla/5.0 Textractor", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, NULL, NULL, 0);
|
||||
|
||||
std::wstring escapedText;
|
||||
for (unsigned char ch : WideStringToString(text))
|
||||
{
|
||||
wchar_t escapedChar[4] = {};
|
||||
swprintf_s<4>(escapedChar, L"%%%02X", (int)ch);
|
||||
escapedText += escapedChar;
|
||||
}
|
||||
|
||||
std::wstring location = translateFrom.empty()
|
||||
? L"/tdetect?text=" + escapedText
|
||||
: L"/ttranslate?from=" + translateFrom + L"&to=" + translateTo + L"&text=" + escapedText;
|
||||
std::wstring translation;
|
||||
if (internet)
|
||||
if (InternetHandle connection = WinHttpConnect(internet, L"www.bing.com", INTERNET_DEFAULT_HTTPS_PORT, 0))
|
||||
if (InternetHandle request = WinHttpOpenRequest(connection, L"POST", location.c_str(), NULL, NULL, NULL, WINHTTP_FLAG_ESCAPE_DISABLE | WINHTTP_FLAG_SECURE))
|
||||
if (WinHttpSendRequest(request, NULL, 0, NULL, 0, 0, NULL))
|
||||
if (auto response = ReceiveHttpRequest(request))
|
||||
if (translateFrom.empty()) translation = response.value();
|
||||
// Response formatted as JSON: translation starts with :" and ends with "}
|
||||
else if (std::wsmatch results; std::regex_search(response.value(), results, std::wregex(L":\"(.+)\"\\}"))) translation = results[1];
|
||||
|
||||
Unescape(translation);
|
||||
return translation;
|
||||
}
|
||||
|
||||
bool ProcessSentence(std::wstring& sentence, SentenceInfo sentenceInfo)
|
||||
{
|
||||
if (sentenceInfo["text number"] == 0) return false;
|
||||
|
||||
static RateLimiter rateLimiter(30, 60 * 1000);
|
||||
|
||||
std::wstring translation;
|
||||
if (!(rateLimiter.Request() || sentenceInfo["current select"])) translation = TOO_MANY_TRANS_REQUESTS;
|
||||
else translation = Translate(sentence, Translate(sentence, L"", translateTo), translateTo);
|
||||
if (translation.empty()) translation = TRANSLATION_ERROR;
|
||||
sentence += L"\n" + translation;
|
||||
return true;
|
||||
}
|
||||
|
||||
TEST(
|
||||
{
|
||||
std::wstring test = L"こんにちは";
|
||||
ProcessSentence(test, { SentenceInfo::DUMMY });
|
||||
assert(test.find(L"Hello") != std::wstring::npos);
|
||||
}
|
||||
);
|
||||
|
@ -1,15 +1,12 @@
|
||||
#include "extension.h"
|
||||
#include "util.h"
|
||||
#include "network.h"
|
||||
#include "util.h"
|
||||
#include <ctime>
|
||||
#include <QInputDialog>
|
||||
#include <QTimer>
|
||||
#include <QStringList>
|
||||
|
||||
extern const char* SELECT_LANGUAGE;
|
||||
extern const wchar_t* TOO_MANY_TRANS_REQUESTS;
|
||||
extern const wchar_t* TRANSLATION_ERROR;
|
||||
extern const char* GOOGLE_PROMPT;
|
||||
|
||||
const char* TRANSLATION_PROVIDER = "Google";
|
||||
QStringList languages
|
||||
{
|
||||
"English: en",
|
||||
@ -73,49 +70,19 @@ QStringList languages
|
||||
"Yiddish: yi",
|
||||
"Zulu: zu"
|
||||
};
|
||||
|
||||
std::wstring translateTo = L"en";
|
||||
|
||||
BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
|
||||
{
|
||||
switch (ul_reason_for_call)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
{
|
||||
QTimer::singleShot(0, []
|
||||
{
|
||||
translateTo = QInputDialog::getItem(
|
||||
nullptr,
|
||||
SELECT_LANGUAGE,
|
||||
GOOGLE_PROMPT,
|
||||
languages,
|
||||
0, false, nullptr,
|
||||
Qt::WindowCloseButtonHint
|
||||
).split(" ")[1].toStdWString();
|
||||
});
|
||||
}
|
||||
break;
|
||||
case DLL_PROCESS_DETACH:
|
||||
{
|
||||
}
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
Synchronized<std::wstring> translateTo = L"en";
|
||||
|
||||
std::wstring GetTranslationUri(const std::wstring& text, unsigned TKK)
|
||||
{
|
||||
// If no TKK available, use this uri. Can't use too much or google will detect unauthorized access
|
||||
if (!TKK) return L"/translate_a/single?client=gtx&dt=ld&dt=rm&dt=t&tl=" + translateTo + L"&q=" + text;
|
||||
if (!TKK) return FormatString(L"/translate_a/single?client=gtx&dt=ld&dt=rm&dt=t&tl=%s&q=%s", translateTo->c_str(), text);
|
||||
|
||||
// Artikash 8/19/2018: reverse engineered from translate.google.com
|
||||
std::wstring escapedText;
|
||||
unsigned a = _time64(NULL) / 3600, b = a; // <- the first part of TKK
|
||||
for (unsigned char ch : WideStringToString(text))
|
||||
{
|
||||
wchar_t escapedChar[4] = {};
|
||||
swprintf_s<4>(escapedChar, L"%%%02X", (int)ch);
|
||||
escapedText += escapedChar;
|
||||
escapedText += FormatString(L"%%%02X", (int)ch);
|
||||
a += ch;
|
||||
a += a << 10;
|
||||
a ^= a >> 6;
|
||||
@ -127,56 +94,28 @@ std::wstring GetTranslationUri(const std::wstring& text, unsigned TKK)
|
||||
a %= 1000000;
|
||||
b ^= a;
|
||||
|
||||
return L"/translate_a/single?client=t&dt=ld&dt=rm&dt=t&tl=" + translateTo + L"&tk=" + std::to_wstring(a) + L"." + std::to_wstring(b) + L"&q=" + escapedText;
|
||||
return FormatString(L"/translate_a/single?client=t&dt=ld&dt=rm&dt=t&tl=%s&tk=%u.%u&q=%s", translateTo->c_str(), a, b, escapedText);
|
||||
}
|
||||
|
||||
bool ProcessSentence(std::wstring& sentence, SentenceInfo sentenceInfo)
|
||||
std::pair<bool, std::wstring> Translate(const std::wstring& text)
|
||||
{
|
||||
if (sentenceInfo["text number"] == 0) return false;
|
||||
static unsigned TKK = 0;
|
||||
if (!TKK)
|
||||
if (HttpRequest httpRequest{ L"Mozilla/5.0 Textractor", L"translate.google.com", L"GET", L"/" })
|
||||
if (std::wsmatch results; std::regex_search(httpRequest.response, results, std::wregex(L"(\\d{7,})'")))
|
||||
_InterlockedCompareExchange(&TKK, stoll(results[1]), 0);
|
||||
|
||||
static std::atomic<HINTERNET> internet = NULL;
|
||||
if (!internet) internet = WinHttpOpen(L"Mozilla/5.0 Textractor", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, NULL, NULL, 0);
|
||||
static std::atomic<unsigned> TKK = 0;
|
||||
static RateLimiter rateLimiter(30, 60 * 1000);
|
||||
|
||||
std::wstring translation;
|
||||
if (!(rateLimiter.Request() || sentenceInfo["current select"])) translation = TOO_MANY_TRANS_REQUESTS;
|
||||
else if (internet)
|
||||
if (HttpRequest httpRequest{ L"Mozilla/5.0 Textractor", L"translate.google.com", L"GET", GetTranslationUri(text, TKK).c_str() })
|
||||
{
|
||||
if (!TKK)
|
||||
if (InternetHandle connection = WinHttpConnect(internet, L"translate.google.com", INTERNET_DEFAULT_HTTPS_PORT, 0))
|
||||
if (InternetHandle request = WinHttpOpenRequest(connection, L"GET", L"/", NULL, NULL, NULL, WINHTTP_FLAG_SECURE))
|
||||
if (WinHttpSendRequest(request, NULL, 0, NULL, 0, 0, NULL))
|
||||
if (auto response = ReceiveHttpRequest(request))
|
||||
if (std::wsmatch results; std::regex_search(response.value(), results, std::wregex(L"(\\d{7,})'"))) TKK = stoll(results[1]);
|
||||
|
||||
if (InternetHandle connection = WinHttpConnect(internet, L"translate.google.com", INTERNET_DEFAULT_HTTPS_PORT, 0))
|
||||
if (InternetHandle request = WinHttpOpenRequest(connection, L"GET", GetTranslationUri(sentence, TKK).c_str(), NULL, NULL, NULL, WINHTTP_FLAG_ESCAPE_DISABLE | WINHTTP_FLAG_SECURE))
|
||||
if (WinHttpSendRequest(request, NULL, 0, NULL, 0, 0, NULL))
|
||||
if (auto response = ReceiveHttpRequest(request))
|
||||
// Response formatted as JSON: starts with [[["
|
||||
if (response.value()[0] == L'[')
|
||||
{
|
||||
for (std::wsmatch results; std::regex_search(response.value(), results, std::wregex(L"\\[\"(.*?)\",[n\"]")); response = results.suffix())
|
||||
translation += std::wstring(results[1]) + L" ";
|
||||
Unescape(translation);
|
||||
}
|
||||
else
|
||||
{
|
||||
translation = TRANSLATION_ERROR + (L" (TKK=" + std::to_wstring(TKK) + L")");
|
||||
TKK = 0;
|
||||
}
|
||||
// Response formatted as JSON: starts with "[[[" and translation is enclosed in quotes followed by a comma
|
||||
if (httpRequest.response[0] == L'[')
|
||||
{
|
||||
std::wstring translation;
|
||||
for (std::wsmatch results; std::regex_search(httpRequest.response, results, std::wregex(L"\\[\"(.*?)\",[n\"]")); httpRequest.response = results.suffix())
|
||||
translation += std::wstring(results[1]) + L" ";
|
||||
if (!translation.empty()) return { true, translation };
|
||||
}
|
||||
return { false, FormatString(L"%s (TKK=%u)", TRANSLATION_ERROR, _InterlockedExchange(&TKK, 0)) };
|
||||
}
|
||||
|
||||
if (translation.empty()) translation = TRANSLATION_ERROR;
|
||||
sentence += L"\n" + translation;
|
||||
return true;
|
||||
else return { false, FormatString(L"%s (code=%u)", TRANSLATION_ERROR, httpRequest.errorCode) };
|
||||
}
|
||||
|
||||
TEST(
|
||||
{
|
||||
std::wstring test = L"こんにちは";
|
||||
ProcessSentence(test, { SentenceInfo::DUMMY });
|
||||
assert(test.find(L"Hello") != std::wstring::npos);
|
||||
}
|
||||
);
|
||||
|
@ -113,7 +113,7 @@ bool ProcessSentence(std::wstring& sentence, SentenceInfo sentenceInfo)
|
||||
luaL_dostring(L, "ProcessSentence = nil");
|
||||
if (luaL_dostring(L, script->c_str()) != LUA_OK)
|
||||
{
|
||||
sentence += L"\n" + FormatWideString(LUA_ERROR, StringToWideString(lua_tolstring(L, 1, nullptr)).c_str());
|
||||
sentence += L"\n" + FormatString(LUA_ERROR, StringToWideString(lua_tolstring(L, 1, nullptr)));
|
||||
lua_settop(L, 0);
|
||||
return logErrors;
|
||||
}
|
||||
@ -121,7 +121,7 @@ bool ProcessSentence(std::wstring& sentence, SentenceInfo sentenceInfo)
|
||||
|
||||
if (lua_getglobal(L, "ProcessSentence") != LUA_TFUNCTION)
|
||||
{
|
||||
sentence += L"\n" + FormatWideString(LUA_ERROR, L"ProcessSentence is not a function");
|
||||
sentence += L"\n" + FormatString(LUA_ERROR, L"ProcessSentence is not a function");
|
||||
lua_settop(L, 0);
|
||||
return logErrors;
|
||||
}
|
||||
@ -135,7 +135,7 @@ bool ProcessSentence(std::wstring& sentence, SentenceInfo sentenceInfo)
|
||||
}
|
||||
if (lua_pcallk(L, 2, 1, 0, NULL, NULL) != LUA_OK)
|
||||
{
|
||||
sentence += L"\n" + FormatWideString(LUA_ERROR, StringToWideString(lua_tolstring(L, 1, nullptr)).c_str());
|
||||
sentence += L"\n" + FormatString(LUA_ERROR, StringToWideString(lua_tolstring(L, 1, nullptr)));
|
||||
lua_settop(L, 0);
|
||||
return logErrors;
|
||||
}
|
||||
|
66
extensions/network.cpp
Normal file
66
extensions/network.cpp
Normal file
@ -0,0 +1,66 @@
|
||||
#include "network.h"
|
||||
#include "util.h"
|
||||
|
||||
HttpRequest::HttpRequest(
|
||||
const wchar_t* agentName,
|
||||
const wchar_t* serverName,
|
||||
const wchar_t* action,
|
||||
const wchar_t* objectName,
|
||||
DWORD requestFlags,
|
||||
const wchar_t* httpVersion,
|
||||
const wchar_t* referrer,
|
||||
const wchar_t** acceptTypes,
|
||||
const wchar_t* headers,
|
||||
void* body,
|
||||
DWORD bodyLength
|
||||
)
|
||||
{
|
||||
static std::atomic<HINTERNET> internet = NULL;
|
||||
if (!internet) internet = WinHttpOpen(agentName, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, NULL, NULL, 0);
|
||||
if (internet)
|
||||
if (InternetHandle connection = WinHttpConnect(internet, serverName, INTERNET_DEFAULT_HTTPS_PORT, 0))
|
||||
if (InternetHandle request = WinHttpOpenRequest(connection, action, objectName, httpVersion, referrer, acceptTypes, requestFlags))
|
||||
if (WinHttpSendRequest(request, headers, -1UL, body, bodyLength, bodyLength, NULL))
|
||||
{
|
||||
WinHttpReceiveResponse(request, NULL);
|
||||
std::string data;
|
||||
DWORD availableSize, downloadedSize;
|
||||
do
|
||||
{
|
||||
availableSize = 0;
|
||||
WinHttpQueryDataAvailable(request, &availableSize);
|
||||
if (!availableSize) break;
|
||||
std::vector<char> buffer(availableSize);
|
||||
WinHttpReadData(request, buffer.data(), availableSize, &downloadedSize);
|
||||
data.append(buffer.data(), downloadedSize);
|
||||
} while (availableSize > 0);
|
||||
response = StringToWideString(data);
|
||||
this->connection = std::move(connection);
|
||||
this->request = std::move(request);
|
||||
}
|
||||
else errorCode = GetLastError();
|
||||
else errorCode = GetLastError();
|
||||
else errorCode = GetLastError();
|
||||
else errorCode = GetLastError();
|
||||
}
|
||||
|
||||
std::wstring Escape(const std::wstring& text)
|
||||
{
|
||||
std::wstring escaped;
|
||||
for (unsigned char ch : WideStringToString(text)) escaped += FormatString(L"%%%02X", (int)ch);
|
||||
return escaped;
|
||||
}
|
||||
|
||||
void Unescape(std::wstring& text)
|
||||
{
|
||||
for (int i = 0; i < text.size(); ++i)
|
||||
{
|
||||
if (text[i] == L'\\')
|
||||
{
|
||||
text[i] = 0x200b;
|
||||
if (text[i + 1] == L'r') text[i + 1] = 0x200b; // for some reason \r gets displayed as a newline
|
||||
if (text[i + 1] == L'n') text[i + 1] = L'\n';
|
||||
if (text[i + 1] == L't') text[i + 1] = L'\t';
|
||||
}
|
||||
}
|
||||
}
|
@ -1,60 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include "util.h"
|
||||
#include "common.h"
|
||||
#include <winhttp.h>
|
||||
|
||||
using InternetHandle = AutoHandle<Functor<WinHttpCloseHandle>>;
|
||||
|
||||
inline std::optional<std::wstring> ReceiveHttpRequest(HINTERNET request)
|
||||
struct HttpRequest
|
||||
{
|
||||
WinHttpReceiveResponse(request, NULL);
|
||||
std::string data;
|
||||
DWORD dwSize, dwDownloaded;
|
||||
do
|
||||
{
|
||||
dwSize = 0;
|
||||
WinHttpQueryDataAvailable(request, &dwSize);
|
||||
if (!dwSize) break;
|
||||
std::vector<char> buffer(dwSize);
|
||||
WinHttpReadData(request, buffer.data(), dwSize, &dwDownloaded);
|
||||
data.append(buffer.data(), dwDownloaded);
|
||||
} while (dwSize > 0);
|
||||
HttpRequest(
|
||||
const wchar_t* agentName,
|
||||
const wchar_t* serverName,
|
||||
const wchar_t* action,
|
||||
const wchar_t* objectName,
|
||||
DWORD requestFlags = WINHTTP_FLAG_SECURE | WINHTTP_FLAG_ESCAPE_DISABLE,
|
||||
const wchar_t* httpVersion = NULL,
|
||||
const wchar_t* referrer = NULL,
|
||||
const wchar_t** acceptTypes = NULL,
|
||||
const wchar_t* headers = NULL,
|
||||
void* body = NULL,
|
||||
DWORD bodyLength = 0
|
||||
);
|
||||
operator bool() { return errorCode == ERROR_SUCCESS; }
|
||||
|
||||
if (data.empty()) return {};
|
||||
return StringToWideString(data);
|
||||
}
|
||||
|
||||
inline void Unescape(std::wstring& text)
|
||||
{
|
||||
for (int i = 0; i < text.size(); ++i)
|
||||
{
|
||||
if (text[i] == L'\\')
|
||||
{
|
||||
text[i] = 0x200b;
|
||||
if (text[i + 1] == L'r') text[i + 1] = 0x200b; // for some reason \r gets displayed as a newline
|
||||
if (text[i + 1] == L'n') text[i + 1] = L'\n';
|
||||
if (text[i + 1] == L't') text[i + 1] = L'\t';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class RateLimiter
|
||||
{
|
||||
public:
|
||||
RateLimiter(int tokenCount, int delay) : tokenCount(tokenCount), delay(delay) {}
|
||||
|
||||
bool Request()
|
||||
{
|
||||
auto tokens = this->tokens.Acquire();
|
||||
tokens->push_back(GetTickCount());
|
||||
if (tokens->size() > tokenCount * 5) tokens->erase(tokens->begin(), tokens->begin() + tokenCount * 3);
|
||||
tokens->erase(std::remove_if(tokens->begin(), tokens->end(), [this](DWORD token) { return GetTickCount() - token > delay; }), tokens->end());
|
||||
return tokens->size() < tokenCount;
|
||||
}
|
||||
|
||||
const int tokenCount, delay;
|
||||
|
||||
private:
|
||||
Synchronized<std::vector<DWORD>> tokens;
|
||||
std::wstring response;
|
||||
InternetHandle connection = NULL;
|
||||
InternetHandle request = NULL;
|
||||
DWORD errorCode = ERROR_SUCCESS;
|
||||
};
|
||||
|
||||
std::wstring Escape(const std::wstring& text);
|
||||
void Unescape(std::wstring& text);
|
||||
|
@ -11,6 +11,7 @@ extern const char* THREAD_LINKER;
|
||||
extern const char* LINK;
|
||||
extern const char* THREAD_LINK_FROM;
|
||||
extern const char* THREAD_LINK_TO;
|
||||
extern const char* HEXADECIMAL;
|
||||
|
||||
std::mutex m;
|
||||
std::unordered_map<int64_t, std::unordered_multiset<int64_t>> linkedTextHandles;
|
||||
@ -29,8 +30,8 @@ struct : QMainWindow
|
||||
connect(addLink, &QPushButton::clicked, [=]
|
||||
{
|
||||
bool ok1, ok2, ok3, ok4;
|
||||
int from = QInputDialog::getText(this, THREAD_LINK_FROM, "", QLineEdit::Normal, "0x", &ok1, Qt::WindowCloseButtonHint).toInt(&ok2, 16);
|
||||
int to = QInputDialog::getText(this, THREAD_LINK_TO, "", QLineEdit::Normal, "0x", &ok3, Qt::WindowCloseButtonHint).toInt(&ok4, 16);
|
||||
int from = QInputDialog::getText(this, THREAD_LINK_FROM, HEXADECIMAL, QLineEdit::Normal, "", &ok1, Qt::WindowCloseButtonHint).toInt(&ok2, 16);
|
||||
int to = QInputDialog::getText(this, THREAD_LINK_TO, HEXADECIMAL, QLineEdit::Normal, "", &ok3, Qt::WindowCloseButtonHint).toInt(&ok4, 16);
|
||||
if (ok1 && ok2 && ok3 && ok4)
|
||||
{
|
||||
std::lock_guard l(m);
|
||||
|
86
extensions/translatewrapper.cpp
Normal file
86
extensions/translatewrapper.cpp
Normal file
@ -0,0 +1,86 @@
|
||||
#include "extension.h"
|
||||
#include "network.h"
|
||||
#include <QTimer>
|
||||
#include <QInputDialog>
|
||||
|
||||
extern const char* SELECT_LANGUAGE;
|
||||
extern const char* SELECT_LANGUAGE_MESSAGE;
|
||||
extern const wchar_t* TOO_MANY_TRANS_REQUESTS;
|
||||
|
||||
extern const char* TRANSLATION_PROVIDER;
|
||||
extern QStringList languages;
|
||||
extern Synchronized<std::wstring> translateTo;
|
||||
std::pair<bool, std::wstring> Translate(const std::wstring& text);
|
||||
|
||||
BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
|
||||
{
|
||||
switch (ul_reason_for_call)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
{
|
||||
QTimer::singleShot(0, []
|
||||
{
|
||||
translateTo->assign(QInputDialog::getItem(
|
||||
nullptr,
|
||||
SELECT_LANGUAGE,
|
||||
QString(SELECT_LANGUAGE_MESSAGE).arg(TRANSLATION_PROVIDER),
|
||||
languages,
|
||||
0,
|
||||
false,
|
||||
nullptr,
|
||||
Qt::WindowCloseButtonHint)
|
||||
.split(": ")[1]
|
||||
.toStdWString()
|
||||
);
|
||||
});
|
||||
}
|
||||
break;
|
||||
case DLL_PROCESS_DETACH:
|
||||
{
|
||||
}
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool ProcessSentence(std::wstring& sentence, SentenceInfo sentenceInfo)
|
||||
{
|
||||
if (sentenceInfo["text number"] == 0) return false;
|
||||
|
||||
static class
|
||||
{
|
||||
public:
|
||||
bool Request()
|
||||
{
|
||||
auto tokens = this->tokens.Acquire();
|
||||
tokens->push_back(GetTickCount());
|
||||
if (tokens->size() > tokenCount * 5) tokens->erase(tokens->begin(), tokens->begin() + tokenCount * 3);
|
||||
tokens->erase(std::remove_if(tokens->begin(), tokens->end(), [this](DWORD token) { return GetTickCount() - token > delay; }), tokens->end());
|
||||
return tokens->size() < tokenCount;
|
||||
}
|
||||
|
||||
private:
|
||||
const int tokenCount = 30, delay = 60 * 1000;
|
||||
Synchronized<std::vector<DWORD>> tokens;
|
||||
} rateLimiter;
|
||||
Synchronized<std::unordered_map<std::wstring, std::wstring>> translationCache;
|
||||
|
||||
bool cache = false;
|
||||
std::wstring translation;
|
||||
if (translationCache->count(sentence) != 0) translation = translationCache->at(sentence);
|
||||
else if (!(rateLimiter.Request() || sentenceInfo["current select"])) translation = TOO_MANY_TRANS_REQUESTS;
|
||||
else std::tie(cache, translation) = Translate(sentence);
|
||||
if (cache) translationCache->insert({ sentence, translation });
|
||||
Unescape(translation);
|
||||
|
||||
sentence += L"\n" + translation;
|
||||
return true;
|
||||
}
|
||||
|
||||
TEST(
|
||||
{
|
||||
std::wstring test = L"こんにちは";
|
||||
ProcessSentence(test, { SentenceInfo::DUMMY });
|
||||
assert(test.find(L"Hello") != std::wstring::npos);
|
||||
}
|
||||
);
|
@ -81,33 +81,39 @@ static struct
|
||||
operator T*() { static_assert(sizeof(T) < sizeof(DUMMY)); return (T*)DUMMY; }
|
||||
} DUMMY;
|
||||
|
||||
template <typename T>
|
||||
inline auto FormatArg(T arg) { return arg; }
|
||||
|
||||
template <typename C>
|
||||
inline auto FormatArg(const std::basic_string<C>& arg) { return arg.c_str(); }
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4996)
|
||||
template <typename... Args>
|
||||
inline std::string FormatString(const char* format, Args... args)
|
||||
{
|
||||
std::string buffer(snprintf(nullptr, 0, format, args...), '\0');
|
||||
sprintf(buffer.data(), format, args...);
|
||||
std::string buffer(snprintf(nullptr, 0, format, FormatArg(args)...), '\0');
|
||||
sprintf(buffer.data(), format, FormatArg(args)...);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline std::wstring FormatWideString(const wchar_t* format, Args... args)
|
||||
inline std::wstring FormatString(const wchar_t* format, Args... args)
|
||||
{
|
||||
std::wstring buffer(_snwprintf(nullptr, 0, format, args...), L'\0');
|
||||
_swprintf(buffer.data(), format, args...);
|
||||
std::wstring buffer(_snwprintf(nullptr, 0, format, FormatArg(args)...), L'\0');
|
||||
_swprintf(buffer.data(), format, FormatArg(args)...);
|
||||
return buffer;
|
||||
}
|
||||
#pragma warning(pop)
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define TEST(...) inline auto TEST_RUNNER_DUMMY = CreateThread(nullptr, 0, [](auto) { __VA_ARGS__; return 0UL; }, NULL, 0, nullptr);
|
||||
#define TEST(...) static auto _ = CreateThread(nullptr, 0, [](auto) { __VA_ARGS__; return 0UL; }, NULL, 0, nullptr);
|
||||
#else
|
||||
#define TEST(...)
|
||||
#endif
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define TEST_SYNC(...) inline auto TEST_RUNNER_DUMMY = [] { __VA_ARGS__; return 0UL; }();
|
||||
#define TEST_SYNC(...) static auto _ = [] { __VA_ARGS__; return 0UL; }();
|
||||
#else
|
||||
#define TEST_SYNC(...)
|
||||
#endif
|
||||
|
18
text.cpp
18
text.cpp
@ -103,8 +103,7 @@ const char* READ_ERROR = u8"Textractor: Reader ERROR (likely an incorrect R-code
|
||||
const char* HIJACK_ERROR = u8"Textractor: Hijack ERROR";
|
||||
const char* COULD_NOT_FIND = u8"Textractor: could not find text";
|
||||
const char* SELECT_LANGUAGE = u8"Select language";
|
||||
const char* BING_PROMPT = u8"What language should Bing translate to?";
|
||||
const char* GOOGLE_PROMPT = u8"What language should Google translate to?";
|
||||
const char* SELECT_LANGUAGE_MESSAGE = u8"What language should %1 translate to?";
|
||||
const wchar_t* TOO_MANY_TRANS_REQUESTS = L"Too many translation requests: refuse to make more";
|
||||
const wchar_t* TRANSLATION_ERROR = L"Error while translating";
|
||||
const char* EXTRA_WINDOW_INFO = u8R"(Right click to change settings
|
||||
@ -149,8 +148,9 @@ const char* THREAD_LINKER = u8"Thread Linker";
|
||||
const char* LINK = u8"Link";
|
||||
const char* THREAD_LINK_FROM = u8"Thread number to link from";
|
||||
const char* THREAD_LINK_TO = u8"Thread number to link to";
|
||||
const char* HEXADECIMAL = u8"Hexadecimal";
|
||||
|
||||
inline auto _ = []
|
||||
static auto _ = []
|
||||
{
|
||||
#ifdef TURKISH
|
||||
ATTACH = u8"Oyuna bağla";
|
||||
@ -236,8 +236,7 @@ Estoy buscando un nuevo trabajo: por favor envíame un correo si estás contrata
|
||||
HIJACK_ERROR = u8"Textractor: Hijack ERROR";
|
||||
COULD_NOT_FIND = u8"Textractor: no se puede encontrar texto";
|
||||
SELECT_LANGUAGE = u8"Seleccionar lenguaje";
|
||||
BING_PROMPT = u8"¿A qué idioma debe traducir Bing?";
|
||||
GOOGLE_PROMPT = u8"¿A qué idioma debe traducir Google?";
|
||||
SELECT_LANGUAGE_MESSAGE = u8"¿A qué idioma debe traducir %1?";
|
||||
TOO_MANY_TRANS_REQUESTS = L"Demasiadas peticiones de traducción: no se puede hacer más";
|
||||
TRANSLATION_ERROR = L"Error al traducir";
|
||||
EXTRA_WINDOW_INFO = u8R"(Clic derecho para configurar
|
||||
@ -303,8 +302,7 @@ Clic y arrastra los bordes de la ventana para moverla, o en la esquina inferior
|
||||
HIJACK_ERROR = u8"Textractor: Hijack 错误";
|
||||
COULD_NOT_FIND = u8"Textractor: 无法找到文本";
|
||||
SELECT_LANGUAGE = u8"选择语言";
|
||||
BING_PROMPT = u8"想要使用 Bing 翻译到哪种语言?";
|
||||
GOOGLE_PROMPT = u8"想要使用 Google 翻译到哪种语言?";
|
||||
SELECT_LANGUAGE_MESSAGE = u8"想要使用 %1 翻译到哪种语言?";
|
||||
TOO_MANY_TRANS_REQUESTS = L"太多翻译请求: 拒绝生成更多";
|
||||
TRANSLATION_ERROR = L"翻译时出错";
|
||||
EXTRA_WINDOW_INFO = u8R"(右键修改设置
|
||||
@ -373,8 +371,7 @@ I'm currently looking for a new job: email me if you know anyone hiring US softw
|
||||
HIJACK_ERROR = u8"Textractor: Hijack ERROR";
|
||||
COULD_NOT_FIND = u8"Textractor: невозможно найти текст";
|
||||
SELECT_LANGUAGE = u8"Выберете язык";
|
||||
BING_PROMPT = u8"На какой язык переводить в Bing?";
|
||||
GOOGLE_PROMPT = u8"На какой язык переводить в Google?";
|
||||
SELECT_LANGUAGE_MESSAGE = u8"На какой язык переводить в %1?";
|
||||
TOO_MANY_TRANS_REQUESTS = L"Слишком много запросов для перевода: отклонено";
|
||||
TRANSLATION_ERROR = L"Ошибка при переводе";
|
||||
EXTRA_WINDOW_INFO = u8R"(Правый клик для изменения настроек
|
||||
@ -473,8 +470,7 @@ Jika kamu menyukai project ini, tolong sebarluaskan project ini :))";
|
||||
HIJACK_ERROR = u8"Textractor: Hijack ERROR";
|
||||
COULD_NOT_FIND = u8"Textractor: tidak dapat menemukan teks";
|
||||
SELECT_LANGUAGE = u8"Pilih bahasa";
|
||||
BING_PROMPT = u8"Bahasa apakah yang Bing harus terjemahkan?";
|
||||
GOOGLE_PROMPT = u8"Bahasa apakah yang Google harus terjemahkan?";
|
||||
SELECT_LANGUAGE_MESSAGE = u8"Bahasa apakah yang %1 harus terjemahkan?";
|
||||
TOO_MANY_TRANS_REQUESTS = L"Terlalu banyak permintaan terjemahan: menolak untuk menerjemahkan";
|
||||
TRANSLATION_ERROR = L"Terjadi kesalahan ketika menerjemahkan";
|
||||
EXTRA_WINDOW_INFO = u8R"(Klik kanan untuk merubah pengaturan
|
||||
|
Loading…
x
Reference in New Issue
Block a user