1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#
4# Copyright (C) 2013-2021 Edgewall Software
5# Copyright (C) 2013 Christian Boos <cboos@edgewall.org>
6# All rights reserved.
7#
8# This software is licensed as described in the file COPYING, which
9# you should have received as part of this distribution. The terms
10# are also available at https://trac.edgewall.org/wiki/TracLicense.
11#
12# This software consists of voluntary contributions made by many
13# individuals. For the exact contribution history, see the revision
14# history and logs, available at https://trac.edgewall.org/.
15
16"""
17
18L10N tool which prepares an index of "interesting" changes found in a
19.diff.
20
21Skipped changes are:
22 - the changes for which the msgid has changed
23 - removal only of msgstr content
24
25
26Example workflow 1), review changes to the 'fr' translation before
27committing:
28
29  make diff-fr | less
30
31
32Example workflow 2), force a pull of all changes from Transifex::
33
34  make update updateopts=-N
35  tx pull -f
36  make update updateopts=-N
37  svn diff > tx.diff
38  python l10n_diff_index.py tx.diff
39  svn revert -R .
40
41And then use either::
42
43  emacs tx.diff.index --eval '(grep-mode)'
44
45or::
46
47  vim -c :cbuffer -c :copen tx.diff.index
48
49This makes it easier to go through the potentially interesting changes
50only, and apply the corresponding chunks if needed.
51
52"""
53
54from bisect import bisect_left
55import re
56
57interesting_changes_re = re.compile(r'''
58                                               \n
59       \s (?: msgid(?:_plural)?\s)? ".*"       \n  # ' ' msgid or "...",
60   (?:
61       [-\s]  ".*"                             \n  # ' ' or - "...",
62   |
63       -      msgstr(?:\[\d+\])? \s ".*"       \n  # or the -msgstr
64   )*
65
66 (?:
67     ( \+     msgstr(?:\[\d+\])? \s "[^"].*" ) \n  # \1 is a non-empty +msgstr
68 |
69       [+\s]  msgstr(?:\[\d+\])? \s ".*"       \n  # or after the msgstr,
70   (?: [-\s]  ".*"                             \n  # optional ' ' or -"...",
71   )*
72     ( \+     "[^"].*" )                           # \2 is a non-empty +"..."
73 )
74''', re.MULTILINE | re.VERBOSE)
75
76def index_diffs(path, diffs):
77    linenums = []
78    re.sub(r'\n', lambda m: linenums.append(m.start()), diffs)
79    index = []
80    for m in interesting_changes_re.finditer(diffs):
81        line = m.group(m.lastindex)
82        if line.startswith(('+"Project-Id-Version:', '+"PO-Revision-Date:')):
83            continue
84        pos = m.start(m.lastindex)
85        index.append((bisect_left(linenums, pos) + 1, line))
86    return index
87
88def write_index_for(path):
89    with open(path, 'rb') as f:
90        diffs = str(f.read(), 'utf-8')
91    changes = index_diffs(path, diffs)
92    if changes:
93        index = path + '.index'
94        with open(index, 'wb') as idx:
95            for n, line in changes:
96                print(("%s:%s: %s" % (path, n, line)).encode('utf-8'),
97                      file=idx)
98        print("%s: %d changes indexed in %s" % (path, len(changes), index))
99    else:
100        print("%s: no interesting changes" % path)
101
102if __name__ == '__main__':
103    import sys
104    for path in sys.argv[1:]:
105        write_index_for(path)
106