File indexing completed on 2024-04-28 12:39:33
0001 #!/usr/bin/env python3 0002 0003 import re 0004 import glob 0005 import os 0006 import sys 0007 0008 0009 def get_po_translations(fn): 0010 """ 0011 Read all translations from a .po file, fill them into an associative 0012 array. 0013 """ 0014 msgctxt = b'' 0015 msgid = b'' 0016 msgstr = b'' 0017 msgstrs = [] 0018 in_msgctxt = False 0019 in_msgid = False 0020 in_msgstr = False 0021 in_msgstrs = False 0022 trans = {} 0023 0024 def add_trans(): 0025 if msgid: 0026 context = msgctxt 0027 pipepos = context.find(b'|') 0028 if pipepos != -1: 0029 context = context[:pipepos] 0030 if in_msgstrs: 0031 if msgstr: 0032 msgstrs.append(msgstr) 0033 trans.setdefault(msgid, {})[context] = msgstrs 0034 else: 0035 trans.setdefault(msgid, {})[context] = msgstr 0036 0037 msgctxtre = re.compile(br'^msgctxt "(.*)"$') 0038 msgidre = re.compile(br'^msgid "(.*)"$') 0039 msgstrre = re.compile(br'^msgstr "(.*)"$') 0040 msgstrsre = re.compile(br'^msgstr\[\d\] "(.*)"$') 0041 strcontre = re.compile(br'^"(.+)"$') 0042 with open(fn, 'rb') as fh: 0043 for line in fh: 0044 line = line.replace(b'\r\n', b'\n') 0045 m = msgctxtre.match(line) 0046 if m: 0047 add_trans() 0048 msgid = b'' # do not add it again in add_trans() call below 0049 msgctxt = m.group(1) 0050 in_msgctxt = True 0051 in_msgid = False 0052 in_msgstr = False 0053 in_msgstrs = False 0054 m = msgidre.match(line) 0055 if m: 0056 add_trans() 0057 msgid = m.group(1) 0058 msgstr = b'' 0059 msgstrs = [] 0060 in_msgctxt = False 0061 in_msgid = True 0062 in_msgstr = False 0063 in_msgstrs = False 0064 m = msgstrre.match(line) 0065 if m: 0066 msgstr = m.group(1) 0067 in_msgctxt = False 0068 in_msgid = False 0069 in_msgstr = True 0070 in_msgstrs = False 0071 m = msgstrsre.match(line) 0072 if m: 0073 if msgstr: 0074 msgstrs.append(msgstr) 0075 msgstr = m.group(1) 0076 in_msgctxt = False 0077 in_msgid = False 0078 in_msgstr = True 0079 in_msgstrs = True 0080 m = strcontre.match(line) 0081 if m: 0082 if in_msgctxt: 0083 msgctxt += m.group(1) 0084 elif in_msgid: 0085 msgid += m.group(1) 0086 elif in_msgstr: 0087 msgstr += m.group(1) 0088 add_trans() 0089 return trans 0090 0091 0092 def set_ts_translations(infn, outfn, trans): 0093 """ 0094 Set the translations in a .ts file replacing & by &, < by <, 0095 > by > and ' by '. 0096 """ 0097 def decode_entities(s): 0098 return s \ 0099 .replace(b'&', b'&') \ 0100 .replace(b'<', b'<') \ 0101 .replace(b'>', b'>') \ 0102 .replace(b''', b"'") \ 0103 .replace(b'"', br'\"') \ 0104 .replace(b'\n', br'\n') 0105 0106 def encode_entities(s): 0107 return s \ 0108 .replace(b'&', b'&') \ 0109 .replace(b'<', b'<') \ 0110 .replace(b'>', b'>') \ 0111 .replace(b"'", b''') \ 0112 .replace(br'\"', b'"') \ 0113 .replace(br'\n', b'\n') 0114 0115 name = b'' 0116 source = b'' 0117 in_source = False 0118 numerusforms = [] 0119 namere = re.compile(br'<name>(.*)</name>') 0120 sourcere = re.compile(br'<source>(.*)</source>') 0121 sourcebeginre = re.compile(br'<source>(.*)$') 0122 sourceendre = re.compile(br'^(.*)</source>') 0123 with open(infn, 'rb') as infh: 0124 with open(outfn, 'wb') as outfh: 0125 for line in infh: 0126 line = line.replace(b'\r\n', b'\n') 0127 m = namere.search(line) 0128 if m: 0129 name = m.group(1) 0130 m = sourcere.search(line) 0131 if m: 0132 source = m.group(1) 0133 in_source = False 0134 else: 0135 m = sourcebeginre.search(line) 0136 if m: 0137 source = m.group(1) 0138 in_source = True 0139 elif in_source: 0140 source += b'\n' 0141 m = sourceendre.match(line) 0142 if m: 0143 source += m.group(1) 0144 in_source = False 0145 else: 0146 source += line.strip() 0147 elif b'<translation' in line: 0148 source = decode_entities(source) 0149 if source in trans: 0150 translations_for_context = trans[source] 0151 translation = translations_for_context.get(name, b'') 0152 if not translation: 0153 for translation in translations_for_context.values(): 0154 if translation: 0155 break 0156 line = line.replace(b' type="unfinished"', b'') 0157 if type(translation) == list: 0158 numerusforms = translation 0159 else: 0160 translation = encode_entities(translation) 0161 line = line.replace(b'</translation>', 0162 translation + b'</translation>') 0163 else: 0164 print('%s: Could not find translation for "%s"' % 0165 (outfn, source.decode())) 0166 elif b'<numerusform' in line: 0167 if numerusforms: 0168 translation = encode_entities(numerusforms.pop(0)) 0169 line = line.replace(b'</numerusform>', 0170 translation + b'</numerusform>') 0171 else: 0172 print('%s: Could not find translation for "%s"' % 0173 (outfn, source.decode())) 0174 0175 outfh.write(line) 0176 0177 0178 def generate_ts(lupdate_cmd, podir, srcdir): 0179 """ 0180 Generate .ts files from .po files. 0181 parameters: path to lupdate command, directory with po-files, 0182 directory with source files 0183 """ 0184 pofiles = glob.glob(os.path.join(podir, '*/kid3_qt.po')) 0185 pofnre = re.compile(r'^.*[\\/]([\w@]+)[\\/][^\\/]+\.po$') 0186 languages = [pofnre.sub(r'\1', f) for f in pofiles] 0187 curdir = os.getcwd() 0188 sources = [] 0189 for root, dirs, files in os.walk(srcdir): 0190 for fn in files: 0191 if fn.endswith('.cpp'): 0192 sources.append(os.path.join(root, fn)) 0193 os.chdir(srcdir) 0194 os.system(lupdate_cmd + ' -recursive -locations none . -ts ' + 0195 ' '.join([os.path.join(curdir, 'tmp_' + l + '.ts') 0196 for l in languages])) 0197 if 'en' in languages: 0198 os.system(lupdate_cmd + ' -pluralonly -recursive -locations none . -ts ' + 0199 os.path.join(curdir, 'tmp_en.ts')) 0200 os.chdir(curdir) 0201 for pofile, lang in zip(pofiles, languages): 0202 tmptsfn = 'tmp_' + lang + '.ts' 0203 set_ts_translations(tmptsfn, 'kid3_' + lang + '.ts', 0204 get_po_translations(pofile)) 0205 os.remove(tmptsfn) 0206 0207 0208 if __name__ == '__main__': 0209 generate_ts(*sys.argv[1:4])