You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
201 lines
6.1 KiB
Python
201 lines
6.1 KiB
Python
#!/usr/bin/env python3
|
|
|
|
import os
|
|
import sys
|
|
|
|
|
|
def die(msg):
|
|
print("ERROR:", msg)
|
|
sys.exit(1)
|
|
|
|
|
|
class LmoEntry:
|
|
def __init__(self, key_id = 0, plural = 0, offset = 0, length = 0, val = None):
|
|
self.key_id = key_id
|
|
self.plural = plural
|
|
self.offset = offset
|
|
self.length = length
|
|
self.val = val
|
|
self.dup = 0
|
|
|
|
|
|
class Lmo:
|
|
options = ""
|
|
entries = [] # list of LmoEntry
|
|
|
|
def __init__(self):
|
|
self.options = ""
|
|
self.entries = []
|
|
|
|
def load_from_bin(self, filename):
|
|
self.entries = []
|
|
use_plural_num = False
|
|
with open(filename, "rb") as file:
|
|
data = file.read()
|
|
table_offset = int.from_bytes(data[-4:], byteorder='big')
|
|
#print("table_offset = 0x%X" % table_offset)
|
|
off = table_offset
|
|
while True:
|
|
if off + 16 >= len(data):
|
|
break
|
|
entry = LmoEntry()
|
|
entry.key_id = int.from_bytes(data[off :off+4] , byteorder='big')
|
|
entry.plural = int.from_bytes(data[off+4 :off+8] , byteorder='big') - 1
|
|
entry.offset = int.from_bytes(data[off+8 :off+12], byteorder='big')
|
|
entry.length = int.from_bytes(data[off+12:off+16], byteorder='big')
|
|
entry.val = data[entry.offset:entry.offset+entry.length]
|
|
#print("%08X %d %08X %d" % (entry.key_id, entry.plural, entry.offset, entry.length))
|
|
if off == table_offset:
|
|
if entry.key_id == 0 and entry.plural == -1:
|
|
use_plural_num = True
|
|
if use_plural_num:
|
|
if entry.plural >= 10 or (off != table_offset and entry.plural < 0):
|
|
die("Too many plural forms")
|
|
else:
|
|
entry.plural = 0 # older version LMO-files contain hash of value
|
|
self.entries.append(entry)
|
|
off += 16
|
|
self.entries = sorted(self.entries, key=lambda x: x.offset)
|
|
#self.dup_search()
|
|
|
|
def dup_search(self):
|
|
count = 0
|
|
for i, ent in enumerate(self.entries):
|
|
for k, ek in enumerate(self.entries):
|
|
if ent.key_id == ek.key_id and ent.offset != ek.offset:
|
|
ent.dup = 1
|
|
count += 1
|
|
break
|
|
return count
|
|
|
|
def dup_search2(self):
|
|
count = 0
|
|
for i, ent in enumerate(self.entries):
|
|
if ent.dup != 1:
|
|
continue # skip unique entry
|
|
val = ent.val.decode('utf-8')
|
|
for k, ek in enumerate(self.entries):
|
|
if ek.key_id != ent.key_id:
|
|
continue
|
|
if ek.offset == ent.offset:
|
|
continue # skip self
|
|
if ek.dup == 1:
|
|
vk = ek.val.decode('utf-8')
|
|
if vk == val:
|
|
ek.dup = 2
|
|
count += 1
|
|
return count
|
|
|
|
def save_to_text(self, filename = None):
|
|
txt = ""
|
|
if 'k' in self.options:
|
|
entries = sorted(self.entries, key=lambda x: x.key_id)
|
|
else:
|
|
entries = sorted(self.entries, key=lambda x: x.offset)
|
|
for i, ent in enumerate(self.entries):
|
|
ent.dup = 0
|
|
self.dup_search()
|
|
if 'z' in self.options:
|
|
self.dup_search2()
|
|
for i, ent in enumerate(entries):
|
|
if ent.dup == 2:
|
|
continue # skip dup2
|
|
val = ent.val.decode('utf-8')
|
|
val = val.replace('\\', '\\\\')
|
|
val = val.replace('"', r'\"')
|
|
if ent.key_id == 0 and ent.plural == -1:
|
|
val = val.replace('\n', r'\n')
|
|
txt += 'msgid ""' + '\n'
|
|
txt += 'msgstr ""' + '\n'
|
|
txt += '"Project-Id-Version: LuCI: {}\\n"'.format("base") + '\n'
|
|
txt += '"Language: {}\\n"'.format("en") + '\n'
|
|
txt += '"MIME-Version: 1.0\\n"' + '\n'
|
|
txt += '"Content-Type: text/plain; charset=UTF-8\\n"' + '\n'
|
|
txt += '"Content-Transfer-Encoding: 8bit\\n"' + '\n'
|
|
txt += '"Plural-Forms: {}\\n"'.format(val) + '\n'
|
|
txt += '"X-Generator: Weblate 4.8.1-dev\\n"' + '\n'
|
|
txt += '\n'
|
|
continue
|
|
prefix = ''
|
|
#if ent.plural != 0:
|
|
# prefix = '[%d]' % ent.plural
|
|
if ent.dup:
|
|
txt += '# DUP' + '\n'
|
|
txt += 'msgid 0x{}'.format("%08X" % ent.key_id) + '\n'
|
|
line_limit = 77
|
|
val = val.replace('\r', '')
|
|
if val.find('\n') >= 0:
|
|
txt += 'msgstr{} ""'.format(prefix) + '\n'
|
|
vlist = val.split('\n')
|
|
for i, v in enumerate(vlist):
|
|
v = v + r'\n' if i+1 < len(vlist) else v
|
|
if len(v) > 0:
|
|
txt += '"{}"'.format(v) + '\n'
|
|
elif val.find(r'\n') >= 0:
|
|
txt += 'msgstr{} ""'.format(prefix) + '\n'
|
|
vlist = val.split(r'\n')
|
|
for i, v in enumerate(vlist):
|
|
if len(v) > 0:
|
|
txt += '"{}"'.format(v) + '\n'
|
|
elif len(val) > line_limit - 10 and val.find(' ') > 0:
|
|
txt += 'msgstr{} ""'.format(prefix) + '\n'
|
|
wlist = val.split(' ')
|
|
v = ""
|
|
for i, word in enumerate(wlist):
|
|
if i > 0:
|
|
word = ' ' + word
|
|
if len(v) + len(word) < line_limit or len(v) == 0:
|
|
v += word
|
|
continue
|
|
txt += '"{}"'.format(v) + '\n'
|
|
v = word
|
|
if len(v) > 0:
|
|
txt += '"{}"'.format(v) + '\n'
|
|
else:
|
|
txt += 'msgstr{} "{}"'.format(prefix, val) + '\n'
|
|
txt += '\n'
|
|
if filename:
|
|
with open(filename, "wt", encoding='UTF-8', newline = "\n") as file:
|
|
file.write(txt)
|
|
return txt
|
|
|
|
|
|
if __name__ == "__main__":
|
|
fn_inp = sys.argv[1]
|
|
fn_out = sys.argv[2]
|
|
lmo = Lmo()
|
|
if len(sys.argv) > 3:
|
|
lmo.options = sys.argv[3]
|
|
lmo.load_from_bin(fn_inp)
|
|
if not ('m' in lmo.options):
|
|
lmo.save_to_text(fn_out)
|
|
print('\nPO-file saved to "{}"'.format(fn_out))
|
|
sys.exit(0)
|
|
|
|
# Merge 2 lmo-files
|
|
fn_inp2 = sys.argv[4]
|
|
lmo2 = Lmo()
|
|
lmo2.load_from_bin(fn_inp2)
|
|
for i, ent in enumerate(lmo2.entries):
|
|
dup = False
|
|
for k, ek in enumerate(lmo.entries):
|
|
if ek.key_id == ent.key_id:
|
|
dup = True
|
|
break
|
|
if not dup:
|
|
lmo.entries.append(ent)
|
|
lmo.options = 'kz'
|
|
lmo.save_to_text(fn_out)
|
|
print('\nMerged PO-file saved to "{}"'.format(fn_out))
|
|
|
|
'''
|
|
fn_out = fn_inp
|
|
import po2lmo
|
|
lmo3 = po2lmo.Lmo(verbose = 0)
|
|
lmo3.load_from_list(lmo.entries)
|
|
lmo3.save_to_bin(fn_out)
|
|
print('\nMerged LMO-file saved to "{}"'.format(fn_out))
|
|
'''
|
|
|
|
|