]> git.lyx.org Git - lyx.git/blobdiff - development/tools/mergepo.py
Check path of Qt tools if qtchooser is detected
[lyx.git] / development / tools / mergepo.py
old mode 100644 (file)
new mode 100755 (executable)
index 5296052..a86b75d
 
 
 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
@@ -39,7 +36,7 @@ def read(input):
         if not line:
             break
         line = trim_eol(line)
-        lines.append(line.decode('UTF-8'))
+        lines.append(line)
     return lines
 
 
@@ -52,24 +49,44 @@ def parse_msg(lines):
         return ''
     msg = lines[0][i:].strip('"')
     for i in range(1, len(lines)):
-        msg = msg + lines[i].strip('"')
-    return msg
+        j = lines[i].find('"')
+        if j < 0:
+            return ''
+        msg = msg + lines[i][j:].strip('"')
+    return polib.unescape(msg)
 
 
-def translate(msgid, msgstr_lines, po2):
+def translate(msgid, flags, msgstr_lines, po2, options):
     msgstr = parse_msg(msgstr_lines)
-    if msgstr != '':
-        return 0
-    other = po2.find(msgid)
-    if not other:
-        return 0
-    if not other.translated():
+    if options.overwrite:
+        other = po2.find(msgid)
+        if not other:
+            return 0
+        if not other.translated():
+            return 0
+        if msgstr == other.msgstr:
+            return 0
+    else:
+        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
-    new_lines = polib.wrap(msgstr_lines[0][0:j+1] + msgstr, 76, drop_whitespace = False)
+    new_lines = polib.wrap(msgstr_lines[0][0:j+1] + polib.escape(msgstr), 76, drop_whitespace = False)
     del msgstr_lines[:]
     for i in range(0, len(new_lines)):
         if i == 0:
@@ -81,36 +98,58 @@ def translate(msgid, msgstr_lines, po2):
     return 1
 
 
-def mergepo_polib(target, source):
+def mergepo_polib(target, source, options):
     changed = 0
     po1 = polib.pofile(target)
     po2 = polib.pofile(source)
-    for entry in po1.untranslated_entries():
-        other = po2.find(entry.msgid, include_obsolete_entries=True)
-        if not other:
-            continue
-        if other.translated():
-            entry.msgstr = other.msgstr
-            changed = changed + 1
+    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
     if changed > 0:
         po1.save(target)
     return changed
 
 
-def mergepo_minimaldiff(target, source):
+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 = ''
@@ -121,19 +160,25 @@ def mergepo_minimaldiff(target, source):
             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)
+                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:
@@ -146,20 +191,26 @@ def mergepo_minimaldiff(target, source):
                 newlines.append(line)
     if msgid != '':
         # the file ended with a msgstr
-        changed = changed + translate(msgid, msgstr_lines, po2)
+        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):
+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
@@ -168,10 +219,12 @@ def mergepo(target, source):
         return
     sys.stderr.write('Merging %s into %s: ' % (source, target))
     try:
-        changed = mergepo_minimaldiff(target, source)
-    except:
-        changed = mergepo_polib(target, source)
-    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):
@@ -185,6 +238,10 @@ yet in the target .po files are not updated.""", usage = "Usage: %prog [options]
                       help="target directory containing .po files. If missing, it is determined from the script location.")
     parser.add_option("-l", "--language", dest="language",
                       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()
@@ -199,13 +256,13 @@ yet in the target .po files are not updated.""", usage = "Usage: %prog [options]
 
     if options.language:
         name = options.language + '.po'
-        mergepo(os.path.join(podir1, name), os.path.join(podir2, name))
+        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))
+            mergepo(os.path.join(podir1, i), os.path.join(podir2, i), options)
 
     return 0