##// END OF EJS Templates
cleanup: rename check-translation.py checker function - don't hide global var
Mads Kiilerich -
r22203:35c2ea4c default
parent child Browse files
Show More
@@ -1,191 +1,191 b''
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 def checker(level, msgidpat):
10 def levelchecker(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 return checker('fatal', msgidpat)
36 return levelchecker('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 return checker('warning', msgidpat)
67 return levelchecker('warning', msgidpat)
68 68
69 69 @warningchecker()
70 70 def taildoublecolons(pe):
71 71 """Check equality of tail '::'-ness between msgid and msgstr
72 72
73 73 >>> pe = polib.POEntry(
74 74 ... msgid ='ends with ::',
75 75 ... msgstr='ends with ::')
76 76 >>> for e in taildoublecolons(pe): print e
77 77 >>> pe = polib.POEntry(
78 78 ... msgid ='ends with ::',
79 79 ... msgstr='ends without double-colons')
80 80 >>> for e in taildoublecolons(pe): print e
81 81 tail '::'-ness differs between msgid and msgstr
82 82 >>> pe = polib.POEntry(
83 83 ... msgid ='ends without double-colons',
84 84 ... msgstr='ends with ::')
85 85 >>> for e in taildoublecolons(pe): print e
86 86 tail '::'-ness differs between msgid and msgstr
87 87 """
88 88 if pe.msgid.endswith('::') != pe.msgstr.endswith('::'):
89 89 yield "tail '::'-ness differs between msgid and msgstr"
90 90
91 91 @warningchecker()
92 92 def indentation(pe):
93 93 """Check equality of initial indentation between msgid and msgstr
94 94
95 95 This may report unexpected warning, because this doesn't aware
96 96 the syntax of rst document and the context of msgstr.
97 97
98 98 >>> pe = polib.POEntry(
99 99 ... msgid =' indented text',
100 100 ... msgstr=' narrowed indentation')
101 101 >>> for e in indentation(pe): print e
102 102 initial indentation width differs betweeen msgid and msgstr
103 103 """
104 104 idindent = len(pe.msgid) - len(pe.msgid.lstrip())
105 105 strindent = len(pe.msgstr) - len(pe.msgstr.lstrip())
106 106 if idindent != strindent:
107 107 yield "initial indentation width differs betweeen msgid and msgstr"
108 108
109 109 ####################
110 110
111 111 def check(pofile, fatal=True, warning=False):
112 112 targetlevel = { 'fatal': fatal, 'warning': warning }
113 113 targetcheckers = [(checker, level)
114 114 for checker, level in checkers
115 115 if targetlevel[level]]
116 116 if not targetcheckers:
117 117 return []
118 118
119 119 detected = []
120 120 for pe in pofile.translated_entries():
121 121 errors = []
122 122 for checker, level in targetcheckers:
123 123 if match(checker, pe):
124 124 errors.extend((level, checker.__name__, error)
125 125 for error in checker(pe))
126 126 if errors:
127 127 detected.append((pe, errors))
128 128 return detected
129 129
130 130 ########################################
131 131
132 132 if __name__ == "__main__":
133 133 import sys
134 134 import optparse
135 135
136 136 optparser = optparse.OptionParser("""%prog [options] pofile ...
137 137
138 138 This checks Mercurial specific translation problems in specified
139 139 '*.po' files.
140 140
141 141 Each detected problems are shown in the format below::
142 142
143 143 filename:linenum:type(checker): problem detail .....
144 144
145 145 "type" is "fatal" or "warning". "checker" is the name of the function
146 146 detecting corresponded error.
147 147
148 148 Checking by checker "foo" on the specific msgstr can be suppressed by
149 149 the "translator comment" like below. Multiple "no-xxxx-check" should
150 150 be separated by whitespaces::
151 151
152 152 # no-foo-check
153 153 msgid = "....."
154 154 msgstr = "....."
155 155 """)
156 156 optparser.add_option("", "--warning",
157 157 help="show also warning level problems",
158 158 action="store_true")
159 159 optparser.add_option("", "--doctest",
160 160 help="run doctest of this tool, instead of check",
161 161 action="store_true")
162 162 (options, args) = optparser.parse_args()
163 163
164 164 if options.doctest:
165 165 import os
166 166 if 'TERM' in os.environ:
167 167 del os.environ['TERM']
168 168 import doctest
169 169 failures, tests = doctest.testmod()
170 170 sys.exit(failures and 1 or 0)
171 171
172 172 # replace polib._POFileParser to show linenum of problematic msgstr
173 173 class ExtPOFileParser(polib._POFileParser):
174 174 def process(self, symbol, linenum):
175 175 super(ExtPOFileParser, self).process(symbol, linenum)
176 176 if symbol == 'MS': # msgstr
177 177 self.current_entry.linenum = linenum
178 178 polib._POFileParser = ExtPOFileParser
179 179
180 180 detected = []
181 181 warning = options.warning
182 182 for f in args:
183 183 detected.extend((f, pe, errors)
184 184 for pe, errors in check(polib.pofile(f),
185 185 warning=warning))
186 186 if detected:
187 187 for f, pe, errors in detected:
188 188 for level, checker, error in errors:
189 189 sys.stderr.write('%s:%d:%s(%s): %s\n'
190 190 % (f, pe.linenum, level, checker, error))
191 191 sys.exit(1)
General Comments 0
You need to be logged in to leave comments. Login now