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