diff options
author | Mert Tumer <mert.tumer@collabora.com> | 2022-07-05 12:03:27 +0300 |
---|---|---|
committer | Miklos Vajna <vmiklos@collabora.com> | 2022-10-12 16:52:03 +0200 |
commit | e20d2de7836da52dbf9e528d1043b1e188097bfd (patch) | |
tree | cec9878830ff561c6d9eb492aa25eb8e8b49876a /linguistic | |
parent | d75f27fd738eeb2c7dc6d22f198d55d3a877aa0b (diff) |
new uno command uno:Translate with deepl api
New Uno command added for translation
right now it is only using deepl translation api
There's a section in the options > language settings
for setting up the api url and auth key
uno:Translate is a menu button under Format tab
which will bring up Language Selection dialog for translation.
DeepL can accept html as the input for translation, this new
feature leverages that by exporting paragraphs/selections to
html and paste them back without losing the formatting (in theory)
This works good in general but we may lose formatting in very complex
styled sentences.
Translation works in two ways;
1) Whole document
when there is no selection, it assumes that we want to translate whole
document. Each paragraphs is sent one by one so that the output timeout
can be minimum for each paragraph.
2) Selection
Change-Id: Ia2d3ab2f6757faf565b939e1d670a7dedac33390
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/140624
Tested-by: Jenkins
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
Diffstat (limited to 'linguistic')
-rw-r--r-- | linguistic/Library_lng.mk | 2 | ||||
-rw-r--r-- | linguistic/source/translate.cxx | 71 |
2 files changed, 73 insertions, 0 deletions
diff --git a/linguistic/Library_lng.mk b/linguistic/Library_lng.mk index cff45e3a68ac..6ac44d7770b8 100644 --- a/linguistic/Library_lng.mk +++ b/linguistic/Library_lng.mk @@ -51,6 +51,7 @@ $(eval $(call gb_Library_use_externals,lng,\ boost_headers \ icuuc \ icu_headers \ + curl \ )) $(eval $(call gb_Library_add_exception_objects,lng,\ @@ -72,6 +73,7 @@ $(eval $(call gb_Library_add_exception_objects,lng,\ linguistic/source/spelldsp \ linguistic/source/spelldta \ linguistic/source/thesdsp \ + linguistic/source/translate \ )) # vim: set noet sw=4 ts=4: diff --git a/linguistic/source/translate.cxx b/linguistic/source/translate.cxx new file mode 100644 index 000000000000..5337c33153e1 --- /dev/null +++ b/linguistic/source/translate.cxx @@ -0,0 +1,71 @@ +#include <linguistic/translate.hxx> +#include <sal/log.hxx> +#include <curl/curl.h> +#include <sal/log.hxx> +#include <rtl/string.h> +#include <boost/property_tree/ptree.hpp> +#include <boost/property_tree/json_parser.hpp> +#include <vcl/htmltransferable.hxx> +#include <tools/long.hxx> + +namespace linguistic +{ +OString Translate(const OString& rTargetLang, const OString& rAPIUrl, const OString& rAuthKey, + const OString& rData) +{ + constexpr tools::Long CURL_TIMEOUT = 10L; + + std::unique_ptr<CURL, std::function<void(CURL*)>> curl(curl_easy_init(), + [](CURL* p) { curl_easy_cleanup(p); }); + curl_easy_setopt(curl.get(), CURLOPT_URL, rAPIUrl.getStr()); + curl_easy_setopt(curl.get(), CURLOPT_FAILONERROR, 1L); + curl_easy_setopt(curl.get(), CURLOPT_TIMEOUT, CURL_TIMEOUT); + + std::string response_body; + curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, + +[](void* buffer, size_t size, size_t nmemb, void* userp) -> size_t { + if (!userp) + return 0; + std::string* response = static_cast<std::string*>(userp); + size_t real_size = size * nmemb; + response->append(static_cast<char*>(buffer), real_size); + return real_size; + }); + curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, static_cast<void*>(&response_body)); + OString aLang(curl_easy_escape(curl.get(), rTargetLang.getStr(), rTargetLang.getLength())); + OString aAuthKey(curl_easy_escape(curl.get(), rAuthKey.getStr(), rAuthKey.getLength())); + OString aData(curl_easy_escape(curl.get(), rData.getStr(), rData.getLength())); + OString aPostData("auth_key=" + aAuthKey + "&target_lang=" + aLang + "&text=" + aData); + + curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDS, aPostData.getStr()); + CURLcode cc = curl_easy_perform(curl.get()); + if (cc != CURLE_OK) + { + SAL_WARN("linguistic", + "Translate: CURL perform returned with error: " << static_cast<sal_Int32>(cc)); + return {}; + } + tools::Long nStatusCode; + curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, &nStatusCode); + if (nStatusCode != 200) + { + SAL_WARN("linguistic", + "Translate: CURL request returned with status code: " << nStatusCode); + return {}; + } + // parse the response + boost::property_tree::ptree root; + std::stringstream aStream(response_body.data()); + boost::property_tree::read_json(aStream, root); + boost::property_tree::ptree& translations = root.get_child("translations"); + size_t size = translations.size(); + if (size <= 0) + { + SAL_WARN("linguistic", "Translate: API did not return any translations"); + } + // take the first one + const boost::property_tree::ptree& translation = translations.begin()->second; + const std::string text = translation.get<std::string>("text"); + return OString(text); +} +} |