Add weblate system

Julien LepillerSun Dec 13 03:10:03+0100 2020

d20223d

Add weblate system

offlate/data/data.json

1-
[{"name": "a2ps", "system": 0}, {"name": "aegis", "system": 0}, {"name": "anubis", "system": 0}, {"name": "aspell", "system": 0}, {"name": "bash", "system": 0}, {"name": "bfd", "system": 0}, {"name": "binutils", "system": 0}, {"name": "bison", "system": 0}, {"name": "bison-runtime", "system": 0}, {"name": "buzztrax", "system": 0}, {"name": "ccd2cue", "system": 0}, {"name": "ccide", "system": 0}, {"name": "cflow", "system": 0}, {"name": "clisp", "system": 0}, {"name": "coreutils", "system": 0}, {"name": "cpio", "system": 0}, {"name": "cppi", "system": 0}, {"name": "cpplib", "system": 0}, {"name": "cryptsetup", "system": 0}, {"name": "datamash", "system": 0}, {"name": "denemo", "system": 0}, {"name": "dfarc", "system": 0}, {"name": "dialog", "system": 0}, {"name": "dico", "system": 0}, {"name": "diffutils", "system": 0}, {"name": "dink", "system": 0}, {"name": "direvent", "system": 0}, {"name": "doodle", "system": 0}, {"name": "dos2unix", "system": 0}, {"name": "dos2unix-man", "system": 0}, {"name": "e2fsprogs", "system": 0}, {"name": "enscript", "system": 0}, {"name": "exif", "system": 0}, {"name": "fetchmail", "system": 0}, {"name": "findutils", "system": 0}, {"name": "flex", "system": 0}, {"name": "freedink", "system": 0}, {"name": "fusionforge", "system": 0}, {"name": "gas", "system": 0}, {"name": "gawk", "system": 0}, {"name": "gcal", "system": 0}, {"name": "gcc", "system": 0}, {"name": "gdbm", "system": 0}, {"name": "gettext-examples", "system": 0}, {"name": "gettext-runtime", "system": 0}, {"name": "gettext-tools", "system": 0}, {"name": "gjay", "system": 0}, {"name": "glunarclock", "system": 0}, {"name": "gnubiff", "system": 0}, {"name": "gnubik", "system": 0}, {"name": "gnucash", "system": 0}, {"name": "gnuchess", "system": 0}, {"name": "gnucobol", "system": 0}, {"name": "gnulib", "system": 0}, {"name": "gnunet", "system": 0}, {"name": "gnunet-gtk", "system": 0}, {"name": "gold", "system": 0}, {"name": "gphoto2", "system": 0}, {"name": "gprof", "system": 0}, {"name": "gramadoir", "system": 0}, {"name": "grep", "system": 0}, {"name": "grip", "system": 0}, {"name": "grub", "system": 0}, {"name": "gsasl", "system": 0}, {"name": "gss", "system": 0}, {"name": "gst-plugins-bad", "system": 0}, {"name": "gst-plugins-base", "system": 0}, {"name": "gst-plugins-good", "system": 0}, {"name": "gst-plugins-ugly", "system": 0}, {"name": "gstreamer", "system": 0}, {"name": "gtick", "system": 0}, {"name": "gtkam", "system": 0}, {"name": "gtkspell", "system": 0}, {"name": "guix", "system": 0}, {"name": "guix-manual", "system": 0}, {"name": "guix-packages", "system": 0}, {"name": "gutenprint", "system": 0}, {"name": "hello", "system": 0}, {"name": "help2man", "system": 0}, {"name": "help2man-texi", "system": 0}, {"name": "hylafax", "system": 0}, {"name": "idutils", "system": 0}, {"name": "jwhois", "system": 0}, {"name": "kbd", "system": 0}, {"name": "klavaro", "system": 0}, {"name": "ld", "system": 0}, {"name": "leafpad", "system": 0}, {"name": "libc", "system": 0}, {"name": "libexif", "system": 0}, {"name": "libextractor", "system": 0}, {"name": "libgnutls", "system": 0}, {"name": "libgphoto2", "system": 0}, {"name": "libgphoto2_port", "system": 0}, {"name": "libgsasl", "system": 0}, {"name": "libiconv", "system": 0}, {"name": "libidn", "system": 0}, {"name": "libidn2", "system": 0}, {"name": "lilypond", "system": 0}, {"name": "lordsawar", "system": 0}, {"name": "lprng", "system": 0}, {"name": "lynx", "system": 0}, {"name": "m4", "system": 0}, {"name": "mailfromd", "system": 0}, {"name": "mailutils", "system": 0}, {"name": "make", "system": 0}, {"name": "man-db", "system": 0}, {"name": "man-db-manpages", "system": 0}, {"name": "midi-instruments", "system": 0}, {"name": "minicom", "system": 0}, {"name": "mkisofs", "system": 0}, {"name": "muibase", "system": 0}, {"name": "myserver", "system": 0}, {"name": "nano", "system": 0}, {"name": "opcodes", "system": 0}, {"name": "parted", "system": 0}, {"name": "pies", "system": 0}, {"name": "pnmixer", "system": 0}, {"name": "popt", "system": 0}, {"name": "procps-ng", "system": 0}, {"name": "procps-ng-man", "system": 0}, {"name": "psmisc", "system": 0}, {"name": "pspp", "system": 0}, {"name": "pushover", "system": 0}, {"name": "pwdutils", "system": 0}, {"name": "pyspread", "system": 0}, {"name": "radius", "system": 0}, {"name": "recode", "system": 0}, {"name": "recutils", "system": 0}, {"name": "rpm", "system": 0}, {"name": "rush", "system": 0}, {"name": "sarg", "system": 0}, {"name": "savane", "system": 0}, {"name": "sed", "system": 0}, {"name": "sharutils", "system": 0}, {"name": "shepherd", "system": 0}, {"name": "shishi", "system": 0}, {"name": "skribilo", "system": 0}, {"name": "solfege", "system": 0}, {"name": "solfege-manual", "system": 0}, {"name": "spotmachine", "system": 0}, {"name": "sudo", "system": 0}, {"name": "sudoers", "system": 0}, {"name": "sysstat", "system": 0}, {"name": "tar", "system": 0}, {"name": "texinfo", "system": 0}, {"name": "texinfo_document", "system": 0}, {"name": "tigervnc", "system": 0}, {"name": "tin", "system": 0}, {"name": "tin-man", "system": 0}, {"name": "tracgoogleappsauthplugin", "system": 0}, {"name": "trader", "system": 0}, {"name": "util-linux", "system": 0}, {"name": "ve", "system": 0}, {"name": "vmm", "system": 0}, {"name": "vorbis-tools", "system": 0}, {"name": "wastesedge", "system": 0}, {"name": "wcd", "system": 0}, {"name": "wcd-man", "system": 0}, {"name": "wdiff", "system": 0}, {"name": "wget", "system": 0}, {"name": "wyslij-po", "system": 0}, {"name": "xboard", "system": 0}, {"name": "xdg-user-dirs", "system": 0}, {"name": "xkeyboard-config", "system": 0}, {"name": "id-editor", "system": 1, "organization": "openstreetmap", "project": "id-editor"}, {"name": "offlate", "system": 2, "repo": "https://framagit.org/tyreunom/offlate", "branch": "master"}]
1+
[{"name": "a2ps", "system": 0}, {"name": "aegis", "system": 0}, {"name": "anubis", "system": 0}, {"name": "aspell", "system": 0}, {"name": "bash", "system": 0}, {"name": "bfd", "system": 0}, {"name": "binutils", "system": 0}, {"name": "bison", "system": 0}, {"name": "bison-runtime", "system": 0}, {"name": "buzztrax", "system": 0}, {"name": "ccd2cue", "system": 0}, {"name": "ccide", "system": 0}, {"name": "cflow", "system": 0}, {"name": "clisp", "system": 0}, {"name": "coreutils", "system": 0}, {"name": "cpio", "system": 0}, {"name": "cppi", "system": 0}, {"name": "cpplib", "system": 0}, {"name": "cryptsetup", "system": 0}, {"name": "datamash", "system": 0}, {"name": "denemo", "system": 0}, {"name": "dfarc", "system": 0}, {"name": "dialog", "system": 0}, {"name": "dico", "system": 0}, {"name": "diffutils", "system": 0}, {"name": "dink", "system": 0}, {"name": "direvent", "system": 0}, {"name": "doodle", "system": 0}, {"name": "dos2unix", "system": 0}, {"name": "dos2unix-man", "system": 0}, {"name": "e2fsprogs", "system": 0}, {"name": "enscript", "system": 0}, {"name": "exif", "system": 0}, {"name": "fetchmail", "system": 0}, {"name": "findutils", "system": 0}, {"name": "flex", "system": 0}, {"name": "freedink", "system": 0}, {"name": "fusionforge", "system": 0}, {"name": "gas", "system": 0}, {"name": "gawk", "system": 0}, {"name": "gcal", "system": 0}, {"name": "gcc", "system": 0}, {"name": "gdbm", "system": 0}, {"name": "gettext-examples", "system": 0}, {"name": "gettext-runtime", "system": 0}, {"name": "gettext-tools", "system": 0}, {"name": "gjay", "system": 0}, {"name": "glunarclock", "system": 0}, {"name": "gnubiff", "system": 0}, {"name": "gnubik", "system": 0}, {"name": "gnucash", "system": 0}, {"name": "gnuchess", "system": 0}, {"name": "gnucobol", "system": 0}, {"name": "gnulib", "system": 0}, {"name": "gnunet", "system": 0}, {"name": "gnunet-gtk", "system": 0}, {"name": "gold", "system": 0}, {"name": "gphoto2", "system": 0}, {"name": "gprof", "system": 0}, {"name": "gramadoir", "system": 0}, {"name": "grep", "system": 0}, {"name": "grip", "system": 0}, {"name": "grub", "system": 0}, {"name": "gsasl", "system": 0}, {"name": "gss", "system": 0}, {"name": "gst-plugins-bad", "system": 0}, {"name": "gst-plugins-base", "system": 0}, {"name": "gst-plugins-good", "system": 0}, {"name": "gst-plugins-ugly", "system": 0}, {"name": "gstreamer", "system": 0}, {"name": "gtick", "system": 0}, {"name": "gtkam", "system": 0}, {"name": "gtkspell", "system": 0}, {"name": "guix", "system": 0}, {"name": "guix-manual", "system": 0}, {"name": "guix-packages", "system": 0}, {"name": "gutenprint", "system": 0}, {"name": "hello", "system": 0}, {"name": "help2man", "system": 0}, {"name": "help2man-texi", "system": 0}, {"name": "hylafax", "system": 0}, {"name": "idutils", "system": 0}, {"name": "jwhois", "system": 0}, {"name": "kbd", "system": 0}, {"name": "klavaro", "system": 0}, {"name": "ld", "system": 0}, {"name": "leafpad", "system": 0}, {"name": "libc", "system": 0}, {"name": "libexif", "system": 0}, {"name": "libextractor", "system": 0}, {"name": "libgnutls", "system": 0}, {"name": "libgphoto2", "system": 0}, {"name": "libgphoto2_port", "system": 0}, {"name": "libgsasl", "system": 0}, {"name": "libiconv", "system": 0}, {"name": "libidn", "system": 0}, {"name": "libidn2", "system": 0}, {"name": "lilypond", "system": 0}, {"name": "lordsawar", "system": 0}, {"name": "lprng", "system": 0}, {"name": "lynx", "system": 0}, {"name": "m4", "system": 0}, {"name": "mailfromd", "system": 0}, {"name": "mailutils", "system": 0}, {"name": "make", "system": 0}, {"name": "man-db", "system": 0}, {"name": "man-db-manpages", "system": 0}, {"name": "midi-instruments", "system": 0}, {"name": "minicom", "system": 0}, {"name": "mkisofs", "system": 0}, {"name": "muibase", "system": 0}, {"name": "myserver", "system": 0}, {"name": "nano", "system": 0}, {"name": "opcodes", "system": 0}, {"name": "parted", "system": 0}, {"name": "pies", "system": 0}, {"name": "pnmixer", "system": 0}, {"name": "popt", "system": 0}, {"name": "procps-ng", "system": 0}, {"name": "procps-ng-man", "system": 0}, {"name": "psmisc", "system": 0}, {"name": "pspp", "system": 0}, {"name": "pushover", "system": 0}, {"name": "pwdutils", "system": 0}, {"name": "pyspread", "system": 0}, {"name": "radius", "system": 0}, {"name": "recode", "system": 0}, {"name": "recutils", "system": 0}, {"name": "rpm", "system": 0}, {"name": "rush", "system": 0}, {"name": "sarg", "system": 0}, {"name": "savane", "system": 0}, {"name": "sed", "system": 0}, {"name": "sharutils", "system": 0}, {"name": "shepherd", "system": 0}, {"name": "shishi", "system": 0}, {"name": "skribilo", "system": 0}, {"name": "solfege", "system": 0}, {"name": "solfege-manual", "system": 0}, {"name": "spotmachine", "system": 0}, {"name": "sudo", "system": 0}, {"name": "sudoers", "system": 0}, {"name": "sysstat", "system": 0}, {"name": "tar", "system": 0}, {"name": "texinfo", "system": 0}, {"name": "texinfo_document", "system": 0}, {"name": "tigervnc", "system": 0}, {"name": "tin", "system": 0}, {"name": "tin-man", "system": 0}, {"name": "tracgoogleappsauthplugin", "system": 0}, {"name": "trader", "system": 0}, {"name": "util-linux", "system": 0}, {"name": "ve", "system": 0}, {"name": "vmm", "system": 0}, {"name": "vorbis-tools", "system": 0}, {"name": "wastesedge", "system": 0}, {"name": "wcd", "system": 0}, {"name": "wcd-man", "system": 0}, {"name": "wdiff", "system": 0}, {"name": "wget", "system": 0}, {"name": "wyslij-po", "system": 0}, {"name": "xboard", "system": 0}, {"name": "xdg-user-dirs", "system": 0}, {"name": "xkeyboard-config", "system": 0}, {"name": "id-editor", "system": 1, "organization": "openstreetmap", "project": "id-editor"}, {"name": "offlate", "system": 2, "repo": "https://framagit.org/tyreunom/offlate", "branch": "master"}, {"name": "guix-weblate", "system": 4, "instance": "https://translate.fedoraproject.org", "project": "guix"}]

offlate/systems/list.py

1919
from .github import GithubProject
2020
from .tp import TPProject
2121
from .transifex import TransifexProject
22+
from .weblate import WeblateProject
2223
2324
systems = [
2425
  {'name': 'Translation Project',

3334
  {'name': 'Github',
3435
   'system': GithubProject,
3536
   'key': 'Github'},
37+
  {'name': 'Weblate',
38+
   'system': WeblateProject,
39+
   'key': 'Weblate'}
3640
]

offlate/systems/weblate.py unknown status 1

1+
#   Copyright (c) 2018 Julien Lepiller <julien@lepiller.eu>
2+
#
3+
#   This program is free software: you can redistribute it and/or modify
4+
#   it under the terms of the GNU Affero General Public License as
5+
#   published by the Free Software Foundation, either version 3 of the
6+
#   License, or (at your option) any later version.
7+
#
8+
#   This program is distributed in the hope that it will be useful,
9+
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11+
#   GNU Affero General Public License for more details.
12+
#
13+
#   You should have received a copy of the GNU Affero General Public License
14+
#   along with this program.  If not, see <https://www.gnu.org/licenses/>.
15+
####
16+
""" The transifex system connector. """
17+
18+
import json
19+
import os
20+
import requests
21+
from requests.auth import HTTPBasicAuth
22+
23+
from ..core.config import *
24+
from ..formats.gettext import GettextFormat
25+
from ..formats.exception import UnsupportedFormatException
26+
from .exception import ProjectNotFoundSystemException
27+
from .project import Project
28+
29+
class WeblateProject(Project):
30+
    def __init__(self, name, lang, conf, data={}):
31+
        Project.__init__(self, name, lang, conf, data)
32+
        for server in self.conf['servers']:
33+
            if server['server'] == data['instance']:
34+
                self.conf['token'] = server['token']
35+
        self.basedir = ''
36+
        self.contents = {}
37+
38+
    def open(self, basedir):
39+
        self.basedir = basedir
40+
        with open(self.basedir + '/project.info') as f:
41+
            self.files = json.load(f)
42+
43+
    def initialize(self, basedir, callback=None):
44+
        self.basedir = basedir
45+
        self.updateFileList()
46+
        with open(self.basedir + '/project.info', 'w') as f:
47+
            f.write(json.dumps(self.files))
48+
        i = 0
49+
        for slug in self.files:
50+
            callback.reportProgress(100.0 * i / len(self.files))
51+
            self.getFiles(slug['slug'])
52+
            i += 1
53+
        callback.reportProgress(100)
54+
55+
    def updateFileList(self):
56+
        self.files = []
57+
        ans = requests.get(self.data['instance'] + '/api/projects/' +
58+
                self.data['project'] + '/components',
59+
                auth=HTTPBasicAuth('Token', self.conf['token']))
60+
        if ans.status_code == 200:
61+
            l = json.loads(ans.text)['results']
62+
            self.files = l
63+
        else:
64+
            raise ProjectNotFoundSystemException(self.name)
65+
66+
    def update(self, askmerge, callback=None):
67+
        self.updateFileList()
68+
        i = 0
69+
        for ff in self.files:
70+
            callback.reportProgress(100.0 * i / len(self.files))
71+
            slug = ff['slug']
72+
            fname = self.filename(slug, False)
73+
            sname = self.filename(slug, True)
74+
            os.rename(fname, fname+'.old')
75+
            os.rename(sname, sname+'.old')
76+
            self.getFiles(slug)
77+
            if ff['file_format'] == 'po':
78+
                oldformat = GettextFormat({'file': fname + '.old',
79+
                    'pot': sname + '.old',
80+
                    'version': self.conf['offlate_version'],
81+
                    'fullname': self.conf['name'] + ' <' + self.conf['email'] + '>',
82+
                    'lang': self.lang})
83+
                currentformat = GettextFormat({'file': fname,
84+
                    'pot': sname,
85+
                    'version': self.conf['offlate_version'],
86+
                    'fullname': self.conf['name'] + ' <' + self.conf['email'] + '>',
87+
                    'lang': self.lang})
88+
            else:
89+
                raise UnsupportedFormatException(ff['file_format'])
90+
            currentformat.merge(oldformat, askmerge)
91+
            i += 1
92+
        callback.reportProgress(100)
93+
94+
    def filename(self, slug, is_source):
95+
        ext = ''
96+
        f = None
97+
        for ff in self.files:
98+
            if ff['slug'] == slug:
99+
                f = ff
100+
                break
101+
        if f['file_format'] == 'po':
102+
            ext = 'po'
103+
        else:
104+
            raise UnsupportedFormatException(ff['file_format'])
105+
        return self.basedir + '/' + slug + ('.source' if is_source else '') + '.' + ext
106+
107+
    def getFiles(self, slug):
108+
        ans = requests.get(self.data['instance'] + '/api/components/' +
109+
                self.data['project'] + '/' + slug + '/',
110+
                auth=HTTPBasicAuth('Token', self.conf['token']))
111+
        source_lang='en'
112+
        if ans.status_code == 200:
113+
            l = json.loads(ans.text)
114+
            source_lang = l['source_language']['code']
115+
116+
        ans = requests.get(self.data['instance'] + '/api/translations/' +
117+
                self.data['project'] + '/' + slug + '/' + self.lang + '/file',
118+
                auth=HTTPBasicAuth('Token', self.conf['token']))
119+
        if ans.status_code == 200:
120+
            with open(self.filename(slug, False), 'wb') as f:
121+
                f.write(ans.text.encode(ans.encoding))
122+
123+
        ans = requests.get(self.data['instance'] + '/api/translations/' +
124+
                self.data['project'] + '/' + slug + '/' + source_lang + '/file',
125+
                auth=HTTPBasicAuth('Token', self.conf['token']))
126+
        if ans.status_code == 200:
127+
            with open(self.filename(slug, True), 'wb') as f:
128+
                f.write(ans.text.encode(ans.encoding))
129+
        else:
130+
            print(ans.text)
131+
132+
    def send(self, callback):
133+
        self.save()
134+
        i = 0
135+
        for slug in self.files:
136+
            callback.reportProgress(100.0 * i / len(self.files))
137+
            print('{} => {}'.format(slug['slug'], slug['file_format']))
138+
            filename = self.filename(slug['slug'], False)
139+
            with open(filename, 'rb') as f:
140+
                ans = requests.get(self.data['instance'] + '/api/translations/' +
141+
                        self.data['project'] + '/' + slug['slug'] + '/' +
142+
                        self.lang + '/file',
143+
                        files={'file': f},
144+
                        auth=HTTPBasicAuth('Token', self.conf['token']))
145+
                print(ans)
146+
                print(ans.text)
147+
            i += 1
148+
        callback.reportProgress(100)
149+
150+
    def save(self):
151+
        for slug in self.slugs:
152+
            slug.save()
153+
154+
    def content(self):
155+
        content = {}
156+
        self.slugs = []
157+
        for slug in self.files:
158+
            fname = self.filename(slug['slug'], False)
159+
            sname = self.filename(slug['slug'], True)
160+
            if slug['file_format'] == 'po':
161+
                myslug = GettextFormat({'file': fname,
162+
                    'pot': sname,
163+
                    'version': self.conf['offlate_version'],
164+
                    'fullname': self.conf['name'] + ' <' + self.conf['email'] + '>',
165+
                    'lang': self.lang})
166+
            else:
167+
                raise UnsupportedFormatException(ff['file_format'])
168+
            self.slugs.append(myslug)
169+
            content[slug['slug']] = myslug.content()
170+
        return content
171+
172+
    def getExternalFiles(self):
173+
        return [x.getExternalFiles() for x in self.slugs]
174+
175+
    def reload(self):
176+
        for x in self.slugs:
177+
            x.reload()
178+
179+
    @staticmethod
180+
    def getProjectConfigSpec():
181+
        return [StringConfigSpec('instance', Project.tr('Instance'),
182+
            Project.tr('Full clone URL of the Weblate instance'),
183+
            Project.tr('https://weblate.org')),
184+
                StringConfigSpec('project', Project.tr('Project'),
185+
                    Project.tr('Name of the project on the instance'),
186+
                    Project.tr('foo'))]
187+
188+
    @staticmethod
189+
    def getSystemConfigSpec():
190+
        return [ListConfigSpec('servers', Project.tr('Configured Weblate instances'),
191+
                    Project.tr('You need to find a token for each Weblate instance \
192+
you have an account on. You can create a token by logging into your account, \
193+
going to your settings and in the API Access page.'),
194+
                    [
195+
                    StringConfigSpec('server', Project.tr('Server'),
196+
                        Project.tr('Server name'),
197+
                        placeholder = Project.tr('https://weblate.org')),
198+
                    StringConfigSpec('token', Project.tr('Token'),
199+
                        Project.tr('The token you created from your account'),
200+
                        placeholder = Project.tr('fKbStkBgFzIL0UW15sfcJh7kC0BAbcVtV16kblXlM'))
201+
                    ],
202+
                    'server',
203+
                    WeblateProject.hasRequiredRow)]
204+
        return specs
205+
206+
    @staticmethod
207+
    def hasRequiredRow(conf, servers):
208+
        """
209+
        Method used by the configuration system: it checks that the configuration
210+
        refers to a server whose configuration is valid.
211+
        """
212+
        server = conf['instance']
213+
        
214+
        for serv in servers:
215+
            if serv["server"] == server:
216+
                return True
217+
        return False