diff options
author | <> | 2003-01-02 05:25:50 +0000 |
---|---|---|
committer | <> | 2003-01-02 05:25:50 +0000 |
commit | b132a73f15e432eaf43310fce9196ca0c0651465 (patch) | |
tree | c15f816ba7c4de99fef510e3bd75af0890d47441 /bin/transcheck | |
download | mailman2-b132a73f15e432eaf43310fce9196ca0c0651465.tar.gz mailman2-b132a73f15e432eaf43310fce9196ca0c0651465.tar.xz mailman2-b132a73f15e432eaf43310fce9196ca0c0651465.zip |
This commit was manufactured by cvs2svn to create branch
'Release_2_1-maint'.
Diffstat (limited to 'bin/transcheck')
-rwxr-xr-x | bin/transcheck | 405 |
1 files changed, 405 insertions, 0 deletions
diff --git a/bin/transcheck b/bin/transcheck new file mode 100755 index 00000000..fbb0dcd8 --- /dev/null +++ b/bin/transcheck @@ -0,0 +1,405 @@ +#! @PYTHON@ +# +# transcheck - (c) 2002 by Simone Piunno <pioppo@ferrara.linux.it> +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the version 2.0 of the GNU General Public License as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +""" +Check a given Mailman translation, making sure that variables and +tags referenced in translation are the same variables and tags in +the original templates and catalog. + +Usage: + +cd $MAILMAN_DIR +%(program)s [-q] <lang> + +Where <lang> is your country code (e.g. 'it' for Italy) and -q is +to ask for a brief summary. +""" + +import sys +import re +import os +import getopt + +import paths +from Mailman.i18n import _ + +program = sys.argv[0] + + + +def usage(code, msg=''): + if code: + fd = sys.stderr + else: + fd = sys.stdout + print >> fd, _(__doc__) + if msg: + print >> fd, msg + sys.exit(code) + + + +class TransChecker: + "check a translation comparing with the original string" + def __init__(self, regexp): + self.dict = {} + self.errs = [] + self.regexp = re.compile(regexp) + + def checkin(self, string): + "scan a string from the original file" + for key in self.regexp.findall(string): + if self.dict.has_key(key): + self.dict[key] += 1 + else: + self.dict[key] = 1 + + def checkout(self, string): + "scan a translated string" + for key in self.regexp.findall(string): + if self.dict.has_key(key): + self.dict[key] -= 1 + else: + self.errs.append( + "%(key)s was not found" % + { 'key' : key } + ) + + def computeErrors(self): + "check for differences between checked in and checked out" + for key in self.dict.keys(): + if self.dict[key] < 0: + self.errs.append( + "Too much %(key)s" % + { 'key' : key } + ) + if self.dict[key] > 0: + self.errs.append( + "Too few %(key)s" % + { 'key' : key } + ) + return self.errs + + def status(self): + if self.errs: + return "FAILED" + else: + return "OK" + + def errorsAsString(self): + msg = "" + for err in self.errs: + msg += " - %(err)s" % { 'err': err } + return msg + + def reset(self): + self.dict = {} + self.errs = [] + + + +class POParser: + "parse a .po file extracting msgids and msgstrs" + def __init__(self, filename=""): + self.status = 0 + self.files = [] + self.msgid = "" + self.msgstr = "" + self.line = 1 + self.f = None + self.esc = { "n": "\n", "r": "\r", "t": "\t" } + if filename: + self.f = open(filename) + + def open(self, filename): + self.f = open(filename) + + def close(self): + self.f.close() + + def parse(self): + """States table for the finite-states-machine parser: + 0 idle + 1 filename-or-comment + 2 msgid + 3 msgstr + 4 end + """ + # each time we can safely re-initialize those vars + self.files = [] + self.msgid = "" + self.msgstr = "" + + + # can't continue if status == 4, this is a dead status + if self.status == 4: + return 0 + + while 1: + # continue scanning, char-by-char + c = self.f.read(1) + if not c: + # EOF -> maybe we have a msgstr to save? + self.status = 4 + if self.msgstr: + return 1 + else: + return 0 + + # keep the line count up-to-date + if c == "\n": + self.line += 1 + + # a pound was detected the previous char... + if self.status == 1: + if c == ":": + # was a line of filenames + row = self.f.readline() + self.files += row.split() + self.line += 1 + elif c == "\n": + # was a single pount on the line + pass + else: + # was a comment... discard + self.f.readline() + self.line += 1 + # in every case, we switch to idle status + self.status = 0; + continue + + # in idle status we search for a '#' or for a 'm' + if self.status == 0: + if c == "#": + # this could be a comment or a filename + self.status = 1; + continue + elif c == "m": + # this should be a msgid start... + s = self.f.read(4) + assert s == "sgid" + # so now we search for a '"' + self.status = 2 + continue + # in idle only those other chars are possibile + assert c in [ "\n", " ", "\t" ] + + # searching for the msgid string + if self.status == 2: + if c == "\n": + # a double LF is not possible here + c = self.f.read(1) + assert c != "\n" + if c == "\"": + # ok, this is the start of the string, + # now search for the end + while 1: + c = self.f.read(1) + if not c: + # EOF, bailout + self.status = 4 + return 0 + if c == "\\": + # a quoted char... + c = self.f.read(1) + if self.esc.has_key(c): + self.msgid += self.esc[c] + else: + self.msgid += c + continue + if c == "\"": + # end of string found + break + # a normal char, add it + self.msgid += c + if c == "m": + # this should be a msgstr identifier + s = self.f.read(5) + assert s == "sgstr" + # ok, now search for the msgstr string + self.status = 3 + + # searching for the msgstr string + if self.status == 3: + if c == "\n": + # a double LF is the end of the msgstr! + c = self.f.read(1) + if c == "\n": + # ok, time to go idle and return + self.status = 0 + self.line += 1 + return 1 + if c == "\"": + # start of string found + while 1: + c = self.f.read(1) + if not c: + # EOF, bail out + self.status = 4 + return 1 + if c == "\\": + # a quoted char... + c = self.f.read(1) + if self.esc.has_key(c): + self.msgid += self.esc[c] + else: + self.msgid += c + continue + if c == "\"": + # end of string + break + # a normal char, add it + self.msgstr += c + + + + +def check_file(translatedFile, originalFile, html=0, quiet=0): + """check a translated template against the original one + search also <MM-*> tags if html is not zero""" + + if html: + c = TransChecker("(%\([^)]+\)[0-9]*[sd]|</?MM-[^>]+>)") + else: + c = TransChecker("(%\([^)]+\)[0-9]*[sd])") + + try: + f = open(originalFile) + except IOError: + if not quiet: + print " - Can'open original file " + originalFile + return 1 + + while 1: + line = f.readline() + if not line: break + c.checkin(line) + + f.close() + + try: + f = open(translatedFile) + except IOError: + if not quiet: + print " - Can'open translated file " + translatedFile + return 1 + + while 1: + line = f.readline() + if not line: break + c.checkout(line) + + f.close() + + n = 0 + msg = "" + for desc in c.computeErrors(): + n +=1 + if not quiet: + print " - %(desc)s" % { 'desc': desc } + return n + + + +def check_po(file, quiet=0): + "scan the po file comparing msgids with msgstrs" + n = 0 + p = POParser(file) + c = TransChecker("(%\([^)]+\)[0-9]*[sdu]|%[0-9]*[sdu])") + while p.parse(): + if p.msgstr: + c.reset() + c.checkin(p.msgid) + c.checkout(p.msgstr) + for desc in c.computeErrors(): + n += 1 + if not quiet: + print " - near line %(line)d %(file)s: %(desc)s" % { + 'line': p.line, + 'file': p.files, + 'desc': desc + } + p.close() + return n + + +def main(): + try: + opts, args = getopt.getopt(sys.argv[1:], 'qh', ['quiet', 'help']) + except getopt.error, msg: + usage(1, msg) + + quiet = 0 + for opt, arg in opts: + if opt in ('-h', '--help'): + usage(0) + elif opt in ('-q', '--quiet'): + quiet = 1 + + if len(args) <> 1: + usage(1) + + lang = args[0] + + isHtml = re.compile("\.html$"); + isTxt = re.compile("\.txt$"); + + numerrors = 0 + numfiles = 0 + try: + files = os.listdir("templates/" + lang + "/") + except: + print "can't open templates/%s/" % lang + for file in files: + fileEN = "templates/en/" + file + fileIT = "templates/" + lang + "/" + file + errlist = [] + if isHtml.search(file): + if not quiet: + print "HTML checking " + fileIT + "... " + n = check_file(fileIT, fileEN, html=1, quiet=quiet) + if n: + numerrors += n + numfiles += 1 + elif isTxt.search(file): + if not quiet: + print "TXT checking " + fileIT + "... " + n = check_file(fileIT, fileEN, html=0, quiet=quiet) + if n: + numerrors += n + numfiles += 1 + + else: + continue + + file = "messages/" + lang + "/LC_MESSAGES/mailman.po" + if not quiet: + print "PO checking " + file + "... " + n = check_po(file, quiet=quiet) + if n: + numerrors += n + numfiles += 1 + + if quiet: + print "%(errs)u warnings in %(files)u files" % { + 'errs': numerrors, + 'files': numfiles + } + + +if __name__ == '__main__': + main() |