diff --git a/.github/workflows/install.yml b/.github/workflows/install.yml index 0a86ddf..4c1f0ad 100644 --- a/.github/workflows/install.yml +++ b/.github/workflows/install.yml @@ -40,5 +40,6 @@ jobs: ${{ matrix.os }}-${{ matrix.python-version }}-v1- - name: Install dependencies run: | + sudo apt-get install gettext python -m pip install -U pip python -m pip install -e . diff --git a/formalchemy/msgfmt.py b/formalchemy/msgfmt.py deleted file mode 100755 index 7756d88..0000000 --- a/formalchemy/msgfmt.py +++ /dev/null @@ -1,182 +0,0 @@ -#! /usr/bin/env python -# -*- coding: iso-8859-1 -*- -# Written by Martin v. Loewis -# -# Changed by Christian 'Tiran' Heimes for the placeless -# translation service (PTS) of zope -# -# Slightly updated by Hanno Schlichting -# -# Included by Ingeniweb from PlacelessTranslationService 1.4.8 - -"""Generate binary message catalog from textual translation description. - -This program converts a textual Uniforum-style message catalog (.po file) into -a binary GNU catalog (.mo file). This is essentially the same function as the -GNU msgfmt program, however, it is a simpler implementation. - -This file was taken from Python-2.3.2/Tools/i18n and altered in several ways. -Now you can simply use it from another python module: - - from msgfmt import Msgfmt - mo = Msgfmt(po).get() - -where po is path to a po file as string, an opened po file ready for reading or -a list of strings (readlines of a po file) and mo is the compiled mo -file as binary string. - -Exceptions: - - * IOError if the file couldn't be read - - * msgfmt.PoSyntaxError if the po file has syntax errors - -""" -import struct -import array -import types -from cStringIO import StringIO - -__version__ = "1.1pts" - -class PoSyntaxError(Exception): - """ Syntax error in a po file """ - def __init__(self, msg): - self.msg = msg - - def __str__(self): - return 'Po file syntax error: %s' % self.msg - -class Msgfmt: - """ """ - def __init__(self, po, name='unknown'): - self.po = po - self.name = name - self.messages = {} - - def readPoData(self): - """ read po data from self.po and store it in self.poLines """ - output = [] - if isinstance(self.po, types.FileType): - self.po.seek(0) - output = self.po.readlines() - if isinstance(self.po, list): - output = self.po - if isinstance(self.po, str): - output = open(self.po, 'rb').readlines() - if not output: - raise ValueError("self.po is invalid! %s" % type(self.po)) - return output - - def add(self, id, str, fuzzy): - "Add a non-empty and non-fuzzy translation to the dictionary." - if str and not fuzzy: - self.messages[id] = str - - def generate(self): - "Return the generated output." - keys = self.messages.keys() - # the keys are sorted in the .mo file - keys.sort() - offsets = [] - ids = strs = '' - for id in keys: - # For each string, we need size and file offset. Each string is NUL - # terminated; the NUL does not count into the size. - offsets.append((len(ids), len(id), len(strs), len(self.messages[id]))) - ids += id + '\0' - strs += self.messages[id] + '\0' - output = '' - # The header is 7 32-bit unsigned integers. We don't use hash tables, so - # the keys start right after the index tables. - # translated string. - keystart = 7*4+16*len(keys) - # and the values start after the keys - valuestart = keystart + len(ids) - koffsets = [] - voffsets = [] - # The string table first has the list of keys, then the list of values. - # Each entry has first the size of the string, then the file offset. - for o1, l1, o2, l2 in offsets: - koffsets += [l1, o1+keystart] - voffsets += [l2, o2+valuestart] - offsets = koffsets + voffsets - output = struct.pack("Iiiiiii", - 0x950412de, # Magic - 0, # Version - len(keys), # # of entries - 7*4, # start of key index - 7*4+len(keys)*8, # start of value index - 0, 0) # size and offset of hash table - output += array.array("i", offsets).tostring() - output += ids - output += strs - return output - - - def get(self): - """ """ - ID = 1 - STR = 2 - - section = None - fuzzy = 0 - - lines = self.readPoData() - - # Parse the catalog - lno = 0 - for l in lines: - lno += 1 - # If we get a comment line after a msgstr or a line starting with - # msgid, this is a new entry - # XXX: l.startswith('msgid') is needed because not all msgid/msgstr - # pairs in the plone pos have a leading comment - if (l[0] == '#' or l.startswith('msgid')) and section == STR: - self.add(msgid, msgstr, fuzzy) - section = None - fuzzy = 0 - # Record a fuzzy mark - if l[:2] == '#,' and 'fuzzy' in l: - fuzzy = 1 - # Skip comments - if l[0] == '#': - continue - # Now we are in a msgid section, output previous section - if l.startswith('msgid'): - section = ID - l = l[5:] - msgid = msgstr = '' - # Now we are in a msgstr section - elif l.startswith('msgstr'): - section = STR - l = l[6:] - # Skip empty lines - l = l.strip() - if not l: - continue - # XXX: Does this always follow Python escape semantics? - # XXX: eval is evil because it could be abused - try: - l = eval(l, globals()) - except Exception as msg: - raise PoSyntaxError('%s (line %d of po file %s): \n%s' % (msg, lno, self.name, l)) - if section == ID: - msgid += l - elif section == STR: - msgstr += l - else: - raise PoSyntaxError('error in line %d of po file %s' % (lno, self.name)) - - # Add last entry - if section == STR: - self.add(msgid, msgstr, fuzzy) - - # Compute output - return self.generate() - - def getAsFile(self): - return StringIO(self.get()) - - def __call__(self): - return self.getAsFile() diff --git a/setup.py b/setup.py index ffb0f7f..918e0fc 100644 --- a/setup.py +++ b/setup.py @@ -4,6 +4,9 @@ from os.path import join import sys import os +import glob +import pathlib +import subprocess def get_version(fname='formalchemy/__init__.py'): with open(fname) as f: @@ -11,28 +14,6 @@ def get_version(fname='formalchemy/__init__.py'): if line.startswith('__version__'): return eval(line.split('=')[-1]) -try: - from msgfmt import Msgfmt -except: - sys.path.insert(0, join(os.getcwd(), 'formalchemy')) - -def compile_po(path): - from msgfmt import Msgfmt - for language in os.listdir(path): - lc_path = join(path, language, 'LC_MESSAGES') - if os.path.isdir(lc_path): - for domain_file in os.listdir(lc_path): - if domain_file.endswith('.po'): - file_path = join(lc_path, domain_file) - mo_file = join(lc_path, '%s.mo' % domain_file[:-3]) - mo_content = Msgfmt(file_path, name=file_path).get() - mo = open(mo_file, 'wb') - mo.write(mo_content) - mo.close() - -# We compile .mo files during setup. Don't think, it is a good idea. [sallner] -compile_po(join(os.getcwd(), 'formalchemy', 'i18n_resources')) - def read(filename): text = open(filename,'r').read() return xml.sax.saxutils.escape(text) @@ -44,6 +25,20 @@ def read(filename): '\n\n' +\ read('CHANGELOG.rst') +PO_FILES = 'i18n_resources/*/LC_MESSAGES/formalchemy.po' + +def create_mo_files(): + mo_files = [] + prefix = 'formalchemy' + + for po_path in glob.glob(str(pathlib.Path(prefix) / PO_FILES)): + mo = pathlib.Path(po_path.replace('.po', '.mo')) + + subprocess.run(['msgfmt', '-o', str(mo), po_path], check=True) + mo_files.append(str(mo.relative_to(prefix))) + + return mo_files + setup(name='FormAlchemy', license='MIT License', version=get_version(), @@ -58,7 +53,7 @@ def read(filename): 'ext/pylons/*.mako', 'ext/pylons/resources/*.css', 'ext/pylons/resources/*.png', 'tests/data/mako/*.mako', 'tests/data/genshi/*.html', 'paster_templates/pylons_fa/+package+/*/*_tmpl', - ]}, + ] + create_mo_files()}, include_package_data=True, classifiers=[ 'Development Status :: 5 - Production/Stable',