LibreOffice plugin to pipe whole Writer documents through Google Translate, that ought to keep most of the page formatting.

⌈⌋ ⎇ branch:  PageTranslate


Check-in [0b1b17e5df]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Prepare for TextFrame support, reinstate self.t=translationbackends.* after lang= change in selection mode. Dialogs´ callHandlerMethod throws empty window in AOO.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 0b1b17e5df2e25330d6f26189b2cd063ee2e705f
User & Date: mario 2020-05-28 13:47:33
Context
2020-05-28
13:47
Release anyway. check-in: e301a66d9c user: mario tags: trunk, 1.4
13:47
Prepare for TextFrame support, reinstate self.t=translationbackends.* after lang= change in selection mode. Dialogs´ callHandlerMethod throws empty window in AOO. check-in: 0b1b17e5df user: mario tags: trunk
13:45
UTF-8 fixes for Py2+3 check-in: 07d99dabfb user: mario tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to Addons.xcu.

41
42
43
44
45
46
47
48
49
50
51






52
53
54
55
56
57
58
                  <prop oor:name="Context" oor:type="xs:string"><value/></prop>
                  <prop oor:name="URL" oor:type="xs:string"><value>service:org.openoffice.comp.pyuno.pagetranslate?trigger&amp;lang=locale</value></prop>
                  <prop oor:name="Title" oor:type="xs:string"><value/><value xml:lang="en-US">→System language 🇪🇺</value></prop>
                  <prop oor:name="Target" oor:type="xs:string"><value>_self</value></prop>
                </node>
                <node oor:name="M3PARA" oor:op="replace">
                  <prop oor:name="Context" oor:type="xs:string"><value/></prop>
                  <prop oor:name="URL" oor:type="xs:string"><value>service:org.openoffice.comp.pyuno.pagetranslate?tradutor&amp;lang=paragraph</value></prop>
                  <prop oor:name="Title" oor:type="xs:string"><value/><value xml:lang="en-US">→Paragraph locale</value></prop>
                  <prop oor:name="Target" oor:type="xs:string"><value>_self</value></prop>
                </node>






              </node>
            </node>
          </node>
        </node>
      </node>
    </node>








|



>
>
>
>
>
>







41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
                  <prop oor:name="Context" oor:type="xs:string"><value/></prop>
                  <prop oor:name="URL" oor:type="xs:string"><value>service:org.openoffice.comp.pyuno.pagetranslate?trigger&amp;lang=locale</value></prop>
                  <prop oor:name="Title" oor:type="xs:string"><value/><value xml:lang="en-US">→System language 🇪🇺</value></prop>
                  <prop oor:name="Target" oor:type="xs:string"><value>_self</value></prop>
                </node>
                <node oor:name="M3PARA" oor:op="replace">
                  <prop oor:name="Context" oor:type="xs:string"><value/></prop>
                  <prop oor:name="URL" oor:type="xs:string"><value>service:org.openoffice.comp.pyuno.pagetranslate?selection&amp;lang=paragraph</value></prop>
                  <prop oor:name="Title" oor:type="xs:string"><value/><value xml:lang="en-US">→Paragraph locale</value></prop>
                  <prop oor:name="Target" oor:type="xs:string"><value>_self</value></prop>
                </node>
                <node oor:name="M3PARA2" oor:op="replace">
                  <prop oor:name="Context" oor:type="xs:string"><value/></prop>
                  <prop oor:name="URL" oor:type="xs:string"><value>service:org.openoffice.comp.pyuno.pagetranslate?tradutor&amp;lang=en</value></prop>
                  <prop oor:name="Title" oor:type="xs:string"><value/><value xml:lang="en-US">→Paragraph langinfo</value></prop>
                  <prop oor:name="Target" oor:type="xs:string"><value>_self</value></prop>
                </node>
              </node>
            </node>
          </node>
        </node>
      </node>
    </node>

Changes to description.xml.

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8"?>
<description xmlns="http://openoffice.org/extensions/description/2006" xmlns:dep="http://openoffice.org/extensions/description/2006" xmlns:xlink="http://www.w3.org/1999/xlink">
  <identifier value="vnd.include-once.pagetranslate"/>
  <version value="1.3"/>
  <display-name>
    <name lang="en">PageTranslate</name>
  </display-name>
  <dependencies>
    <OpenOffice.org-minimal-version value="3.0" dep:name="OpenOffice.org 3.0"/>
  </dependencies>
  <registration>



|







1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8"?>
<description xmlns="http://openoffice.org/extensions/description/2006" xmlns:dep="http://openoffice.org/extensions/description/2006" xmlns:xlink="http://www.w3.org/1999/xlink">
  <identifier value="vnd.include-once.pagetranslate"/>
  <version value="1.4"/>
  <display-name>
    <name lang="en">PageTranslate</name>
  </display-name>
  <dependencies>
    <OpenOffice.org-minimal-version value="3.0" dep:name="OpenOffice.org 3.0"/>
  </dependencies>
  <registration>

Changes to pagetranslate.py.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/python
# encoding: utf-8
# api: uno
# type: callback
# category: language
# title: PageTranslate
# description: Action button to get whole Writer document translated
# version: 1.3.24
# state: beta
# author: mario
# url: https://fossil.include-once.org/pagetranslate/
# depends: python:requests (>= 2.5), python:translate
# pack: *.py, pythonpath/*.py, META-INF/*, pkg-desc, *.x*, icons/*
# license: GNU LGPL 2.1
# forked-from: TradutorLibreText (Claudemir de Almeida Rosa)







|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/python
# encoding: utf-8
# api: uno
# type: callback
# category: language
# title: PageTranslate
# description: Action button to get whole Writer document translated
# version: 1.4
# state: beta
# author: mario
# url: https://fossil.include-once.org/pagetranslate/
# depends: python:requests (>= 2.5), python:translate
# pack: *.py, pythonpath/*.py, META-INF/*, pkg-desc, *.x*, icons/*
# license: GNU LGPL 2.1
# forked-from: TradutorLibreText (Claudemir de Almeida Rosa)
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# sys modules
import os, sys
import string, json, re
from traceback import format_exc
from tempfile import gettempdir
# log file
import logging as log
log.basicConfig(filename='%s/pagetranslate-libreoffice.log'%gettempdir(), level=log.WARNING)
# pythonpath/*.py modules
import httprequests
httprequests.log = log
import translationbackends
translationbackends.log = log









|







53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# sys modules
import os, sys
import string, json, re
from traceback import format_exc
from tempfile import gettempdir
# log file
import logging as log
log.basicConfig(filename='%s/pagetranslate-libreoffice.log'%gettempdir(), level=log.DEBUG)
# pythonpath/*.py modules
import httprequests
httprequests.log = log
import translationbackends
translationbackends.log = log


93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
        desktop = self.ctx.ServiceManager.createInstanceWithContext( "com.sun.star.frame.Desktop", self.ctx )
        self.document = desktop.getCurrentComponent()
        #self.dispatcher = self.ctx.ServiceManager.createInstanceWithContext("com.sun.star.frame.DispatchHelper", self.ctx)


    # invoked from toolbar button
    def trigger(self, args):
        log.debug(".trigger(args=%s) invoked" % repr(args))
        try:
            # merge defaults from registry + params from args
            self.params.update(settings(self.ctx).read())
            self.params.update(self.argparse(args))
            if self.params.get("debug"):
                log.root.handlers[0].setLevel(log.DEBUG)
            log.info(repr(self.params))







|







93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
        desktop = self.ctx.ServiceManager.createInstanceWithContext( "com.sun.star.frame.Desktop", self.ctx )
        self.document = desktop.getCurrentComponent()
        #self.dispatcher = self.ctx.ServiceManager.createInstanceWithContext("com.sun.star.frame.DispatchHelper", self.ctx)


    # invoked from toolbar button
    def trigger(self, args):
        log.info(".trigger(args=%s) invoked" % repr(args))
        try:
            # merge defaults from registry + params from args
            self.params.update(settings(self.ctx).read())
            self.params.update(self.argparse(args))
            if self.params.get("debug"):
                log.root.handlers[0].setLevel(log.DEBUG)
            log.info(repr(self.params))
121
122
123
124
125
126
127

128
129
130
131
132
133
134
135
136
137
138
            selection = self.document.getCurrentController().getSelection().getByIndex(0)
            if len(selection.getString()):
                return self.rewrite_selection(selection)

            # else iterate over text snippets
            tree = self.document.getText().createEnumeration()
            self.traverse(tree)


        except Exception as exc:
            log.error(format_exc())
            self.MessageBox(format_exc())
        log.info("----")

    
    # break up UNO service: url query string `.pagetranslate?page&lang=en`
    def argparse(self, args):
        # parameterize leading ?action&
        args = "mode=" + args







>



|







121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
            selection = self.document.getCurrentController().getSelection().getByIndex(0)
            if len(selection.getString()):
                return self.rewrite_selection(selection)

            # else iterate over text snippets
            tree = self.document.getText().createEnumeration()
            self.traverse(tree)
            #self.document.getTextFrames().getElementNames+getByName()

        except Exception as exc:
            log.error(format_exc())
            self.MessageBox(format_exc(), MsgType=ERRORBOX)
        log.info("----")

    
    # break up UNO service: url query string `.pagetranslate?page&lang=en`
    def argparse(self, args):
        # parameterize leading ?action&
        args = "mode=" + args
154
155
156
157
158
159
160



161
162
163
164
165
166
167
            if para.supportsService("com.sun.star.text.TextTable"):
                for cellname in para.getCellNames():
                    log.debug(cellname)
                    text = para.getCellByName(cellname).getText()
                    #self.traverse(text.createEnumeration())
                    text.setString(self.t.linebreakwise(text.getString())) # or .translate #linebreakwise
                pass



            # normal flow text
            elif para.supportsService("com.sun.star.text.TextContent"):
                text = para.getString()
                text = self.t.translate(text)
                para.setString(text)
                # the paragraph itself can be enumerated for text portions,
                # but for now it's really slow enough







>
>
>







155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
            if para.supportsService("com.sun.star.text.TextTable"):
                for cellname in para.getCellNames():
                    log.debug(cellname)
                    text = para.getCellByName(cellname).getText()
                    #self.traverse(text.createEnumeration())
                    text.setString(self.t.linebreakwise(text.getString())) # or .translate #linebreakwise
                pass
            # subdocuments?
            elif para.supportsService("com.sun.star.text.TextFrame"):
                pass
            # normal flow text
            elif para.supportsService("com.sun.star.text.TextContent"):
                text = para.getString()
                text = self.t.translate(text)
                para.setString(text)
                # the paragraph itself can be enumerated for text portions,
                # but for now it's really slow enough
186
187
188
189
190
191
192



193
194
195
196
197
198
199
        # Get selected text and language
        string = xTextRange.getString()
        if self.params["lang"] == "paragraph":
            self.params["lang"] = xTextRange.CharLocale.Language
        elif self.params["mode"] == "tradutor":
            code = self.getOoLocale()
            self.params["lang"] = self.getParaLang(xTextRange).Language




        # translate/replace (plain text) with linebreaks intact
        trans = self.t.linebreakwise(string)
        trans = trans.replace('\\n',"\n").replace('\\r',"\n")
        xTextRange.setString(trans)

    # Query system locale







>
>
>







190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
        # Get selected text and language
        string = xTextRange.getString()
        if self.params["lang"] == "paragraph":
            self.params["lang"] = xTextRange.CharLocale.Language
        elif self.params["mode"] == "tradutor":
            code = self.getOoLocale()
            self.params["lang"] = self.getParaLang(xTextRange).Language
        log.debug("paragraph.lang="+self.params["lang"])
        # we kinda have to reinstantiate the backend, because params` lang= might be hard-applied to handler (e.g. translate-python)
        self.t = translationbackends.assign_service(self.params)

        # translate/replace (plain text) with linebreaks intact
        trans = self.t.linebreakwise(string)
        trans = trans.replace('\\n',"\n").replace('\\r',"\n")
        xTextRange.setString(trans)

    # Query system locale
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
    # Langinfo=(com.sun.star.i18n.LanguageCountryInfo){ Language = (string)"de", LanguageDefaultName = (string)"German", Country = (string)"DE", CountryDefaultName = (string)"Germany", Variant = (string)"" }
    def getParaLang(self, xTextRange):
        Langinfo = self.LocaleData.getLanguageCountryInfo(xTextRange.CharLocale)
        log.info("Langinfo="+repr(Langinfo))
        return Langinfo

    # user notifications
    def MessageBox(self,MsgText, MsgTitle="", MsgType=MESSAGEBOX, MsgButtons=BUTTONS_OK):
        ParentWin = self.document.getCurrentController().Frame.ContainerWindow
        ctx = uno.getComponentContext()
        sm = ctx.ServiceManager
        sv = sm.createInstanceWithContext("com.sun.star.awt.Toolkit", ctx)
        myBox = sv.createMessageBox(ParentWin, MsgType, MsgButtons, MsgTitle, MsgText)
        return myBox.execute()








|







215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
    # Langinfo=(com.sun.star.i18n.LanguageCountryInfo){ Language = (string)"de", LanguageDefaultName = (string)"German", Country = (string)"DE", CountryDefaultName = (string)"Germany", Variant = (string)"" }
    def getParaLang(self, xTextRange):
        Langinfo = self.LocaleData.getLanguageCountryInfo(xTextRange.CharLocale)
        log.info("Langinfo="+repr(Langinfo))
        return Langinfo

    # user notifications
    def MessageBox(self, MsgText, MsgTitle="", MsgType=MESSAGEBOX, MsgButtons=BUTTONS_OK):
        ParentWin = self.document.getCurrentController().Frame.ContainerWindow
        ctx = uno.getComponentContext()
        sm = ctx.ServiceManager
        sv = sm.createInstanceWithContext("com.sun.star.awt.Toolkit", ctx)
        myBox = sv.createMessageBox(ParentWin, MsgType, MsgButtons, MsgTitle, MsgText)
        return myBox.execute()

248
249
250
251
252
253
254

255

256
257
258
259
260
261
262
            if self.access.hasByName(name):
                self.access.setPropertyValue(name, value)
        self.access.commitChanges()

    # invoked on dialog initialization or for saving
    def callHandlerMethod(self, window=".UnoDialogControl", action="initialize|ok|back", name="external_event"):
        try:

            params = self.read()

            # iterate over all dialog controls by name, and assign from/to config dict
            for name, cntrl in [(c.Model.Name, c) for c in window.getControls()]:
                if action == "initialize":
                    self.setControlValue(cntrl, params.get(name, ""))
                elif action == "ok":
                    params[name] = self.getControlValue(cntrl)
            if action == "ok":







>

>







255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
            if self.access.hasByName(name):
                self.access.setPropertyValue(name, value)
        self.access.commitChanges()

    # invoked on dialog initialization or for saving
    def callHandlerMethod(self, window=".UnoDialogControl", action="initialize|ok|back", name="external_event"):
        try:
            log.debug("callHandlerMethod({}, {}, {})".format(repr(window), action, name))
            params = self.read()
            log.info(repr(params))
            # iterate over all dialog controls by name, and assign from/to config dict
            for name, cntrl in [(c.Model.Name, c) for c in window.getControls()]:
                if action == "initialize":
                    self.setControlValue(cntrl, params.get(name, ""))
                elif action == "ok":
                    params[name] = self.getControlValue(cntrl)
            if action == "ok":

Changes to pythonpath/Makefile.

1
2
3
4
5
6
7
8
9
10
11
# The pythonpath/ directory can be packaged alongside, to inject new python
# packages into LibreOffices` python bundle. Which really just makes sense
# for the Windows package, because distro-packaged Office setups utilize the
# system python path.


all:
	pip install requests -t ./ --upgrade

clean:
	find . -iname '*py[co]*' -exec rm -r {} \;







|

<
<
1
2
3
4
5
6
7
8
9


# The pythonpath/ directory can be packaged alongside, to inject new python
# packages into LibreOffices` python bundle. Which really just makes sense
# for the Windows package, because distro-packaged Office setups utilize the
# system python path.


all:
	pip install requests translate six -t ./ --upgrade --no-compile