Viewing file: BuildL10n.py (6.07 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
import os, glob, struct, array from distutils import dep_util from distutils.core import Command from distutils.errors import DistutilsFileError
class BuildL10n(Command):
command_name = 'build_l10n'
description = "compile message catalog to binary format"
user_options = [ ('build-dir=', 'd', "directory to \"build\" (copy) to"), ('force', 'f', "forcibly build everything (ignore file timestamps"), ]
def initialize_options(self): self.build_dir = None self.force = None return
def finalize_options(self): self.set_undefined_options('build', ('build_l10n', 'build_dir'), ('force', 'force')) return def run(self): domain = self.distribution.get_name() for loc in self.distribution.l10n: # Create the build directory for this language build_dir = os.path.join(self.build_dir, loc.language, 'LC_MESSAGES') self.mkpath(build_dir) pofile = loc.source mofile = os.path.join(build_dir, domain + '.mo')
if not (self.force or dep_util.newer(pofile, mofile)): self.announce("not compiling %s (up-to-date)" % pofile, 1) continue self.announce("compiling %s -> %s" % (pofile, mofile), 2)
# Parse the catalog msgfmt = MsgFmt() msgfmt.parse(pofile)
# Compute output if not self.dry_run: fp = open(mofile, "wb") try: msgfmt.generate(fp) finally: fp.close() return
# -- external interfaces ------------------------------------------
def get_outputs(self): outputs = [] domain = self.distribution.get_name() for loc in self.distribution.l10n: # Create the build directory for this language build_dir = os.path.join(self.build_dir, loc.language, 'LC_MESSAGES') mofile = os.path.join(build_dir, domain + '.mo') outputs.append(mofile) return outputs
def get_source_files(self): sources = [ loc.source for loc in self.l10n ] return sources
class MsgFmt:
def __init__(self): self.__messages = {} return
def add(self, id, str, fuzzy): "Add a non-fuzzy translation to the dictionary." if not fuzzy and str: self.__messages[id] = str
def generate(self, fp): "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. msgstr = self.__messages[id] offsets.append((len(ids), len(id), len(strs), len(msgstr))) ids += id + '\0' strs += msgstr + '\0'
# 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 fp.write(struct.pack("Iiiiiii", 0x950412deL, # 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 fp.write(array.array("i", offsets).tostring()) fp.write(ids) fp.write(strs) return
def parse(self, pofile): ID = 1 STR = 2
try: lines = open(pofile).readlines() except IOError, (errno, errstr): raise DistutilsFileError("could not read from '%s': %s" % ( pofile, errstr)) section = None fuzzy = 0
# Parse the catalog lno = 0 for l in lines: lno += 1 # If we get a comment line after a msgstr, this is a new entry if l[0] == '#' and section == STR: self.add(msgid, msgstr, fuzzy) section = None fuzzy = 0 # Record a fuzzy mark if l[:2] == '#,' and l.find('fuzzy'): fuzzy = 1 # Skip comments if l[0] == '#': continue # Now we are in a msgid section, output previous section if l.startswith('msgid'): if section == STR: self.add(msgid, msgstr, fuzzy) 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? l = eval(l) if section == ID: msgid += l elif section == STR: msgstr += l else: self.warn('Syntax error on %s:%d before: %s ' % ( pofile, lno, l)) return
# Add last entry if section == STR: self.add(msgid, msgstr, fuzzy) return
|