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

⌈⌋ ⎇ branch:  PageTranslate


Check-in [098c56d9cf]

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

Overview
Comment:add little standalone tool
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 098c56d9cf5d8ff8311bef219f262ca5e4e6fb83
User & Date: mario 2022-10-15 20:40:05
Context
2022-10-15
21:57
DeepL Web was a trivial fix. check-in: d2bb771af3 user: mario tags: trunk
20:40
add little standalone tool check-in: 098c56d9cf user: mario tags: trunk
19:58
fix invalid log calls check-in: 4a972afa0a user: mario tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Added setup.py.

























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/usr/bin/env python3
# encoding: utf-8
# api: pip
# type: build
# title: config for setuptools
#
# Always prefer setuptools over distutils
#

from pluginconf.setup import setup

setup(
    debug=1,
    fn="tk_translate/__init__.py",
    name="tk_translate",
    packages=["tk_translate"],
    package_dir={"": "."},
    entry_points={
        "console_scripts": [
            "tk_translate=tk_translate:main",
        ]
    },
    extras_require={
        "build": ["pluginconf","setuptools"],
        "unused": ["ttkthemes"]
    },
#    universal=True
)

Added tk_translate/__init__.py.























































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
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
#!/usr/bin/env python3
# encoding: utf-8
# fmt: off
# api: python
# type: gui
# title: standalone PageTranslate
# description: Utilizes translationbackends in trivial from→to texteditor
# category: transform
# version: 0.1
# state: beta
# license: GNU-LGPL-2.1
# config: -
# priority: optional
# depends: python >= 3.8, python:PySimpleGUI >= 4.37, python:requests
# pack: pythonpath/*.py=gui/
# architecture: all
# classifiers: translation
# keywords: translation
# url: https://fossil.include-once.org/pagetranslate/
# doc-format: text/markdown
#
# **tk-translate** is a PySimpleGUI variant of
# [PageTranslate](https://fossil.include-once.org/pagetranslate/).
# It provides a terse GUI to get some text translated using one of the various
# services from PT or Deep-Translator. Albeit it has no config dialog, thus
# won't pacify API-key requirements. It's mostly just meant for testing.
#
# Presents two input boxes, some buttons, for plain text translations.
# Usage:
#
#  🞂 Insert text into left input
#
#  🞂 Select backend
#
#  🞂 Change target language
#
#  🞂 Hit translate
#
# Defaults must be edited in tk_translate/__init__.py conf={}.
#


import sys, os, re, json, subprocess, warnings
import tkinter as tk, PySimpleGUI as sg  # ⚠ install python3-tk / tkinter in your distro package manager
from operator import itemgetter
from traceback import format_exc
sys.path.append("./pythonpath")
import translationbackends
import logging as log
log.basicConfig(level=log.DEBUG)
sys.excepthook = lambda *exc: log.critical(format_exc())


#-- init
conf = dict(
    mode = "page",      # unused
    quick = 0,          # split/iterate over text sections
    api_key = "",       # API key
    email = "",         # MyMemory email
    cmd = "translate-cli -o -f auto -t {lang} {text}",  # if cli tool
    default = "GoogleWeb",
    available = [
        "Google Translate",
        "Google Ajax",
        "MyMemory",
        "PONS Web",
        "ArgosTranslate",
        "translate -o {text}",
        "Linguee Dict",
        "PONS Dict",
        "dingonyms --urban {text}",
        "dingonyms --merriam {text}",
        "LibreTranslate ⚿",
        "SysTRAN ⚿",
        "QCRI ⚿",
        "Yandex ⚿",
        "DeepL API ⚿",
        "DeepL Free ⚿",
        "Microsoft ⚿",
    ],
    office = f"TkInter/{tk.TkVersion}"
)
#-- widget structure
layout = [
    # top frame
    [
        sg.Combo(values=conf["available"], default_value=conf["default"], size=(20,1), key="backend", tooltip="Service to use"),
        sg.T("                              "),
        sg.Combo(values=["auto", "en", "es"], default_value="auto", size=(4,1), key="from", tooltip="Source language"),
        sg.T("     "),
        sg.Button("➜ Translate ➜", tooltip="Translate to target language"),
        sg.T("     "),
        sg.Combo(values=["en", "de", "fr", "nl", "es", "it", "pt"], default_value="en", size=(4,1), key="lang", tooltip="Target language"),
        sg.T("                                "),
        sg.Checkbox("␤␍⮒", key="linebreakwise", default=True, tooltip="Use .linebreakwise() translation"),
    ],
    # tabs
    [
        sg.Multiline(size=(55,25), key="orig"),
        sg.Multiline(size=(55,25), key="outp"),
    ]
]


#-- GUI event loop and handlers
class gui_event_handler:

    # prepare window
    def __init__(self):

        #-- build
        gui_event_handler.mainwindow = self
        #sg.theme()
        self.w = sg.Window(
            title=f"pagetranslate", layout=layout, font="Sans 12",
            ttk_theme="clam"
            #size=(1000,525), margins=(0,0), resizable=False, use_custom_titlebar=False,
            #background_color="#fafafa",#,
        )
        self.win_map = {}
        # widget patching per tk
        self.w.read(timeout=1)
        self.w["orig"].set_focus()

    
   # add to *win_map{} event loop
    def win_register(self, win, cb=None):
        if not cb:
            def cb(event, data):
                win.close()
        self.win_map[win] = cb
        win.read(timeout=1)

    # demultiplex PySimpleGUI events across multiple windows
    def main(self):
        self.win_register(self.w, self.event)
        while True:
            win_ls = [win for win in self.win_map.keys()]
            #log.event_loop.win_ls_length.debug(len(win_ls))
            # unlink closed windows
            for win in win_ls:
                if win.TKrootDestroyed:
                    #log.event.debug("destroyed", win)
                    del self.win_map[win]
            # all gone
            if len(win_ls) == 0:
                break
            # if we're just running the main window, then a normal .read() does suffice
            elif len(win_ls) == 1 and win_ls==[self.w]:
                self.event(*self.w.read())
            # poll all windows - sg.read_all_windows() doesn't quite work
            else:
                #win_ls = self.win_map.iteritems()
                for win in win_ls:
                    event, data = win.read(timeout=20)
                    if event and event != "__TIMEOUT__" and self.win_map.get(win):
                        self.win_map[win](event, data)
                    elif event == sg.WIN_CLOSED:
                        win.close()
        sys.exit()

    # mainwindow event dispatcher
    def event(self, raw_event, data):
        if not raw_event:
            return
        # prepare common properties
        data = data or {}
        event = self._case(data.get("menu") or raw_event)
        event = gui_event_handler.map.get(event, event)
        if event.startswith("menu_"): raw_event = data[event] # raw Évéńt name for MenuButtons

        # dispatch
        if event and hasattr(self, event):
            #self.status("")
            getattr(self, event)(data)
            return
        # plugins
        elif mod := None: #self._plugin_has(raw_event)
            mod.show(name=event, raw_event=raw_event, data=data, mainwindow=self, main=self)
        else:
            log.error(f"UNKNOWN EVENT: {event} / {data}")

    # alias/keyboard map
    map = {
        sg.WIN_CLOSED: "exit",
        "none": "exit",  # happens when mainwindow still in destruction process
    }
    
    # Main: translation
    def translate(self, data):
        data.update(conf)
        if re.search(r"\{(|text|lang|from)\}|\s(-\w+)", data["backend"]):
            data.update({
                "cmd": data["backend"],
                "backend": "CLI",
            })
        t = translationbackends.assign_service(data)
        translate = t.linebreakwise if data.get("linebreakwise") else t.translate
        self.w["outp"].update(translate(data["orig"]))

    # File: Exit
    def exit(self, data):
        self.w.close()

    # set mouse pointer ("watch" for planned hangups)
    def _cursor(self, s="arrow"):
        self.w.config(cursor=s)
        self.w.read(timeout=1)
    
    # remove non-alphanumeric characters (for event buttons / tab titles / etc.)
    def _case(self, s):
        return re.sub("\(?\w+\)|\W+|_0x\w+$", "_", str(s)).strip("_").lower()


#-- main
def main():
    gui_event_handler().main()
if __name__ == "__main__":
    main()