import os, re, string, sys
+import io
import polib
from optparse import OptionParser
-# we do unix/windows line trimming ourselves since it can happen that we
-# are on unix, but the file has been written on windows or vice versa.
def trim_eol(line):
- " Remove end of line char(s)."
- if line[-2:-1] == '\r':
- return line[:-2]
- elif line[-1:] == '\r' or line[-1:] == '\n':
+ " Remove end of line char."
+ if line[-1:] == '\n':
return line[:-1]
else:
# file with no EOL in last line
if not line:
break
line = trim_eol(line)
- lines.append(line.decode('UTF-8'))
+ lines.append(line)
return lines
return polib.unescape(msg)
-def translate(msgid, msgstr_lines, po2, overwrite):
+def translate(msgid, flags, msgstr_lines, po2, options):
msgstr = parse_msg(msgstr_lines)
- if overwrite:
+ if options.overwrite:
other = po2.find(msgid)
if not other:
return 0
if msgstr == other.msgstr:
return 0
else:
- if msgstr != '':
+ if msgstr != '' and not u'fuzzy' in flags:
return 0
other = po2.find(msgid)
if not other:
return 0
if not other.translated():
return 0
+ if options.nonnull and other.msgstr == other.msgid:
+ return 0
msgstr = other.msgstr
+ if 'fuzzy' in other.flags:
+ if not u'fuzzy' in flags:
+ flags.append(u'fuzzy')
+ else:
+ if u'fuzzy' in flags:
+ flags.remove(u'fuzzy')
obsolete = (msgstr_lines[0].find('#~') == 0)
j = msgstr_lines[0].find('"')
# must not assign to msgstr_lines, because that would not be seen by our caller
return 1
-def mergepo_polib(target, source, overwrite):
+def mergepo_polib(target, source, options):
changed = 0
po1 = polib.pofile(target)
po2 = polib.pofile(source)
- if overwrite:
+ if options.overwrite:
for entry in po1.entries():
other = po2.find(entry.msgid, include_obsolete_entries=True)
if not other:
continue
+ if options.nonnull and other.msgstr == other.msgid:
+ continue
if other.translated() and other.msgstr != entry.msgstr:
entry.msgstr = other.msgstr
+ if 'fuzzy' in other.flags:
+ if not 'fuzzy' in entry.flags:
+ entry.flags.append('fuzzy')
+ else:
+ if 'fuzzy' in entry.flags:
+ entry.flags.remove('fuzzy')
changed = changed + 1
else:
for entry in po1.untranslated_entries():
other = po2.find(entry.msgid, include_obsolete_entries=True)
if not other:
continue
+ if options.nonnull and other.msgstr == other.msgid:
+ continue
if other.translated():
entry.msgstr = other.msgstr
changed = changed + 1
return changed
-def mergepo_minimaldiff(target, source, overwrite):
+def mergepo_minimaldiff(target, source, options):
changed = 0
po2 = polib.pofile(source)
target_enc = polib.detect_encoding(target)
# for utf8 files we can use our self written parser to minimize diffs,
# otherwise we need to use polib
- if target_enc != 'UTF-8':
+ if not target_enc in ['UTF-8', 'utf-8', 'utf_8']:
raise
- po1 = open(target, 'rb')
+ # open file with universal newlines, since it can happen that we are
+ # on unix, but the file has been written on windows or vice versa.
+ po1 = io.open(target, 'r', encoding='utf_8', newline=None)
oldlines = read(po1)
po1.close()
newlines = []
in_msgid = False
in_msgstr = False
+ flags = []
msgstr_lines = []
msgid_lines = []
msgid = ''
else:
in_msgid = False
msgid = parse_msg(msgid_lines)
- newlines.extend(msgid_lines)
- msgid_lines = []
elif in_msgstr:
if line.find('"') == 0 or line.find('#~ "') == 0:
msgstr_lines.append(line)
else:
in_msgstr = False
- changed = changed + translate(msgid, msgstr_lines, po2, overwrite)
+ changed = changed + translate(msgid, flags, msgstr_lines, po2, options)
+ if len(flags) > 0:
+ flagline = u'#, ' + u', '.join(flags)
+ newlines.append(flagline)
+ flags = []
+ newlines.extend(msgid_lines)
newlines.extend(msgstr_lines)
+ msgid_lines = []
msgstr_lines = []
msgid = ''
if not in_msgid and not in_msgstr:
- if line.find('msgid') == 0 or line.find('#~ msgid') == 0:
+ if line.find('#,') == 0 and len(flags) == 0:
+ flags = line[2:].strip().split(u', ')
+ elif line.find('msgid') == 0 or line.find('#~ msgid') == 0:
msgid_lines.append(line)
in_msgid = True
elif line.find('msgstr') == 0 or line.find('#~ msgstr') == 0:
newlines.append(line)
if msgid != '':
# the file ended with a msgstr
- changed = changed + translate(msgid, msgstr_lines, po2, overwrite)
+ changed = changed + translate(msgid, flags, msgstr_lines, po2, options)
+ if len(flags) > 0:
+ flagline = u'#, ' + u', '.join(flags)
+ newlines.append(flagline)
+ flags = []
+ newlines.extend(msgid_lines)
newlines.extend(msgstr_lines)
+ msgid_lines = []
msgstr_lines = []
msgid = ''
if changed > 0:
# we store .po files with unix line ends in git,
# so do always write them even on windows
- po1 = open(target, 'wb')
+ po1 = io.open(target, 'w', encoding='utf_8', newline='\n')
for line in newlines:
- po1.write(line.encode('UTF-8') + '\n')
+ po1.write(line + '\n')
return changed
-def mergepo(target, source, overwrite):
+def mergepo(target, source, options):
if not os.path.exists(source):
sys.stderr.write('Skipping %s since %s does not exist.\n' % (target, source))
return
return
sys.stderr.write('Merging %s into %s: ' % (source, target))
try:
- changed = mergepo_minimaldiff(target, source, overwrite)
- except:
- changed = mergepo_polib(target, source, overwrite)
- sys.stderr.write('Updated %d translations.\n' % changed)
+ changed = mergepo_minimaldiff(target, source, options)
+ sys.stderr.write('Updated %d translations with minimal diff.\n' % changed)
+ except Exception as e:
+ sys.stderr.write('Unable to use minimal diff: %s\n' % e)
+ changed = mergepo_polib(target, source, options)
+ sys.stderr.write('Updated %d translations using polib.\n' % changed)
def main(argv):
help="language for which translations are merged (if missing, all languages are merged)")
parser.add_option("-o", "--overwrite", action="store_true", dest="overwrite", default=False,
help="overwrite existing target translations with source translations (if missing, only new translations are added)")
+ parser.add_option("-n", "--nonnull", action="store_true", dest="nonnull", default=False,
+ help="do not update target translations with source translations that are identical to the untranslated text)")
(options, args) = parser.parse_args(argv)
if len(args) <= 1:
parser.print_help()
if options.language:
name = options.language + '.po'
- mergepo(os.path.join(podir1, name), os.path.join(podir2, name), options.overwrite)
+ mergepo(os.path.join(podir1, name), os.path.join(podir2, name), options)
else:
for i in os.listdir(podir1):
(base, ext) = os.path.splitext(i)
if ext != ".po":
continue
- mergepo(os.path.join(podir1, i), os.path.join(podir2, i), options.overwrite)
+ mergepo(os.path.join(podir1, i), os.path.join(podir2, i), options)
return 0