summaryrefslogtreecommitdiff
path: root/linguistic
diff options
context:
space:
mode:
authorMert Tumer <mert.tumer@collabora.com>2022-07-05 12:03:27 +0300
committerMiklos Vajna <vmiklos@collabora.com>2022-10-12 16:52:03 +0200
commite20d2de7836da52dbf9e528d1043b1e188097bfd (patch)
treecec9878830ff561c6d9eb492aa25eb8e8b49876a /linguistic
parentd75f27fd738eeb2c7dc6d22f198d55d3a877aa0b (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.mk2
-rw-r--r--linguistic/source/translate.cxx71
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);
+}
+}