##// END OF EJS Templates
i18n: look translation of both "DEPRECATED" and "(DEPRECATED)" up...
FUJIWARA Katsunori -
r26838:47dd34f2 3.6 stable
parent child Browse files
Show More
@@ -1,244 +1,246
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 pes = [p for p in pofile if p.msgid == 'DEPRECATED']
74 pes = [p for p in pofile
75 if ((p.msgid == 'DEPRECATED' or p.msgid == '(DEPRECATED)') and
76 p.msgstr)]
75 77 if len(pes):
76 78 global deprecatedpe
77 79 deprecatedpe = pes[0]
78 80
79 81 @fatalchecker(r'\(DEPRECATED\)')
80 82 def deprecated(pe):
81 83 """Check for DEPRECATED
82 84 >>> ped = polib.POEntry(
83 85 ... msgid = 'DEPRECATED',
84 86 ... msgstr= 'DETACERPED')
85 87 >>> deprecatedsetup([ped])
86 88 >>> pe = polib.POEntry(
87 89 ... msgid = 'Something (DEPRECATED)',
88 90 ... msgstr= 'something (DEPRECATED)')
89 91 >>> match(deprecated, pe)
90 92 True
91 93 >>> for e in deprecated(pe): print e
92 94 >>> pe = polib.POEntry(
93 95 ... msgid = 'Something (DEPRECATED)',
94 96 ... msgstr= 'something (DETACERPED)')
95 97 >>> match(deprecated, pe)
96 98 True
97 99 >>> for e in deprecated(pe): print e
98 100 >>> pe = polib.POEntry(
99 101 ... msgid = 'Something (DEPRECATED)',
100 102 ... msgstr= 'something')
101 103 >>> match(deprecated, pe)
102 104 True
103 105 >>> for e in deprecated(pe): print e
104 106 msgstr inconsistently translated (DEPRECATED)
105 107 >>> pe = polib.POEntry(
106 108 ... msgid = 'Something (DEPRECATED, foo bar)',
107 109 ... msgstr= 'something (DETACERPED, foo bar)')
108 110 >>> match(deprecated, pe)
109 111 """
110 112 if not ('(DEPRECATED)' in pe.msgstr or
111 (deprecatedpe and deprecatedpe.msgstr and
113 (deprecatedpe and
112 114 deprecatedpe.msgstr in pe.msgstr)):
113 115 yield "msgstr inconsistently translated (DEPRECATED)"
114 116
115 117 ####################
116 118
117 119 def warningchecker(msgidpat=None):
118 120 return levelchecker('warning', msgidpat)
119 121
120 122 @warningchecker()
121 123 def taildoublecolons(pe):
122 124 """Check equality of tail '::'-ness between msgid and msgstr
123 125
124 126 >>> pe = polib.POEntry(
125 127 ... msgid ='ends with ::',
126 128 ... msgstr='ends with ::')
127 129 >>> for e in taildoublecolons(pe): print e
128 130 >>> pe = polib.POEntry(
129 131 ... msgid ='ends with ::',
130 132 ... msgstr='ends without double-colons')
131 133 >>> for e in taildoublecolons(pe): print e
132 134 tail '::'-ness differs between msgid and msgstr
133 135 >>> pe = polib.POEntry(
134 136 ... msgid ='ends without double-colons',
135 137 ... msgstr='ends with ::')
136 138 >>> for e in taildoublecolons(pe): print e
137 139 tail '::'-ness differs between msgid and msgstr
138 140 """
139 141 if pe.msgid.endswith('::') != pe.msgstr.endswith('::'):
140 142 yield "tail '::'-ness differs between msgid and msgstr"
141 143
142 144 @warningchecker()
143 145 def indentation(pe):
144 146 """Check equality of initial indentation between msgid and msgstr
145 147
146 148 This may report unexpected warning, because this doesn't aware
147 149 the syntax of rst document and the context of msgstr.
148 150
149 151 >>> pe = polib.POEntry(
150 152 ... msgid =' indented text',
151 153 ... msgstr=' narrowed indentation')
152 154 >>> for e in indentation(pe): print e
153 155 initial indentation width differs betweeen msgid and msgstr
154 156 """
155 157 idindent = len(pe.msgid) - len(pe.msgid.lstrip())
156 158 strindent = len(pe.msgstr) - len(pe.msgstr.lstrip())
157 159 if idindent != strindent:
158 160 yield "initial indentation width differs betweeen msgid and msgstr"
159 161
160 162 ####################
161 163
162 164 def check(pofile, fatal=True, warning=False):
163 165 targetlevel = { 'fatal': fatal, 'warning': warning }
164 166 targetcheckers = [(checker, level)
165 167 for checker, level in checkers
166 168 if targetlevel[level]]
167 169 if not targetcheckers:
168 170 return []
169 171
170 172 detected = []
171 173 for checker in scanners:
172 174 checker(pofile)
173 175 for pe in pofile.translated_entries():
174 176 errors = []
175 177 for checker, level in targetcheckers:
176 178 if match(checker, pe):
177 179 errors.extend((level, checker.__name__, error)
178 180 for error in checker(pe))
179 181 if errors:
180 182 detected.append((pe, errors))
181 183 return detected
182 184
183 185 ########################################
184 186
185 187 if __name__ == "__main__":
186 188 import sys
187 189 import optparse
188 190
189 191 optparser = optparse.OptionParser("""%prog [options] pofile ...
190 192
191 193 This checks Mercurial specific translation problems in specified
192 194 '*.po' files.
193 195
194 196 Each detected problems are shown in the format below::
195 197
196 198 filename:linenum:type(checker): problem detail .....
197 199
198 200 "type" is "fatal" or "warning". "checker" is the name of the function
199 201 detecting corresponded error.
200 202
201 203 Checking by checker "foo" on the specific msgstr can be suppressed by
202 204 the "translator comment" like below. Multiple "no-xxxx-check" should
203 205 be separated by whitespaces::
204 206
205 207 # no-foo-check
206 208 msgid = "....."
207 209 msgstr = "....."
208 210 """)
209 211 optparser.add_option("", "--warning",
210 212 help="show also warning level problems",
211 213 action="store_true")
212 214 optparser.add_option("", "--doctest",
213 215 help="run doctest of this tool, instead of check",
214 216 action="store_true")
215 217 (options, args) = optparser.parse_args()
216 218
217 219 if options.doctest:
218 220 import os
219 221 if 'TERM' in os.environ:
220 222 del os.environ['TERM']
221 223 import doctest
222 224 failures, tests = doctest.testmod()
223 225 sys.exit(failures and 1 or 0)
224 226
225 227 # replace polib._POFileParser to show linenum of problematic msgstr
226 228 class ExtPOFileParser(polib._POFileParser):
227 229 def process(self, symbol, linenum):
228 230 super(ExtPOFileParser, self).process(symbol, linenum)
229 231 if symbol == 'MS': # msgstr
230 232 self.current_entry.linenum = linenum
231 233 polib._POFileParser = ExtPOFileParser
232 234
233 235 detected = []
234 236 warning = options.warning
235 237 for f in args:
236 238 detected.extend((f, pe, errors)
237 239 for pe, errors in check(polib.pofile(f),
238 240 warning=warning))
239 241 if detected:
240 242 for f, pe, errors in detected:
241 243 for level, checker, error in errors:
242 244 sys.stderr.write('%s:%d:%s(%s): %s\n'
243 245 % (f, pe.linenum, level, checker, error))
244 246 sys.exit(1)
General Comments 0
You need to be logged in to leave comments. Login now