2 # -*- coding: utf-8 -*-
5 # This file is part of LyX, the document processor.
6 # Licence details can be found in the file COPYING.
10 # Full author contact details are available in file CREDITS
12 # This script takes missing translations from another set of po files and
13 # merges them into the po files in this source tree.
16 import os, re, string, sys
18 from optparse import OptionParser
21 # we do unix/windows line trimming ourselves since it can happen that we
22 # are on unix, but the file has been written on windows or vice versa.
24 " Remove end of line char(s)."
25 if line[-2:-1] == '\r':
27 elif line[-1:] == '\r' or line[-1:] == '\n':
30 # file with no EOL in last line
35 " Read utf8 input file and strip lineendings."
38 line = input.readline()
42 lines.append(line.decode('UTF-8'))
47 " Extracts msgid or msgstr from lines."
50 i = lines[0].find('"')
53 msg = lines[0][i:].strip('"')
54 for i in range(1, len(lines)):
55 j = lines[i].find('"')
58 msg = msg + lines[i][j:].strip('"')
59 return polib.unescape(msg)
62 def translate(msgid, msgstr_lines, po2):
63 msgstr = parse_msg(msgstr_lines)
66 other = po2.find(msgid)
69 if not other.translated():
72 obsolete = (msgstr_lines[0].find('#~') == 0)
73 j = msgstr_lines[0].find('"')
74 # must not assign to msgstr_lines, because that would not be seen by our caller
75 new_lines = polib.wrap(msgstr_lines[0][0:j+1] + polib.escape(msgstr), 76, drop_whitespace = False)
77 for i in range(0, len(new_lines)):
79 msgstr_lines.append(new_lines[i] + '"')
81 msgstr_lines.append('#~ "' + new_lines[i] + '"')
83 msgstr_lines.append('"' + new_lines[i] + '"')
87 def mergepo_polib(target, source):
89 po1 = polib.pofile(target)
90 po2 = polib.pofile(source)
91 for entry in po1.untranslated_entries():
92 other = po2.find(entry.msgid, include_obsolete_entries=True)
95 if other.translated():
96 entry.msgstr = other.msgstr
103 def mergepo_minimaldiff(target, source):
105 po2 = polib.pofile(source)
106 target_enc = polib.detect_encoding(target)
107 # for utf8 files we can use our self written parser to minimize diffs,
108 # otherwise we need to use polib
109 if target_enc != 'UTF-8':
111 po1 = open(target, 'rb')
120 for line in oldlines:
122 if line.find('"') == 0 or line.find('#~ "') == 0:
123 msgid_lines.append(line)
126 msgid = parse_msg(msgid_lines)
127 newlines.extend(msgid_lines)
130 if line.find('"') == 0 or line.find('#~ "') == 0:
131 msgstr_lines.append(line)
134 changed = changed + translate(msgid, msgstr_lines, po2)
135 newlines.extend(msgstr_lines)
138 if not in_msgid and not in_msgstr:
139 if line.find('msgid') == 0 or line.find('#~ msgid') == 0:
140 msgid_lines.append(line)
142 elif line.find('msgstr') == 0 or line.find('#~ msgstr') == 0:
143 if line.find('msgstr[') == 0 or line.find('#~ msgstr[') == 0:
144 # plural forms are not implemented
146 msgstr_lines.append(line)
149 newlines.append(line)
151 # the file ended with a msgstr
152 changed = changed + translate(msgid, msgstr_lines, po2)
153 newlines.extend(msgstr_lines)
157 # we store .po files with unix line ends in git,
158 # so do always write them even on windows
159 po1 = open(target, 'wb')
160 for line in newlines:
161 po1.write(line.encode('UTF-8') + '\n')
165 def mergepo(target, source):
166 if not os.path.exists(source):
167 sys.stderr.write('Skipping %s since %s does not exist.\n' % (target, source))
169 if not os.path.exists(target):
170 sys.stderr.write('Skipping %s since %s does not exist.\n' % (target, target))
172 sys.stderr.write('Merging %s into %s: ' % (source, target))
174 changed = mergepo_minimaldiff(target, source)
176 changed = mergepo_polib(target, source)
177 sys.stderr.write('Updated %d translations.\n' % changed)
182 parser = OptionParser(description = """This script reads translations from .po files in the given source directory
183 and adds all translations that do not already exist to the corresponding .po
184 files in the target directory. It is recommended to remerge strings from the
185 source code before running this script. Otherwise translations that are not
186 yet in the target .po files are not updated.""", usage = "Usage: %prog [options] sourcedir")
187 parser.add_option("-t", "--target", dest="target",
188 help="target directory containing .po files. If missing, it is determined from the script location.")
189 parser.add_option("-l", "--language", dest="language",
190 help="language for which translations are merged (if missing, all languages are merged)")
191 (options, args) = parser.parse_args(argv)
196 toolsdir = os.path.dirname(args[0])
198 podir1 = os.path.abspath(options.target)
200 podir1 = os.path.normpath(os.path.join(toolsdir, '../../po'))
201 podir2 = os.path.abspath(args[1])
204 name = options.language + '.po'
205 mergepo(os.path.join(podir1, name), os.path.join(podir2, name))
207 for i in os.listdir(podir1):
208 (base, ext) = os.path.splitext(i)
211 mergepo(os.path.join(podir1, i), os.path.join(podir2, i))
216 if __name__ == "__main__":