]> git.lyx.org Git - features.git/blob - development/tools/mergepo.py
Add utility to merge po files
[features.git] / development / tools / mergepo.py
1 #! /usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 # file mergepo.py
5 # This file is part of LyX, the document processor.
6 # Licence details can be found in the file COPYING.
7
8 # author Georg Baum
9
10 # Full author contact details are available in file CREDITS
11
12 # This script takes missing translations from another set of po files and
13 # merges them into the po files in this source tree.
14
15
16 import os, re, string, sys
17 import polib
18
19
20 # we do unix/windows line trimming ourselves since it can happen that we
21 # are on unix, but the file has been written on windows or vice versa.
22 def trim_eol(line):
23     " Remove end of line char(s)."
24     if line[-2:-1] == '\r':
25         return line[:-2]
26     elif line[-1:] == '\r' or line[-1:] == '\n':
27         return line[:-1]
28     else:
29         # file with no EOL in last line
30         return line
31
32
33 def read(input):
34     " Read utf8 input file and strip lineendings."
35     lines = list()
36     while 1:
37         line = input.readline()
38         if not line:
39             break
40         line = trim_eol(line)
41         lines.append(line.decode('UTF-8'))
42     return lines
43
44
45 def parse_msg(lines):
46     " Extracts msgid or msgstr from lines."
47     if len(lines) < 1:
48         return ''
49     i = lines[0].find('"')
50     if i < 0:
51         return ''
52     msg = lines[0][i:].strip('"')
53     for i in range(1, len(lines)):
54         msg = msg + lines[i].strip('"')
55     return msg
56
57
58 def translate(msgid, msgstr_lines, po2):
59     msgstr = parse_msg(msgstr_lines)
60     if msgstr != '':
61         return 0
62     other = po2.find(msgid)
63     if not other:
64         return 0
65     if not other.translated():
66         return 0
67     msgstr = other.msgstr
68     obsolete = (msgstr_lines[0].find('#~') == 0)
69     j = msgstr_lines[0].find('"')
70     # must not assign to msgstr_lines, because that would not be seen by our caller
71     new_lines = polib.wrap(msgstr_lines[0][0:j+1] + msgstr, 76, drop_whitespace = False)
72     del msgstr_lines[:]
73     for i in range(0, len(new_lines)):
74         if i == 0:
75             msgstr_lines.append(new_lines[i] + '"')
76         elif obsolete:
77             msgstr_lines.append('#~ "' + new_lines[i] + '"')
78         else:
79             msgstr_lines.append('"' + new_lines[i] + '"')
80     return 1
81
82
83 def mergepo_polib(target, source):
84     changed = 0
85     po1 = polib.pofile(target)
86     po2 = polib.pofile(source)
87     for entry in po1.untranslated_entries():
88         other = po2.find(entry.msgid, include_obsolete_entries=True)
89         if not other:
90             continue
91         if other.translated():
92             entry.msgstr = other.msgstr
93             changed = changed + 1
94     if changed > 0:
95         po1.save(target)
96     return changed
97
98
99 def mergepo_minimaldiff(target, source):
100     changed = 0
101     po2 = polib.pofile(source)
102     target_enc = polib.detect_encoding(target)
103     # for utf8 files we can use our self written parser to minimize diffs,
104     # otherwise we need to use polib
105     if target_enc != 'UTF-8':
106         raise
107     po1 = open(target, 'rb')
108     oldlines = read(po1)
109     po1.close()
110     newlines = []
111     in_msgid = False
112     in_msgstr = False
113     msgstr_lines = []
114     msgid_lines = []
115     msgid = ''
116     for line in oldlines:
117         if in_msgid:
118             if line.find('"') == 0:
119                 msgid_lines.append(line)
120             else:
121                 in_msgid = False
122                 msgid = parse_msg(msgid_lines)
123                 newlines.extend(msgid_lines)
124                 msgid_lines = []
125         elif in_msgstr:
126             if line.find('"') == 0:
127                 msgstr_lines.append(line)
128             else:
129                 in_msgstr = False
130                 changed = changed + translate(msgid, msgstr_lines, po2)
131                 newlines.extend(msgstr_lines)
132                 msgstr_lines = []
133                 msgid = ''
134         if not in_msgid and not in_msgstr:
135             if line.find('msgid') == 0 or line.find('#~ msgid') == 0:
136                 msgid_lines.append(line)
137                 in_msgid = True
138             elif line.find('msgstr') == 0 or line.find('#~ msgstr') == 0:
139                 if line.find('msgstr[') == 0 or line.find('#~ msgstr[') == 0:
140                     # plural forms are not implemented
141                     raise
142                 msgstr_lines.append(line)
143                 in_msgstr = True
144             else:
145                 newlines.append(line)
146     if msgid != '':
147         # the file ended with a msgstr
148         changed = changed + translate(msgid, msgstr_lines, po2)
149         newlines.extend(msgstr_lines)
150         msgstr_lines = []
151         msgid = ''
152     if changed > 0:
153         # we store .po files with unix line ends in git,
154         # so do always write them even on windows
155         po1 = open(target, 'wb')
156         for line in newlines:
157             po1.write(line.encode('UTF-8') + '\n')
158     return changed
159
160
161 def mergepo(target, source):
162     if not os.path.exists(source):
163         sys.stderr.write('Skipping %s since %s does not exist.\n' % (target, source))
164         return
165     if not os.path.exists(target):
166         sys.stderr.write('Skipping %s since %s does not exist.\n' % (target, target))
167         return
168     sys.stderr.write('Merging %s into %s: ' % (source, target))
169     try:
170         changed = mergepo_minimaldiff(target, source)
171     except:
172         changed = mergepo_polib(target, source)
173     sys.stderr.write('Updated %d translations.\n' % changed)
174
175
176 def main(argv):
177
178     toolsdir = os.path.dirname(argv[0])
179     podir1 = os.path.normpath(os.path.join(toolsdir, '../../po'))
180     if len(argv) <= 1:
181         sys.stderr.write('''Usage: %s <dir> [lang] where dir is a directory containing the .po
182        files you want to take missing translations from. If lang is not given, all languages
183        are translated, otherwise only lang.\n''' % (argv[0]))
184     podir2 = os.path.abspath(argv[1])
185
186     if len(argv) > 2:
187         name = argv[2] + '.po'
188         mergepo(os.path.join(podir1, name), os.path.join(podir2, name))
189     else:
190         for i in os.listdir(podir1):
191             (base, ext) = os.path.splitext(i)
192             if ext != ".po":
193                 continue
194             mergepo(os.path.join(podir1, i), os.path.join(podir2, i))
195
196     return 0
197
198
199 if __name__ == "__main__":
200     main(sys.argv)