##// END OF EJS Templates
merge with stable
Matt Mackall -
r20165:1c92524c merge default
parent child Browse files
Show More
@@ -1,150 +1,151
1 1 #!/usr/bin/env python
2 2 #
3 3 # check-translation.py - check Mercurial specific translation problems
4 4
5 5 import polib
6 6 import re
7 7
8 8 checkers = []
9 9
10 10 def checker(level, msgidpat):
11 11 def decorator(func):
12 12 if msgidpat:
13 13 match = re.compile(msgidpat).search
14 14 else:
15 15 match = lambda msgid: True
16 16 checkers.append((func, level))
17 17 func.match = match
18 18 return func
19 19 return decorator
20 20
21 21 def match(checker, pe):
22 22 """Examine whether POEntry "pe" is target of specified checker or not
23 23 """
24 24 if not checker.match(pe.msgid):
25 25 return
26 26 # examine suppression by translator comment
27 27 nochecker = 'no-%s-check' % checker.__name__
28 28 for tc in pe.tcomment.split():
29 29 if nochecker == tc:
30 30 return
31 31 return True
32 32
33 33 ####################
34 34
35 35 def fatalchecker(msgidpat=None):
36 36 return checker('fatal', msgidpat)
37 37
38 38 @fatalchecker(r'\$\$')
39 39 def promptchoice(pe):
40 40 """Check translation of the string given to "ui.promptchoice()"
41 41
42 42 >>> pe = polib.POEntry(
43 43 ... msgid ='prompt$$missing &sep$$missing &amp$$followed by &none',
44 44 ... msgstr='prompt missing &sep$$missing amp$$followed by none&')
45 45 >>> match(promptchoice, pe)
46 46 True
47 47 >>> for e in promptchoice(pe): print e
48 48 number of choices differs between msgid and msgstr
49 49 msgstr has invalid choice missing '&'
50 50 msgstr has invalid '&' followed by none
51 51 """
52 52 idchoices = [c.rstrip(' ') for c in pe.msgid.split('$$')[1:]]
53 53 strchoices = [c.rstrip(' ') for c in pe.msgstr.split('$$')[1:]]
54 54
55 55 if len(idchoices) != len(strchoices):
56 56 yield "number of choices differs between msgid and msgstr"
57 57
58 58 indices = [(c, c.find('&')) for c in strchoices]
59 59 if [c for c, i in indices if i == -1]:
60 60 yield "msgstr has invalid choice missing '&'"
61 61 if [c for c, i in indices if len(c) == i + 1]:
62 62 yield "msgstr has invalid '&' followed by none"
63 63
64 64 ####################
65 65
66 66 def warningchecker(msgidpat=None):
67 67 return checker('warning', msgidpat)
68 68
69 69 ####################
70 70
71 71 def check(pofile, fatal=True, warning=False):
72 72 targetlevel = { 'fatal': fatal, 'warning': warning }
73 73 targetcheckers = [(checker, level)
74 74 for checker, level in checkers
75 75 if targetlevel[level]]
76 76 if not targetcheckers:
77 77 return []
78 78
79 79 detected = []
80 80 for pe in pofile.translated_entries():
81 81 errors = []
82 82 for checker, level in targetcheckers:
83 83 if match(checker, pe):
84 84 errors.extend((level, checker.__name__, error)
85 85 for error in checker(pe))
86 86 if errors:
87 87 detected.append((pe, errors))
88 88 return detected
89 89
90 90 ########################################
91 91
92 92 if __name__ == "__main__":
93 93 import sys
94 94 import optparse
95 95
96 96 optparser = optparse.OptionParser("""%prog [options] pofile ...
97 97
98 98 This checks Mercurial specific translation problems in specified
99 99 '*.po' files.
100 100
101 101 Each detected problems are shown in the format below::
102 102
103 103 filename:linenum:type(checker): problem detail .....
104 104
105 105 "type" is "fatal" or "warning". "checker" is the name of the function
106 106 detecting corresponded error.
107 107
108 108 Checking by checker "foo" on the specific msgstr can be suppressed by
109 109 the "translator comment" like below. Multiple "no-xxxx-check" should
110 110 be separated by whitespaces::
111 111
112 112 # no-foo-check
113 113 msgid = "....."
114 114 msgstr = "....."
115 115 """)
116 116 optparser.add_option("", "--warning",
117 117 help="show also warning level problems",
118 118 action="store_true")
119 119 optparser.add_option("", "--doctest",
120 120 help="run doctest of this tool, instead of check",
121 121 action="store_true")
122 122 (options, args) = optparser.parse_args()
123 123
124 124 if options.doctest:
125 import os
125 126 if 'TERM' in os.environ:
126 127 del os.environ['TERM']
127 128 import doctest
128 129 failures, tests = doctest.testmod()
129 130 sys.exit(failures and 1 or 0)
130 131
131 132 # replace polib._POFileParser to show linenum of problematic msgstr
132 133 class ExtPOFileParser(polib._POFileParser):
133 134 def process(self, symbol, linenum):
134 135 super(ExtPOFileParser, self).process(symbol, linenum)
135 136 if symbol == 'MS': # msgstr
136 137 self.current_entry.linenum = linenum
137 138 polib._POFileParser = ExtPOFileParser
138 139
139 140 detected = []
140 141 warning = options.warning
141 142 for f in args:
142 143 detected.extend((f, pe, errors)
143 144 for pe, errors in check(polib.pofile(f),
144 145 warning=warning))
145 146 if detected:
146 147 for f, pe, errors in detected:
147 148 for level, checker, error in errors:
148 149 sys.stderr.write('%s:%d:%s(%s): %s\n'
149 150 % (f, pe.linenum, level, checker, error))
150 151 sys.exit(1)
General Comments 0
You need to be logged in to leave comments. Login now