#!/usr/bin/env python3 """Compiles every languages/*.po into a binary .mo (no gettext tools required). Usage: python3 bin/compile-mo.py """ import ast import glob import os import struct ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) def parse_po(path): messages = {} ctx = msgid = msgstr = None plural = None plurals = {} section = None def flush(): if msgid is None: return key = msgid if ctx is None else ctx + "\x04" + msgid if plural is not None: key = key + "\x00" + plural value = "\x00".join(plurals[i] for i in sorted(plurals)) else: value = msgstr or "" if msgid == "" or value: messages[key] = value for raw in open(path, encoding="utf-8"): line = raw.strip() if not line or line.startswith("#"): continue if line.startswith("msgctxt "): flush() ctx, msgid, msgstr, plural, plurals = ast.literal_eval(line[8:]), None, None, None, {} section = "ctx" elif line.startswith("msgid_plural "): plural = ast.literal_eval(line[13:]) section = "plural" elif line.startswith("msgid "): if section != "ctx": flush() ctx, msgstr, plural, plurals = None, None, None, {} msgid = ast.literal_eval(line[6:]) section = "id" elif line.startswith("msgstr["): idx = int(line[7:line.index("]")]) plurals[idx] = ast.literal_eval(line[line.index("]") + 1:].strip()) section = ("pl", idx) elif line.startswith("msgstr "): msgstr = ast.literal_eval(line[7:]) section = "str" elif line.startswith('"'): chunk = ast.literal_eval(line) if section == "ctx": ctx += chunk elif section == "id": msgid += chunk elif section == "plural": plural += chunk elif section == "str": msgstr += chunk elif isinstance(section, tuple): plurals[section[1]] += chunk flush() return messages def write_mo(messages, path): keys = sorted(messages) ids = b"" strs = b"" offsets = [] for k in keys: kb = k.encode("utf-8") vb = messages[k].encode("utf-8") offsets.append((len(ids), len(kb), len(strs), len(vb))) ids += kb + b"\x00" strs += vb + b"\x00" n = len(keys) keystart = 7 * 4 + 16 * n valuestart = keystart + len(ids) koffsets = [] voffsets = [] for o1, l1, o2, l2 in offsets: koffsets += [l1, o1 + keystart] voffsets += [l2, o2 + valuestart] output = struct.pack("Iiiiiii", 0x950412DE, 0, n, 7 * 4, 7 * 4 + n * 8, 0, 0) output += struct.pack("%di" % len(koffsets), *koffsets) output += struct.pack("%di" % len(voffsets), *voffsets) output += ids + strs open(path, "wb").write(output) for po in sorted(glob.glob(os.path.join(ROOT, "languages", "*.po"))): mo = po[:-3] + ".mo" messages = parse_po(po) write_mo(messages, mo) print("Compiled %s (%d entries)" % (os.path.relpath(mo, ROOT), len(messages) - 1))