File indexing completed on 2024-04-14 04:48:42

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 &amp;, < by &lt;,
0095     > by &gt; and ' by &apos;.
0096     """
0097     def decode_entities(s):
0098         return s \
0099             .replace(b'&amp;', b'&') \
0100             .replace(b'&lt;', b'<') \
0101             .replace(b'&gt;', b'>') \
0102             .replace(b'&apos;', b"'") \
0103             .replace(b'&quot;', br'\"') \
0104             .replace(b'\n', br'\n')
0105 
0106     def encode_entities(s):
0107         return s \
0108             .replace(b'&', b'&amp;') \
0109             .replace(b'<', b'&lt;') \
0110             .replace(b'>', b'&gt;') \
0111             .replace(b"'", b'&apos;') \
0112             .replace(br'\"', b'&quot;') \
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])