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

⌈⌋ ⎇ branch:  PageTranslate


Check-in [cc30af6872]

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

Overview
Comment:update http version to 2.0, freeze requests at 2.25 for Py2/AOO, update six, apply .skip() for draw documents
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: cc30af68726d28206e794e88ef801042075a3b3c
User & Date: mario 2022-10-15 04:20:44
Context
2022-10-15
17:07
Prepare **params for different frontend, add __str__ for log decoration. check-in: e3207de9d6 user: mario tags: trunk
04:20
update http version to 2.0, freeze requests at 2.25 for Py2/AOO, update six, apply .skip() for draw documents check-in: cc30af6872 user: mario tags: trunk
04:18
split BackendUtils from GoogleWeb, document API check-in: 204f7ec8ff user: mario tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

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.9.104"/>
  <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.9.200"/>
  <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.9.195
# state: beta
# author: mario
# url: https://fossil.include-once.org/pagetranslate/
# depends: python:requests (>= 2.5), python:uno
# pack: *.py, pythonpath/*.py, META-INF/*, pkg-desc, *.x*, icons/*
# config:
#    { name: frames, type: bool, value: 0, description: traverse TextFrames }







|







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.9.200
# state: beta
# author: mario
# url: https://fossil.include-once.org/pagetranslate/
# depends: python:requests (>= 2.5), python:uno
# pack: *.py, pythonpath/*.py, META-INF/*, pkg-desc, *.x*, icons/*
# config:
#    { name: frames, type: bool, value: 0, description: traverse TextFrames }
188
189
190
191
192
193
194


195
196
197
198
199
200
201
202

203
204
205
206
207
208
209
210
211

212
213
214
215
216
217
218
219
            # table/cells
            if para.supportsService("com.sun.star.text.TextTable"):
                for cellname in para.getCellNames():
                    self.log.debug("table/cells:cellname = %r", cellname)
                    text = para.getCellByName(cellname).getText()
                    # not an enumeration, but simple linebreak-formatting in cells
                    orig = text.getString()


                    text.setString(self.t.linebreakwise(orig))
                    self.add_comment(text, orig)
            # subdocuments?
            elif para.supportsService("com.sun.star.text.TextFrame"):
                self.log.debug(".traverse: TextFrame.Enumeration…")
                self.traverse(para.getText().createEnumeration())
            # ignore existing textfields/comments?
            # if para.supportsService("com.sun.star.text.textfield.Annotation"):

            elif para.supportsService("com.sun.star.text.XTextField"): # Annotations aren't differentiatable from SwXTextPortion
                pass
            # a paragraph can be further enumerated for text portions (same character/style attributes),
            # but that will obviously slow things down further / also complicate coherent translations
            elif slow and para.supportsService("com.sun.star.text.Paragraph"): # doesn't work with com.sun.star.container.XEnumerationAccess?
                self.traverse(para.createEnumeration(), slow=0)  # list of TextPortion`s
            # normal flow text / paragraph
            elif para.supportsService("com.sun.star.text.TextContent") or para.supportsService("com.sun.star.text.TextPortion"):
                orig = para.getString()

                #raise Exception(orig)
                text = self.t.translate(orig)
                para.setString(text)
                self.add_comment(para, orig)
            else:
                self.log.warning(".traverse: Unsupported document element.")
                #self.mri(para)








>
>







|
>
|








>
|







188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
            # table/cells
            if para.supportsService("com.sun.star.text.TextTable"):
                for cellname in para.getCellNames():
                    self.log.debug("table/cells:cellname = %r", cellname)
                    text = para.getCellByName(cellname).getText()
                    # not an enumeration, but simple linebreak-formatting in cells
                    orig = text.getString()
                    if self.t.skip(orig):
                        continue
                    text.setString(self.t.linebreakwise(orig))
                    self.add_comment(text, orig)
            # subdocuments?
            elif para.supportsService("com.sun.star.text.TextFrame"):
                self.log.debug(".traverse: TextFrame.Enumeration…")
                self.traverse(para.getText().createEnumeration())
            # ignore existing textfields/comments?
            elif para.supportsService("com.sun.star.text.textfield.Annotation"):
                pass
            elif para.supportsService("com.sun.star.text.XTextField"):
                pass
            # a paragraph can be further enumerated for text portions (same character/style attributes),
            # but that will obviously slow things down further / also complicate coherent translations
            elif slow and para.supportsService("com.sun.star.text.Paragraph"): # doesn't work with com.sun.star.container.XEnumerationAccess?
                self.traverse(para.createEnumeration(), slow=0)  # list of TextPortion`s
            # normal flow text / paragraph
            elif para.supportsService("com.sun.star.text.TextContent") or para.supportsService("com.sun.star.text.TextPortion"):
                orig = para.getString()
                if self.t.skip(orig):  # preempt short/empty segments (incidentally preserves Annotations and TextFields)
                    continue
                text = self.t.translate(orig)
                para.setString(text)
                self.add_comment(para, orig)
            else:
                self.log.warning(".traverse: Unsupported document element.")
                #self.mri(para)

241
242
243
244
245
246
247



248
249
250
251
252
253
254
255
    def drawtranslate(self, pages):
        for page_index in range(0, pages.getCount()):
            page = pages.getByIndex(page_index)
            for shape_index in range(0, page.getCount()):
                shape = page.getByIndex(shape_index)
                if shape.supportsService("com.sun.star.drawing.TextShape"):
                    self.log.info(".drawtranslate: shape = %r", str(shape))



                    shape.Text.setString(self.t.translate(shape.Text.getString()))

    #-- TradutorLibreText (selection rewrite)
    def rewrite_selection(self, xTextRange):
        self.log.info(".rewrite_selection() ---- begin ----")

        # Get selected text and language
        string = xTextRange.getString()







>
>
>
|







245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
    def drawtranslate(self, pages):
        for page_index in range(0, pages.getCount()):
            page = pages.getByIndex(page_index)
            for shape_index in range(0, page.getCount()):
                shape = page.getByIndex(shape_index)
                if shape.supportsService("com.sun.star.drawing.TextShape"):
                    self.log.info(".drawtranslate: shape = %r", str(shape))
                    text = shape.Text.getString()
                    if self.t.skip(text):
                        continue
                    shape.Text.setString(self.t.translate(text))

    #-- TradutorLibreText (selection rewrite)
    def rewrite_selection(self, xTextRange):
        self.log.info(".rewrite_selection() ---- begin ----")

        # Get selected text and language
        string = xTextRange.getString()

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:
	pip2 install requests langdetect six -t ./ --upgrade --no-compile
	#pip install translate -t ./ --upgrade --no-compile
	pip3 install deep-translator -t ./ --upgrade --no-compile










<


>
>

1
2
3
4
5
6
7

8
9
10
11
12
# 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 translate -t ./ --upgrade --no-compile
	pip3 install deep-translator -t ./ --upgrade --no-compile
	# freeze requests at 2.25 (last Python2.7-compatible)
	pip2 install requests==2.25.1 langdetect six -t ./ --upgrade --no-compile

Changes to pythonpath/httprequests.py.

64
65
66
67
68
69
70
71
72
73
74
            return self

    http = FakeRequests()
    log.info("using fake_requests() for now")

# headers
http.headers.update({
    "User-Agent": "Mozilla/5.0 (X11; Linux; LibreOffice/7.2), PageTranslate/1.9",
    "Accept-Language": "*; q=1.0",
    "Accept-Encoding": "utf-8"
})







|



64
65
66
67
68
69
70
71
72
73
74
            return self

    http = FakeRequests()
    log.info("using fake_requests() for now")

# headers
http.headers.update({
    "User-Agent": "Mozilla/5.0 (X11; Linux; LibreOffice/7.4), PageTranslate/2.0",
    "Accept-Language": "*; q=1.0",
    "Accept-Encoding": "utf-8"
})

Changes to pythonpath/six.py.

25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import functools
import itertools
import operator
import sys
import types

__author__ = "Benjamin Peterson <benjamin@python.org>"
__version__ = "1.15.0"


# Useful for very coarse version differentiation.
PY2 = sys.version_info[0] == 2
PY3 = sys.version_info[0] == 3
PY34 = sys.version_info[0:2] >= (3, 4)








|







25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import functools
import itertools
import operator
import sys
import types

__author__ = "Benjamin Peterson <benjamin@python.org>"
__version__ = "1.16.0"


# Useful for very coarse version differentiation.
PY2 = sys.version_info[0] == 2
PY3 = sys.version_info[0] == 3
PY34 = sys.version_info[0:2] >= (3, 4)

67
68
69
70
71
72
73





74
75
76
77
78
79
80
            # 32-bit
            MAXSIZE = int((1 << 31) - 1)
        else:
            # 64-bit
            MAXSIZE = int((1 << 63) - 1)
        del X







def _add_doc(func, doc):
    """Add documentation to a function."""
    func.__doc__ = doc


def _import_module(name):







>
>
>
>
>







67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
            # 32-bit
            MAXSIZE = int((1 << 31) - 1)
        else:
            # 64-bit
            MAXSIZE = int((1 << 63) - 1)
        del X

if PY34:
    from importlib.util import spec_from_loader
else:
    spec_from_loader = None


def _add_doc(func, doc):
    """Add documentation to a function."""
    func.__doc__ = doc


def _import_module(name):
181
182
183
184
185
186
187





188
189
190
191
192
193
194
    def _get_module(self, fullname):
        return self.known_modules[self.name + "." + fullname]

    def find_module(self, fullname, path=None):
        if fullname in self.known_modules:
            return self
        return None






    def __get_module(self, fullname):
        try:
            return self.known_modules[fullname]
        except KeyError:
            raise ImportError("This loader does not know module " + fullname)








>
>
>
>
>







186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
    def _get_module(self, fullname):
        return self.known_modules[self.name + "." + fullname]

    def find_module(self, fullname, path=None):
        if fullname in self.known_modules:
            return self
        return None

    def find_spec(self, fullname, path, target=None):
        if fullname in self.known_modules:
            return spec_from_loader(fullname, self)
        return None

    def __get_module(self, fullname):
        try:
            return self.known_modules[fullname]
        except KeyError:
            raise ImportError("This loader does not know module " + fullname)

218
219
220
221
222
223
224






225
226
227
228
229
230
231
    def get_code(self, fullname):
        """Return None

        Required, if is_package is implemented"""
        self.__get_module(fullname)  # eventually raises ImportError
        return None
    get_source = get_code  # same as get_code







_importer = _SixMetaPathImporter(__name__)


class _MovedItems(_LazyModule):

    """Lazy loading of moved objects"""







>
>
>
>
>
>







228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
    def get_code(self, fullname):
        """Return None

        Required, if is_package is implemented"""
        self.__get_module(fullname)  # eventually raises ImportError
        return None
    get_source = get_code  # same as get_code

    def create_module(self, spec):
        return self.load_module(spec.name)

    def exec_module(self, module):
        pass

_importer = _SixMetaPathImporter(__name__)


class _MovedItems(_LazyModule):

    """Lazy loading of moved objects"""