##// END OF EJS Templates
tests: cleanup check-translation deprecated
timeless@mozdev.org -
r26276:93395bee default
parent child Browse files
Show More
@@ -1,234 +1,233
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 scanners = []
9 9 checkers = []
10 10
11 11 def scanner():
12 12 def decorator(func):
13 13 scanners.append(func)
14 14 return func
15 15 return decorator
16 16
17 17 def levelchecker(level, msgidpat):
18 18 def decorator(func):
19 19 if msgidpat:
20 20 match = re.compile(msgidpat).search
21 21 else:
22 22 match = lambda msgid: True
23 23 checkers.append((func, level))
24 24 func.match = match
25 25 return func
26 26 return decorator
27 27
28 28 def match(checker, pe):
29 29 """Examine whether POEntry "pe" is target of specified checker or not
30 30 """
31 31 if not checker.match(pe.msgid):
32 32 return
33 33 # examine suppression by translator comment
34 34 nochecker = 'no-%s-check' % checker.__name__
35 35 for tc in pe.tcomment.split():
36 36 if nochecker == tc:
37 37 return
38 38 return True
39 39
40 40 ####################
41 41
42 42 def fatalchecker(msgidpat=None):
43 43 return levelchecker('fatal', msgidpat)
44 44
45 45 @fatalchecker(r'\$\$')
46 46 def promptchoice(pe):
47 47 """Check translation of the string given to "ui.promptchoice()"
48 48
49 49 >>> pe = polib.POEntry(
50 50 ... msgid ='prompt$$missing &sep$$missing &amp$$followed by &none',
51 51 ... msgstr='prompt missing &sep$$missing amp$$followed by none&')
52 52 >>> match(promptchoice, pe)
53 53 True
54 54 >>> for e in promptchoice(pe): print e
55 55 number of choices differs between msgid and msgstr
56 56 msgstr has invalid choice missing '&'
57 57 msgstr has invalid '&' followed by none
58 58 """
59 59 idchoices = [c.rstrip(' ') for c in pe.msgid.split('$$')[1:]]
60 60 strchoices = [c.rstrip(' ') for c in pe.msgstr.split('$$')[1:]]
61 61
62 62 if len(idchoices) != len(strchoices):
63 63 yield "number of choices differs between msgid and msgstr"
64 64
65 65 indices = [(c, c.find('&')) for c in strchoices]
66 66 if [c for c, i in indices if i == -1]:
67 67 yield "msgstr has invalid choice missing '&'"
68 68 if [c for c, i in indices if len(c) == i + 1]:
69 69 yield "msgstr has invalid '&' followed by none"
70 70
71 71 deprecatedpe = None
72 72 @scanner()
73 73 def deprecatedsetup(pofile):
74 74 pes = [p for p in pofile if p.msgid == 'DEPRECATED']
75 75 if len(pes):
76 76 global deprecatedpe
77 77 deprecatedpe = pes[0]
78 78
79 79 @fatalchecker('(DEPRECATED)')
80 80 def deprecated(pe):
81 81 """Check for DEPRECATED
82 82 >>> ped = polib.POEntry(
83 83 ... msgid = 'DEPRECATED',
84 84 ... msgstr= 'DETACERPED')
85 85 >>> deprecatedsetup([ped])
86 86 >>> pe = polib.POEntry(
87 87 ... msgid = 'Something (DEPRECATED)',
88 88 ... msgstr= 'something (DETACERPED)')
89 89 >>> match(deprecated, pe)
90 90 True
91 91 >>> pe = polib.POEntry(
92 92 ... msgid = 'Something (DEPRECATED)',
93 93 ... msgstr= 'something')
94 94 >>> match(deprecated, pe)
95 95 True
96 96 >>> for e in deprecated(pe): print e
97 97 msgstr inconsistently translated (DEPRECATED)
98 98 """
99 global deprecatedpe
100 if not '(DEPRECATED)' in pe.msgstr:
101 if not (deprecatedpe and deprecatedpe.msgstr
102 and deprecatedpe.msgstr in pe.msgstr):
103 yield "msgstr inconsistently translated (DEPRECATED)"
99 if not ('(DEPRECATED)' in pe.msgstr or
100 (deprecatedpe and deprecatedpe.msgstr and
101 deprecatedpe.msgstr in pe.msgstr)):
102 yield "msgstr inconsistently translated (DEPRECATED)"
104 103
105 104 ####################
106 105
107 106 def warningchecker(msgidpat=None):
108 107 return levelchecker('warning', msgidpat)
109 108
110 109 @warningchecker()
111 110 def taildoublecolons(pe):
112 111 """Check equality of tail '::'-ness between msgid and msgstr
113 112
114 113 >>> pe = polib.POEntry(
115 114 ... msgid ='ends with ::',
116 115 ... msgstr='ends with ::')
117 116 >>> for e in taildoublecolons(pe): print e
118 117 >>> pe = polib.POEntry(
119 118 ... msgid ='ends with ::',
120 119 ... msgstr='ends without double-colons')
121 120 >>> for e in taildoublecolons(pe): print e
122 121 tail '::'-ness differs between msgid and msgstr
123 122 >>> pe = polib.POEntry(
124 123 ... msgid ='ends without double-colons',
125 124 ... msgstr='ends with ::')
126 125 >>> for e in taildoublecolons(pe): print e
127 126 tail '::'-ness differs between msgid and msgstr
128 127 """
129 128 if pe.msgid.endswith('::') != pe.msgstr.endswith('::'):
130 129 yield "tail '::'-ness differs between msgid and msgstr"
131 130
132 131 @warningchecker()
133 132 def indentation(pe):
134 133 """Check equality of initial indentation between msgid and msgstr
135 134
136 135 This may report unexpected warning, because this doesn't aware
137 136 the syntax of rst document and the context of msgstr.
138 137
139 138 >>> pe = polib.POEntry(
140 139 ... msgid =' indented text',
141 140 ... msgstr=' narrowed indentation')
142 141 >>> for e in indentation(pe): print e
143 142 initial indentation width differs betweeen msgid and msgstr
144 143 """
145 144 idindent = len(pe.msgid) - len(pe.msgid.lstrip())
146 145 strindent = len(pe.msgstr) - len(pe.msgstr.lstrip())
147 146 if idindent != strindent:
148 147 yield "initial indentation width differs betweeen msgid and msgstr"
149 148
150 149 ####################
151 150
152 151 def check(pofile, fatal=True, warning=False):
153 152 targetlevel = { 'fatal': fatal, 'warning': warning }
154 153 targetcheckers = [(checker, level)
155 154 for checker, level in checkers
156 155 if targetlevel[level]]
157 156 if not targetcheckers:
158 157 return []
159 158
160 159 detected = []
161 160 for checker in scanners:
162 161 checker(pofile)
163 162 for pe in pofile.translated_entries():
164 163 errors = []
165 164 for checker, level in targetcheckers:
166 165 if match(checker, pe):
167 166 errors.extend((level, checker.__name__, error)
168 167 for error in checker(pe))
169 168 if errors:
170 169 detected.append((pe, errors))
171 170 return detected
172 171
173 172 ########################################
174 173
175 174 if __name__ == "__main__":
176 175 import sys
177 176 import optparse
178 177
179 178 optparser = optparse.OptionParser("""%prog [options] pofile ...
180 179
181 180 This checks Mercurial specific translation problems in specified
182 181 '*.po' files.
183 182
184 183 Each detected problems are shown in the format below::
185 184
186 185 filename:linenum:type(checker): problem detail .....
187 186
188 187 "type" is "fatal" or "warning". "checker" is the name of the function
189 188 detecting corresponded error.
190 189
191 190 Checking by checker "foo" on the specific msgstr can be suppressed by
192 191 the "translator comment" like below. Multiple "no-xxxx-check" should
193 192 be separated by whitespaces::
194 193
195 194 # no-foo-check
196 195 msgid = "....."
197 196 msgstr = "....."
198 197 """)
199 198 optparser.add_option("", "--warning",
200 199 help="show also warning level problems",
201 200 action="store_true")
202 201 optparser.add_option("", "--doctest",
203 202 help="run doctest of this tool, instead of check",
204 203 action="store_true")
205 204 (options, args) = optparser.parse_args()
206 205
207 206 if options.doctest:
208 207 import os
209 208 if 'TERM' in os.environ:
210 209 del os.environ['TERM']
211 210 import doctest
212 211 failures, tests = doctest.testmod()
213 212 sys.exit(failures and 1 or 0)
214 213
215 214 # replace polib._POFileParser to show linenum of problematic msgstr
216 215 class ExtPOFileParser(polib._POFileParser):
217 216 def process(self, symbol, linenum):
218 217 super(ExtPOFileParser, self).process(symbol, linenum)
219 218 if symbol == 'MS': # msgstr
220 219 self.current_entry.linenum = linenum
221 220 polib._POFileParser = ExtPOFileParser
222 221
223 222 detected = []
224 223 warning = options.warning
225 224 for f in args:
226 225 detected.extend((f, pe, errors)
227 226 for pe, errors in check(polib.pofile(f),
228 227 warning=warning))
229 228 if detected:
230 229 for f, pe, errors in detected:
231 230 for level, checker, error in errors:
232 231 sys.stderr.write('%s:%d:%s(%s): %s\n'
233 232 % (f, pe.linenum, level, checker, error))
234 233 sys.exit(1)
General Comments 0
You need to be logged in to leave comments. Login now