summaryrefslogtreecommitdiff
path: root/sw
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 /sw
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 'sw')
-rw-r--r--sw/Library_sw.mk1
-rw-r--r--sw/Library_swui.mk2
-rw-r--r--sw/UIConfig_swriter.mk1
-rw-r--r--sw/inc/strings.hrc1
-rw-r--r--sw/inc/swabstdlg.hxx13
-rw-r--r--sw/sdi/_textsh.sdi6
-rw-r--r--sw/source/ui/dialog/swdlgfact.cxx12
-rw-r--r--sw/source/ui/dialog/swdlgfact.hxx14
-rw-r--r--sw/source/ui/misc/translatelangselect.cxx153
-rw-r--r--sw/source/uibase/inc/translatehelper.hxx42
-rw-r--r--sw/source/uibase/inc/translatelangselect.hxx69
-rw-r--r--sw/source/uibase/shells/textsh1.cxx29
-rw-r--r--sw/source/uibase/shells/translatehelper.cxx208
-rw-r--r--sw/uiconfig/sglobal/menubar/menubar.xml2
-rw-r--r--sw/uiconfig/swriter/menubar/menubar.xml2
-rw-r--r--sw/uiconfig/swriter/ui/translationdialog.ui104
16 files changed, 659 insertions, 0 deletions
diff --git a/sw/Library_sw.mk b/sw/Library_sw.mk
index 22694869fb54..d5c59fa4cc4a 100644
--- a/sw/Library_sw.mk
+++ b/sw/Library_sw.mk
@@ -694,6 +694,7 @@ $(eval $(call gb_Library_add_exception_objects,sw,\
sw/source/uibase/shells/grfsh \
sw/source/uibase/shells/grfshex \
sw/source/uibase/shells/langhelper \
+ sw/source/uibase/shells/translatehelper \
sw/source/uibase/shells/listsh \
sw/source/uibase/shells/mediash \
sw/source/uibase/shells/navsh \
diff --git a/sw/Library_swui.mk b/sw/Library_swui.mk
index 9187d3c209a1..8e9222d97cc1 100644
--- a/sw/Library_swui.mk
+++ b/sw/Library_swui.mk
@@ -79,6 +79,7 @@ $(eval $(call gb_Library_use_libraries,swui,\
vcl \
drawinglayercore \
drawinglayer \
+ lng \
))
$(eval $(call gb_Library_add_exception_objects,swui,\
@@ -152,6 +153,7 @@ $(eval $(call gb_Library_add_exception_objects,swui,\
sw/source/ui/misc/pgfnote \
sw/source/ui/misc/pggrid \
sw/source/ui/misc/srtdlg \
+ sw/source/ui/misc/translatelangselect \
sw/source/ui/misc/swmodalredlineacceptdlg \
sw/source/ui/misc/titlepage \
sw/source/ui/table/colwd \
diff --git a/sw/UIConfig_swriter.mk b/sw/UIConfig_swriter.mk
index f4e003e37243..02ea3ded32d0 100644
--- a/sw/UIConfig_swriter.mk
+++ b/sw/UIConfig_swriter.mk
@@ -188,6 +188,7 @@ $(eval $(call gb_UIConfig_add_uifiles,modules/swriter,\
sw/uiconfig/swriter/ui/insertautotextdialog \
sw/uiconfig/swriter/ui/insertbookmark \
sw/uiconfig/swriter/ui/insertbreak \
+ sw/uiconfig/swriter/ui/translationdialog \
sw/uiconfig/swriter/ui/insertcaption \
sw/uiconfig/swriter/ui/insertdbcolumnsdialog \
sw/uiconfig/swriter/ui/insertfootnote \
diff --git a/sw/inc/strings.hrc b/sw/inc/strings.hrc
index 81a54280d92c..4f2dd39ff86b 100644
--- a/sw/inc/strings.hrc
+++ b/sw/inc/strings.hrc
@@ -299,6 +299,7 @@
#define STR_DELETE_NOTE_AUTHOR NC_("STR_DELETE_NOTE_AUTHOR", "Delete ~All Comments by $1")
#define STR_HIDE_NOTE_AUTHOR NC_("STR_HIDE_NOTE_AUTHOR", "H~ide All Comments by $1")
#define STR_OUTLINE_NUMBERING NC_("STR_OUTLINE_NUMBERING", "Chapter Numbering")
+#define STR_STATSTR_SWTRANSLATE NC_("STR_STATSTR_SWTRANSLATE", "Translating document...")
/* To translators: $1 == will be replaced by STR_WORDCOUNT_WORDARG, and $2 by STR_WORDCOUNT_COLARG
e.g. Selected: 1 word, 2 characters */
#define STR_WORDCOUNT NC_("STR_WORDCOUNT", "Selected: $1, $2")
diff --git a/sw/inc/swabstdlg.hxx b/sw/inc/swabstdlg.hxx
index 5c5f2bc2cae7..02b38a71d5ee 100644
--- a/sw/inc/swabstdlg.hxx
+++ b/sw/inc/swabstdlg.hxx
@@ -383,6 +383,18 @@ public:
virtual sal_uInt16 GetRestartPage() const = 0;
};
+class SwLanguageListItem;
+
+class AbstractSwTranslateLangSelectDlg
+{
+protected:
+ virtual ~AbstractSwTranslateLangSelectDlg() = default;
+public:
+ virtual std::shared_ptr<weld::DialogController> getDialogController() = 0;
+ virtual std::optional<SwLanguageListItem> GetSelectedLanguage() = 0;
+};
+
+
class SwAbstractDialogFactory
{
public:
@@ -406,6 +418,7 @@ public:
CreateSwContentControlListItemDlg(weld::Window* pParent, SwContentControlListItem& rItem) = 0;
virtual std::shared_ptr<AbstractSwBreakDlg> CreateSwBreakDlg(weld::Window *pParent, SwWrtShell &rSh) = 0;
+ virtual std::shared_ptr<AbstractSwTranslateLangSelectDlg> CreateSwTranslateLangSelectDlg(weld::Window *pParent, SwWrtShell &rSh) = 0;
virtual VclPtr<VclAbstractDialog> CreateSwChangeDBDlg(SwView& rVw) = 0;
virtual VclPtr<SfxAbstractTabDialog> CreateSwCharDlg(weld::Window* pParent, SwView& pVw, const SfxItemSet& rCoreSet,
SwCharDlgMode nDialogMode, const OUString* pFormatStr = nullptr) = 0;
diff --git a/sw/sdi/_textsh.sdi b/sw/sdi/_textsh.sdi
index 05f6af09f823..6830a248a24b 100644
--- a/sw/sdi/_textsh.sdi
+++ b/sw/sdi/_textsh.sdi
@@ -1814,5 +1814,11 @@ interface BaseText
StateMethod = GetState ;
]
+ SID_FM_TRANSLATE
+ [
+ ExecMethod = Execute ;
+ StateMethod = GetState ;
+ ]
+
} // end of interface text
diff --git a/sw/source/ui/dialog/swdlgfact.cxx b/sw/source/ui/dialog/swdlgfact.cxx
index 1aa438f01af6..6e9ebc873ab7 100644
--- a/sw/source/ui/dialog/swdlgfact.cxx
+++ b/sw/source/ui/dialog/swdlgfact.cxx
@@ -89,6 +89,7 @@
#include <uiborder.hxx>
#include <mmresultdialogs.hxx>
#include <formatlinebreak.hxx>
+#include <translatelangselect.hxx>
using namespace ::com::sun::star;
using namespace css::frame;
@@ -796,6 +797,12 @@ sal_uInt16 AbstractMailMergeWizard_Impl::GetRestartPage() const
return m_xDlg->GetRestartPage();
}
+std::optional<SwLanguageListItem> AbstractSwTranslateLangSelectDlg_Impl::GetSelectedLanguage()
+{
+ SwTranslateLangSelectDlg* pDlg = dynamic_cast<SwTranslateLangSelectDlg*>(m_xDlg.get());
+ return pDlg->GetSelectedLanguage();
+}
+
VclPtr<AbstractSwInsertAbstractDlg> SwAbstractDialogFactory_Impl::CreateSwInsertAbstractDlg(weld::Window* pParent)
{
return VclPtr<AbstractSwInsertAbstractDlg_Impl>::Create(std::make_unique<SwInsertAbstractDlg>(pParent));
@@ -855,6 +862,11 @@ std::shared_ptr<AbstractSwBreakDlg> SwAbstractDialogFactory_Impl::CreateSwBreakD
return std::make_shared<AbstractSwBreakDlg_Impl>(std::make_unique<SwBreakDlg>(pParent, rSh));
}
+std::shared_ptr<AbstractSwTranslateLangSelectDlg> SwAbstractDialogFactory_Impl::CreateSwTranslateLangSelectDlg(weld::Window* pParent, SwWrtShell &rSh)
+{
+ return std::make_shared<AbstractSwTranslateLangSelectDlg_Impl>(std::make_unique<SwTranslateLangSelectDlg>(pParent, rSh));
+}
+
VclPtr<VclAbstractDialog> SwAbstractDialogFactory_Impl::CreateSwChangeDBDlg(SwView& rVw)
{
#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
diff --git a/sw/source/ui/dialog/swdlgfact.hxx b/sw/source/ui/dialog/swdlgfact.hxx
index c127566ea10a..f97ff430c294 100644
--- a/sw/source/ui/dialog/swdlgfact.hxx
+++ b/sw/source/ui/dialog/swdlgfact.hxx
@@ -188,6 +188,19 @@ public:
virtual std::shared_ptr<weld::DialogController> getDialogController() override { return m_xDlg; }
};
+class AbstractSwTranslateLangSelectDlg_Impl : public AbstractSwTranslateLangSelectDlg
+{
+ std::shared_ptr<weld::DialogController> m_xDlg;
+public:
+ explicit AbstractSwTranslateLangSelectDlg_Impl(std::shared_ptr<weld::DialogController> p)
+ : m_xDlg(std::move(p))
+ {
+ }
+
+ virtual std::shared_ptr<weld::DialogController> getDialogController() override { return m_xDlg; }
+ virtual std::optional<SwLanguageListItem> GetSelectedLanguage() override;
+};
+
class AbstractSwTableWidthDlg_Impl : public VclAbstractDialog
{
std::unique_ptr<SwTableWidthDlg> m_xDlg;
@@ -684,6 +697,7 @@ public:
SwContentControlListItem& rItem) override;
virtual std::shared_ptr<AbstractSwBreakDlg> CreateSwBreakDlg(weld::Window *pParent, SwWrtShell &rSh) override;
+ virtual std::shared_ptr<AbstractSwTranslateLangSelectDlg> CreateSwTranslateLangSelectDlg(weld::Window *pParent, SwWrtShell &rSh) override;
virtual VclPtr<VclAbstractDialog> CreateSwChangeDBDlg(SwView& rVw) override;
virtual VclPtr<SfxAbstractTabDialog> CreateSwCharDlg(weld::Window* pParent, SwView& pVw, const SfxItemSet& rCoreSet,
SwCharDlgMode nDialogMode, const OUString* pFormatStr = nullptr) override;
diff --git a/sw/source/ui/misc/translatelangselect.cxx b/sw/source/ui/misc/translatelangselect.cxx
new file mode 100644
index 000000000000..7e6d02cf94fb
--- /dev/null
+++ b/sw/source/ui/misc/translatelangselect.cxx
@@ -0,0 +1,153 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/svapp.hxx>
+#include <osl/diagnose.h>
+#include <uitool.hxx>
+#include <swtypes.hxx>
+#include <wrtsh.hxx>
+#include <view.hxx>
+#include <viewopt.hxx>
+#include <translatelangselect.hxx>
+#include <pagedesc.hxx>
+#include <poolfmt.hxx>
+#include <sal/log.hxx>
+#include <ndtxt.hxx>
+#include <shellio.hxx>
+#include <svtools/deeplcfg.hxx>
+#include <vcl/idle.hxx>
+#include <mdiexp.hxx>
+#include <strings.hrc>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+#include <sfx2/viewfrm.hxx>
+#include <com/sun/star/task/XStatusIndicatorFactory.hpp>
+#include <linguistic/translate.hxx>
+
+int SwTranslateLangSelectDlg::selectedLangIdx = -1;
+SwTranslateLangSelectDlg::SwTranslateLangSelectDlg(weld::Window* pParent, SwWrtShell& rSh)
+ : GenericDialogController(pParent, "modules/swriter/ui/translationdialog.ui",
+ "LanguageSelectDialog")
+ , rWrtSh(rSh)
+ , m_xLanguageListBox(m_xBuilder->weld_combo_box("combobox1"))
+ , m_xBtnCancel(m_xBuilder->weld_button("cancel"))
+ , m_xBtnTranslate(m_xBuilder->weld_button("translate"))
+ , m_xLanguageVec({
+ SwLanguageListItem("BG", "Bulgarian"),
+ SwLanguageListItem("CS", "Czech"),
+ SwLanguageListItem("DA", "Danish"),
+ SwLanguageListItem("DE", "German"),
+ SwLanguageListItem("EL", "Greek"),
+ SwLanguageListItem("EN-GB", "English (British)"),
+ SwLanguageListItem("EN-US", "English (American)"),
+ SwLanguageListItem("ET", "Estonian"),
+ SwLanguageListItem("FI", "Finnish"),
+ SwLanguageListItem("FR", "French"),
+ SwLanguageListItem("HU", "Hungarian"),
+ SwLanguageListItem("ID", "Indonesian"),
+ SwLanguageListItem("IT", "Italian"),
+ SwLanguageListItem("JA", "Japanese"),
+ SwLanguageListItem("LT", "Lithuanian"),
+ SwLanguageListItem("LV", "Dutch"),
+ SwLanguageListItem("PL", "Polish"),
+ SwLanguageListItem("PT-BR", "Portuguese (Brazilian)"),
+ SwLanguageListItem("PT-PT", "Portuguese (European)"),
+ SwLanguageListItem("RO", "Romanian"),
+ SwLanguageListItem("RU", "Russian"),
+ SwLanguageListItem("SK", "Slovak"),
+ SwLanguageListItem("SL", "Slovenian"),
+ SwLanguageListItem("SV", "Swedish"),
+ SwLanguageListItem("TR", "Turkish"),
+ SwLanguageListItem("ZH", "Chinese (simplified)"),
+ })
+ , m_bTranslationStarted(false)
+ , m_bCancelTranslation(false)
+{
+ m_xLanguageListBox->connect_changed(LINK(this, SwTranslateLangSelectDlg, LangSelectHdl));
+ m_xBtnCancel->connect_clicked(LINK(this, SwTranslateLangSelectDlg, LangSelectCancelHdl));
+ m_xBtnTranslate->connect_clicked(LINK(this, SwTranslateLangSelectDlg, LangSelectTranslateHdl));
+
+ for (const auto& item : m_xLanguageVec)
+ {
+ m_xLanguageListBox->append_text(OStringToOUString(item.getName(), RTL_TEXTENCODING_UTF8));
+ }
+
+ if (SwTranslateLangSelectDlg::selectedLangIdx != -1)
+ {
+ m_xLanguageListBox->set_active(SwTranslateLangSelectDlg::selectedLangIdx);
+ }
+}
+
+std::optional<SwLanguageListItem> SwTranslateLangSelectDlg::GetSelectedLanguage()
+{
+ if (SwTranslateLangSelectDlg::selectedLangIdx != -1)
+ {
+ return m_xLanguageVec.at(SwTranslateLangSelectDlg::selectedLangIdx);
+ }
+
+ return {};
+}
+
+IMPL_STATIC_LINK(SwTranslateLangSelectDlg, LangSelectHdl, weld::ComboBox&, rBox, void)
+{
+ const auto selected = rBox.get_active();
+ SwTranslateLangSelectDlg::selectedLangIdx = selected;
+}
+
+IMPL_LINK_NOARG(SwTranslateLangSelectDlg, LangSelectCancelHdl, weld::Button&, void)
+{
+ // stop translation first
+ if (m_bTranslationStarted)
+ m_bCancelTranslation = true;
+ else
+ m_xDialog->response(RET_CANCEL);
+}
+
+IMPL_LINK_NOARG(SwTranslateLangSelectDlg, LangSelectTranslateHdl, weld::Button&, void)
+{
+ if (SwTranslateLangSelectDlg::selectedLangIdx == -1)
+ {
+ m_xDialog->response(RET_CANCEL);
+ return;
+ }
+
+ SvxDeeplOptions& rDeeplOptions = SvxDeeplOptions::Get();
+ if (rDeeplOptions.getAPIUrl().isEmpty() || rDeeplOptions.getAuthKey().isEmpty())
+ {
+ SAL_WARN("sw.ui", "SwTranslateLangSelectDlg: API options are not set");
+ m_xDialog->response(RET_CANCEL);
+ return;
+ }
+
+ const OString aAPIUrl
+ = OUStringToOString(rtl::Concat2View(rDeeplOptions.getAPIUrl() + "?tag_handling=html"),
+ RTL_TEXTENCODING_UTF8)
+ .trim();
+ const OString aAuthKey
+ = OUStringToOString(rDeeplOptions.getAuthKey(), RTL_TEXTENCODING_UTF8).trim();
+ const auto aTargetLang
+ = m_xLanguageVec.at(SwTranslateLangSelectDlg::selectedLangIdx).getLanguage();
+
+ m_bTranslationStarted = true;
+
+ SwTranslateHelper::TranslateAPIConfig aConfig({ aAPIUrl, aAuthKey, aTargetLang });
+ SwTranslateHelper::TranslateDocumentCancellable(rWrtSh, aConfig, m_bCancelTranslation);
+ m_xDialog->response(RET_OK);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/inc/translatehelper.hxx b/sw/source/uibase/inc/translatehelper.hxx
new file mode 100644
index 000000000000..906b527647b6
--- /dev/null
+++ b/sw/source/uibase/inc/translatehelper.hxx
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+#include <swtypes.hxx>
+
+class SwWrtShell;
+class SwPaM;
+class SwNode;
+class SwTextNode;
+
+namespace SwTranslateHelper
+{
+struct SW_DLLPUBLIC TranslateAPIConfig final
+{
+ const OString m_xAPIUrl;
+ const OString m_xAuthKey;
+ const OString m_xTargetLanguage;
+};
+SW_DLLPUBLIC OString ExportPaMToHTML(SwPaM* pCursor, bool bReplacePTag);
+SW_DLLPUBLIC void PasteHTMLToPaM(SwWrtShell& rWrtSh, SwPaM* pCursor, const OString& rData,
+ bool bSetSelection);
+SW_DLLPUBLIC void TranslateDocument(SwWrtShell& rWrtSh, const TranslateAPIConfig& rConfig);
+SW_DLLPUBLIC void TranslateDocumentCancellable(SwWrtShell& rWrtSh,
+ const TranslateAPIConfig& rConfig,
+ bool& rCancelTranslation);
+}
diff --git a/sw/source/uibase/inc/translatelangselect.hxx b/sw/source/uibase/inc/translatelangselect.hxx
new file mode 100644
index 000000000000..4190aafac442
--- /dev/null
+++ b/sw/source/uibase/inc/translatelangselect.hxx
@@ -0,0 +1,69 @@
+
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+#include <vcl/weld.hxx>
+#include <rtl/string.h>
+#include <vector>
+#include <optional>
+#include "translatehelper.hxx"
+
+class SwWrtShell;
+
+// SwLanguageListItem Helper class for displaying available languages with their names and tags on the listbox.
+class SwLanguageListItem final
+{
+public:
+ SwLanguageListItem(const OString& sLanguage, const OString& sName)
+ : m_sLanguage(sLanguage)
+ , m_sName(sName)
+ {
+ }
+ const OString& getLanguage() const { return m_sLanguage; }
+ const OString& getName() const { return m_sName; }
+
+private:
+ const OString m_sLanguage;
+ const OString m_sName;
+};
+
+// SwTranslateLangSelectDlg Language selection dialog for translation API.
+class SwTranslateLangSelectDlg final : public weld::GenericDialogController
+{
+public:
+ static int selectedLangIdx;
+ SwTranslateLangSelectDlg(weld::Window* pParent, SwWrtShell& rSh);
+ std::optional<SwLanguageListItem> GetSelectedLanguage();
+
+private:
+ SwWrtShell& rWrtSh;
+ std::unique_ptr<weld::ComboBox> m_xLanguageListBox;
+ std::unique_ptr<weld::Button> m_xBtnCancel;
+ std::unique_ptr<weld::Button> m_xBtnTranslate;
+ std::vector<SwLanguageListItem> m_xLanguageVec;
+
+ bool m_bTranslationStarted;
+ bool m_bCancelTranslation;
+
+ DECL_STATIC_LINK(SwTranslateLangSelectDlg, LangSelectHdl, weld::ComboBox&, void);
+ DECL_LINK(LangSelectCancelHdl, weld::Button&, void);
+ DECL_LINK(LangSelectTranslateHdl, weld::Button&, void);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/shells/textsh1.cxx b/sw/source/uibase/shells/textsh1.cxx
index b7cfc8d9f7b5..da69a34ef75d 100644
--- a/sw/source/uibase/shells/textsh1.cxx
+++ b/sw/source/uibase/shells/textsh1.cxx
@@ -103,6 +103,9 @@
#include <bookmark.hxx>
#include <linguistic/misc.hxx>
#include <authfld.hxx>
+#include <translatelangselect.hxx>
+#include <svtools/deeplcfg.hxx>
+#include <translatehelper.hxx>
using namespace ::com::sun::star;
using namespace com::sun::star::beans;
@@ -1489,6 +1492,32 @@ void SwTextShell::Execute(SfxRequest &rReq)
}
}
break;
+ case SID_FM_TRANSLATE:
+ {
+ const SfxPoolItem* pTargetLangStringItem = nullptr;
+ if (pArgs && SfxItemState::SET == pArgs->GetItemState(SID_ATTR_TARGETLANG_STR, false, &pTargetLangStringItem))
+ {
+ SvxDeeplOptions& rDeeplOptions = SvxDeeplOptions::Get();
+ if (rDeeplOptions.getAPIUrl().isEmpty() || rDeeplOptions.getAuthKey().isEmpty())
+ {
+ SAL_WARN("sw.ui", "SID_FM_TRANSLATE: API options are not set");
+ break;
+ }
+ const OString aAPIUrl = OUStringToOString(rtl::Concat2View(rDeeplOptions.getAPIUrl() + "?tag_handling=html"), RTL_TEXTENCODING_UTF8).trim();
+ const OString aAuthKey = OUStringToOString(rDeeplOptions.getAuthKey(), RTL_TEXTENCODING_UTF8).trim();
+ OString aTargetLang = OUStringToOString(static_cast<const SfxStringItem*>(pTargetLangStringItem)->GetValue(), RTL_TEXTENCODING_UTF8);
+ SwTranslateHelper::TranslateAPIConfig aConfig({aAPIUrl, aAuthKey, aTargetLang});
+ SwTranslateHelper::TranslateDocument(rWrtSh, aConfig);
+ }
+ else
+ {
+ SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create();
+ std::shared_ptr<AbstractSwTranslateLangSelectDlg> pAbstractDialog(pFact->CreateSwTranslateLangSelectDlg(GetView().GetFrameWeld(), rWrtSh));
+ std::shared_ptr<weld::DialogController> pDialogController(pAbstractDialog->getDialogController());
+ weld::DialogController::runAsync(pDialogController, [] (sal_Int32 /*nResult*/) { });
+ }
+ }
+ break;
case SID_SPELLCHECK_IGNORE:
{
SwPaM *pPaM = rWrtSh.GetCursor();
diff --git a/sw/source/uibase/shells/translatehelper.cxx b/sw/source/uibase/shells/translatehelper.cxx
new file mode 100644
index 000000000000..92f09913730a
--- /dev/null
+++ b/sw/source/uibase/shells/translatehelper.cxx
@@ -0,0 +1,208 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include <wrtsh.hxx>
+#include <pam.hxx>
+#include <node.hxx>
+#include <ndtxt.hxx>
+#include <translatehelper.hxx>
+#include <sal/log.hxx>
+#include <rtl/string.h>
+#include <shellio.hxx>
+#include <vcl/scheduler.hxx>
+#include <vcl/svapp.hxx>
+#include <boost/property_tree/ptree.hpp>
+#include <boost/property_tree/json_parser.hpp>
+#include <vcl/htmltransferable.hxx>
+#include <vcl/transfer.hxx>
+#include <swdtflvr.hxx>
+#include <linguistic/translate.hxx>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+#include <sfx2/viewfrm.hxx>
+#include <com/sun/star/task/XStatusIndicatorFactory.hpp>
+#include <strings.hrc>
+
+namespace SwTranslateHelper
+{
+OString ExportPaMToHTML(SwPaM* pCursor, bool bReplacePTag)
+{
+ SolarMutexGuard gMutex;
+ OString aResult;
+ WriterRef xWrt;
+ GetHTMLWriter(u"NoLineLimit,SkipHeaderFooter", OUString(), xWrt);
+ if (pCursor != nullptr)
+ {
+ SvMemoryStream aMemoryStream;
+ SwWriter aWriter(aMemoryStream, *pCursor);
+ ErrCode nError = aWriter.Write(xWrt);
+ if (nError.IsError())
+ {
+ SAL_WARN("sw.ui", "ExportPaMToHTML: failed to export selection to HTML");
+ return {};
+ }
+ aResult
+ = OString(static_cast<const char*>(aMemoryStream.GetData()), aMemoryStream.GetSize());
+ if (bReplacePTag)
+ {
+ aResult = aResult.replaceAll("<p", "<span");
+ aResult = aResult.replaceAll("</p>", "</span>");
+ }
+ return aResult;
+ }
+ return {};
+}
+
+void PasteHTMLToPaM(SwWrtShell& rWrtSh, SwPaM* pCursor, const OString& rData, bool bSetSelection)
+{
+ SolarMutexGuard gMutex;
+ rtl::Reference<vcl::unohelper::HtmlTransferable> pHtmlTransferable
+ = new vcl::unohelper::HtmlTransferable(rData);
+ if (pHtmlTransferable.is())
+ {
+ TransferableDataHelper aDataHelper(pHtmlTransferable);
+ if (aDataHelper.GetXTransferable().is()
+ && SwTransferable::IsPasteSpecial(rWrtSh, aDataHelper))
+ {
+ if (bSetSelection)
+ {
+ rWrtSh.SetSelection(*pCursor);
+ }
+ SwTransferable::Paste(rWrtSh, aDataHelper);
+ rWrtSh.KillSelection(nullptr, false);
+ }
+ }
+}
+
+void TranslateDocument(SwWrtShell& rWrtSh, const TranslateAPIConfig& rConfig)
+{
+ bool bCancel = false;
+ TranslateDocumentCancellable(rWrtSh, rConfig, bCancel);
+}
+
+void TranslateDocumentCancellable(SwWrtShell& rWrtSh, const TranslateAPIConfig& rConfig,
+ bool& rCancelTranslation)
+{
+ auto m_pCurrentPam = rWrtSh.GetCursor();
+ bool bHasSelection = rWrtSh.HasSelection();
+
+ if (bHasSelection)
+ {
+ // iteration will start top to bottom
+ if (m_pCurrentPam->GetPoint()->nNode > m_pCurrentPam->GetMark()->nNode)
+ m_pCurrentPam->Exchange();
+ }
+
+ auto const& pNodes = rWrtSh.GetNodes();
+ SwPosition aPoint = *m_pCurrentPam->GetPoint();
+ SwPosition aMark = *m_pCurrentPam->GetMark();
+ auto startNode = bHasSelection ? aPoint.nNode.GetIndex() : SwNodeOffset(0);
+ auto endNode = bHasSelection ? aMark.nNode.GetIndex() : pNodes.Count() - 1;
+
+ sal_Int32 nCount(0);
+ sal_Int32 nProgress(0);
+
+ for (SwNodeOffset n(startNode); n <= endNode; ++n)
+ {
+ if (pNodes[n] && pNodes[n]->IsTextNode())
+ {
+ if (pNodes[n]->GetTextNode()->GetText().isEmpty())
+ continue;
+ nCount++;
+ }
+ }
+
+ SfxViewFrame* pFrame = SfxViewFrame::Current();
+ uno::Reference<frame::XFrame> xFrame = pFrame->GetFrame().GetFrameInterface();
+ uno::Reference<task::XStatusIndicatorFactory> xProgressFactory(xFrame, uno::UNO_QUERY);
+ uno::Reference<task::XStatusIndicator> xStatusIndicator;
+
+ if (xProgressFactory.is())
+ {
+ xStatusIndicator = xProgressFactory->createStatusIndicator();
+ }
+
+ if (xStatusIndicator.is())
+ xStatusIndicator->start(SwResId(STR_STATSTR_SWTRANSLATE), nCount);
+
+ for (SwNodeOffset n(startNode); n <= endNode; ++n)
+ {
+ if (rCancelTranslation)
+ break;
+
+ if (n >= rWrtSh.GetNodes().Count())
+ break;
+
+ if (!pNodes[n])
+ break;
+
+ SwNode* pNode = pNodes[n];
+ if (pNode->IsTextNode())
+ {
+ if (pNode->GetTextNode()->GetText().isEmpty())
+ continue;
+
+ auto cursor
+ = Writer::NewUnoCursor(*rWrtSh.GetDoc(), pNode->GetIndex(), pNode->GetIndex());
+
+ // set edges (start, end) for nodes inside the selection.
+ if (bHasSelection)
+ {
+ if (startNode == endNode)
+ {
+ cursor->SetMark();
+ cursor->GetPoint()->nContent = aPoint.nContent;
+ cursor->GetMark()->nContent = aMark.nContent;
+ }
+ else if (n == startNode)
+ {
+ cursor->SetMark();
+ cursor->GetPoint()->nContent = std::min(aPoint.nContent, aMark.nContent);
+ }
+ else if (n == endNode)
+ {
+ cursor->SetMark();
+ cursor->GetMark()->nContent = aMark.nContent;
+ cursor->GetPoint()->nContent = 0;
+ }
+ }
+
+ const auto aOut = SwTranslateHelper::ExportPaMToHTML(cursor.get(), true);
+ const auto aTranslatedOut = linguistic::Translate(
+ rConfig.m_xTargetLanguage, rConfig.m_xAPIUrl, rConfig.m_xAuthKey, aOut);
+ SwTranslateHelper::PasteHTMLToPaM(rWrtSh, cursor.get(), aTranslatedOut, true);
+
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue((100 * ++nProgress) / nCount);
+
+ Idle aIdle("ProgressBar::SetValue aIdle");
+ aIdle.SetPriority(TaskPriority::POST_PAINT);
+ aIdle.Start();
+
+ rWrtSh.LockView(true);
+ while (aIdle.IsActive() && !Application::IsQuit())
+ {
+ Application::Yield();
+ }
+ rWrtSh.LockView(false);
+ }
+ }
+
+ if (xStatusIndicator.is())
+ xStatusIndicator->end();
+}
+}
diff --git a/sw/uiconfig/sglobal/menubar/menubar.xml b/sw/uiconfig/sglobal/menubar/menubar.xml
index 6246d44392df..4e305c1410f4 100644
--- a/sw/uiconfig/sglobal/menubar/menubar.xml
+++ b/sw/uiconfig/sglobal/menubar/menubar.xml
@@ -720,6 +720,8 @@
<menu:menuitem menu:id=".uno:WordCountDialog" menu:style="text"/>
<menu:menuitem menu:id=".uno:AccessibilityCheck"/>
<menu:menuseparator/>
+ <menu:menuitem menu:id=".uno:Translate" menu:style="text"/>
+ <menu:menuseparator/>
<menu:menu menu:id=".uno:AutoFormatMenu">
<menu:menupopup>
<menu:menuitem menu:id=".uno:OnlineAutoFormat"/>
diff --git a/sw/uiconfig/swriter/menubar/menubar.xml b/sw/uiconfig/swriter/menubar/menubar.xml
index 45a7c3b10b53..06823b5beacc 100644
--- a/sw/uiconfig/swriter/menubar/menubar.xml
+++ b/sw/uiconfig/swriter/menubar/menubar.xml
@@ -745,6 +745,8 @@
<menu:menuitem menu:id=".uno:WordCountDialog"/>
<menu:menuitem menu:id=".uno:AccessibilityCheck"/>
<menu:menuseparator/>
+ <menu:menuitem menu:id=".uno:Translate"/>
+ <menu:menuseparator/>
<menu:menu menu:id=".uno:AutoFormatMenu">
<menu:menupopup>
<menu:menuitem menu:id=".uno:OnlineAutoFormat"/>
diff --git a/sw/uiconfig/swriter/ui/translationdialog.ui b/sw/uiconfig/swriter/ui/translationdialog.ui
new file mode 100644
index 000000000000..a6aff92ab00c
--- /dev/null
+++ b/sw/uiconfig/swriter/ui/translationdialog.ui
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.2 -->
+<interface domain="sw">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkDialog" id="LanguageSelectDialog">
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="title" translatable="yes" context="LanguageSelectDialog">Language Selection</property>
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <property name="default_width">0</property>
+ <property name="default_height">0</property>
+ <property name="type_hint">dialog</property>
+ <child type="titlebar">
+ <placeholder/>
+ </child>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="action-area1">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="cancel">
+ <property name="label">_Cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="translate">
+ <property name="label">_OK</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkGrid" id="grid1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="row_spacing">5</property>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes" context="LanguageSelectDialog">Select the target language for translation</property>
+ <property name="mnemonic_widget">combobox1</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="combobox1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-6">cancel</action-widget>
+ <action-widget response="-5">translate</action-widget>
+ </action-widgets>
+ </object>
+</interface>