summaryrefslogtreecommitdiffstatshomepage
path: root/scripts/build/check_po.py
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/build/check_po.py')
-rw-r--r--scripts/build/check_po.py490
1 files changed, 0 insertions, 490 deletions
diff --git a/scripts/build/check_po.py b/scripts/build/check_po.py
deleted file mode 100644
index 5328b76b1b4..00000000000
--- a/scripts/build/check_po.py
+++ /dev/null
@@ -1,490 +0,0 @@
-#! /usr/bin/env python
-#
-# check_po - a gramps tool to check validity of po files
-#
-# Copyright (C) 2006-2006 Kees Bakker
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# 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
-
-#
-# TODO
-#
-# * Check for HTML text in msgstr when there is none in msgid
-# * Check for matching HTML tag/endtag in msgstr
-#
-
-# Adapted for Umit by Guilherme Polo, original file:
-# https://gramps.svn.sourceforge.net/svnroot/gramps/branches/gramps22/po/check_po
-
-import re
-import sys
-from optparse import OptionParser
-
-APP = "Umit"
-
-all_total = {}
-all_fuzzy = {}
-all_untranslated = {}
-all_percent_s = {}
-all_named_s = {}
-all_bnamed_s = {}
-all_context = {}
-all_coverage = {}
-all_template_coverage = {}
-
-def strip_quotes(st):
- if len(st) >= 2 and st[0] == '"' and st[len(st)-1] == '"':
- st = st.strip()[1:-1]
- return st
-
-# This is a base class for all checks
-class Check:
- def __init__( self ):
- self.msgs = []
- def diag( self ):
- if len( self.msgs ):
- print
- print self.diag_header
- for m in self.msgs:
- m.diag()
- def summary( self ):
- print "%-20s%d" % ( self.summary_text, len(self.msgs) )
-
-class Check_fmt( Check ):
- def __init__( self, fmt ):
- Check.__init__( self )
- self.diag_header = "-------- %s mismatches --------------" % fmt
- self.summary_text = "%s mismatches:" % fmt
- self.fmt = fmt
- def process( self, msg ):
- msgid = msg.msgid
- msgstr = msg.msgstr
- cnt1 = msgid.count( self.fmt )
- cnt2 = msgstr.count( self.fmt )
- if cnt1 != cnt2:
- self.msgs.append( msg )
-
-class Check_named_fmt( Check ):
- # A pattern to find all %()
- find_named_fmt_pat = re.compile('% \( \w+ \) \d* \D', re.VERBOSE)
-
- def __init__( self ):
- Check.__init__( self )
- self.diag_header = "-------- %() name mismatches --------------"
- self.summary_text = "%() name mismatches:"
- def process( self, msg ):
- msgid = msg.msgid
- msgstr = msg.msgstr
- # Same number of named formats?
- fmts1 = self.find_named_fmt_pat.findall( msgid )
- fmts2 = self.find_named_fmt_pat.findall( msgstr )
- if len( fmts1 ) != len( fmts2 ):
- self.msgs.append( msg )
- else:
- # Do we have the same named formats?
- fmts1.sort()
- fmts2.sort()
- if fmts1 != fmts2:
- self.msgs.append( msg )
-
-class Check_missing_sd( Check ):
- # A pattern to find %() without s or d
- # Here is a command to use for testing
- # print re.compile('% \( \w+ \) \d* (\D|$)', re.VERBOSE).findall( '%(event_name)s: %(place)s%(endnotes)s. ' )
- find_named_fmt_pat2 = re.compile('% \( \w+ \) \d* (\D|$)', re.VERBOSE)
-
- def __init__( self ):
- Check.__init__( self )
- self.diag_header = "-------- %() without 's' or 'd' mismatches --------------"
- self.summary_text = "%() missing s/d:"
- def process( self, msg ):
- msgstr = msg.msgstr
- fmts = self.find_named_fmt_pat2.findall( msgstr )
- for f in fmts:
- if not f in ('s', 'd'):
- self.msgs.append( msg )
- break
-
-class Check_runaway( Check ):
- def __init__( self ):
- Check.__init__( self )
- self.diag_header = "-------- Runaway context in translation ---------"
- self.summary_text = "Runaway context:"
- def process( self, msg ):
- msgid = msg.msgid
- msgstr = msg.msgstr
-
- # Runaway context. In the translated part we only to see
- # the translation of the word after the |
- if msgid.count('|') > 0 and msgstr.count('|') > 0 and msgid != msgstr:
- self.msgs.append( msg )
-
-class Check_xml_chars( Check ):
- # Special XML characters
- # It is not allowed to have a quote, an ampersand or an angle bracket
- xml_chars_pat = re.compile( r'(?<=\W) > | " | & (?!(quot|nbsp|gt|amp);)', re.VERBOSE )
-
- def __init__( self ):
- Check.__init__( self )
- self.diag_header = "-------- unescaped XML special characters ---------"
- self.summary_text = "XML special chars:"
- def process( self, msg ):
- msgid = msg.msgid
- msgstr = msg.msgstr
-
- # XML errors
- # Only look at messages in the tips.xml
- if msg.is_tips_xml:
- if self.xml_chars_pat.search( msgstr ):
- self.msgs.append( msg )
-
-class Check_last_char( Check ):
- def __init__( self ):
- Check.__init__( self )
- self.diag_header = "-------- last character not identical ---------"
- self.summary_text = "Last character:"
- def process( self, msg ):
- msgid = msg.msgid
- msgstr = msg.msgstr
-
- # Last character of msgid? White space? Period?
- if msg.is_fuzzy:
- return
-
- msgid_last = msgid[-1:]
- msgstr_last = msgstr[-1:]
- if msgid_last.isspace() != msgstr_last.isspace():
- self.msgs.append( msg )
- elif (msgid_last == '.') != (msgstr_last == '.'):
- self.msgs.append( msg )
-
-class Check_shortcut_trans( Check ):
- def __init__( self ):
- Check.__init__( self )
- self.diag_header = "-------- shortcut key in translation ---------"
- self.summary_text = "Shortcut in msgstr:"
- def process( self, msg ):
- msgid = msg.msgid
- msgstr = msg.msgstr
-
- if msgid.count('_') == 0 and msgstr.count('_') > 0:
- self.msgs.append( msg )
-
-class Msgid:
- fuzzy_pat = re.compile( 'fuzzy' )
- tips_xml_pat = re.compile( r'tips\.xml' )
- def __init__( self, msgnr, lineno ):
- self._msgid = []
- self._msgstr = []
- self.msgid = ''
- self.msgstr = ''
- self._cmnt = []
- self.nr = msgnr
- self.lineno = lineno
- self.is_fuzzy = 0
- self.is_tips_xml = 0
-
- def diag( self ):
- if 1:
- print
- print "msg nr: %d, lineno: %d%s" % ( self.nr, self.lineno, self.is_fuzzy and " (fuzzy)" or "" )
- sys.stdout.write( ''.join( self._msgid ) )
- sys.stdout.write( ''.join( self._msgstr ) )
- else:
- # Compatible with the old check_po
- print "%d '%s' : '%s'" % ( self.lineno, self.msgid, self.msgstr )
-
- def add_msgid( self, line, lineno ):
- self._msgid.append( line )
- line = re.sub( r'msgid\s+', '', line )
- line = line.strip()
- if line[0] != '"' or line[-1:] != '"':
- print "ERROR at line %d: Missing quote." % lineno
- line = strip_quotes( line )
- self.msgid += line
-
- def add_msgstr( self, line, lineno ):
- self._msgstr.append( line )
- line = re.sub( r'msgstr\s+', '', line )
- line = line.strip()
- if line[0] != '"' or line[-1:] != '"':
- print "ERROR at line %d: Missing quote." % lineno
- line = strip_quotes( line )
- self.msgstr += line
-
- def add_cmnt( self, line ):
- self._cmnt.append( line )
- if not self.is_fuzzy and self.fuzzy_pat.search( line ):
- self.is_fuzzy = 1
- if not self.is_tips_xml and self.tips_xml_pat.search( line ):
- self.is_tips_xml = 1
-
-def read_msgs( fname ):
- empty_pat = re.compile( r'^ \s* $', re.VERBOSE )
- comment_pat = re.compile( r'\#', re.VERBOSE )
- msgid_pat = re.compile( r'msgid \s+ "', re.VERBOSE )
- msgstr_pat = re.compile( r'msgstr \s+ "', re.VERBOSE )
- str_pat = re.compile( r'"', re.VERBOSE )
- old_pat = re.compile( r'\#~ \s+ ', re.VERBOSE )
-
- msgnr = 0 # This is the message number of the next message to read. The first real message is 1.
- f = open( fname )
- lines = f.readlines()
-
- # parse it like a statemachine
- NONE = 0 # Nothing detected, yet
- CMNT = 1 # Inside comment part
- MSGID = 2 # Inside msgid part
- MSGSTR = 3 # Inside msgstr part
- STR = 4 # A continuation string
- OLD = 5 # An old pattern with #~
-
- state = NONE
- msg = None
- msgs = []
-
- for ix in range( len(lines) ): # Use line numbers for messages
- line = lines[ix]
- lineno = ix + 1
-
- m = empty_pat.match( line )
- if m:
- continue # Empty lines are not interesting
-
- # What's the next state?
- if old_pat.match( line ):
- next_state = OLD
- elif comment_pat.match( line ):
- next_state = CMNT
- elif msgid_pat.match( line ):
- next_state = MSGID
- elif msgstr_pat.match( line ):
- next_state = MSGSTR
- elif str_pat.match( line ):
- next_state = STR
- else:
- print 'WARNING: Unexpected input at %(fname)s:%(lineno)d' % vars()
- next_state = NONE
-
- #print "%(state)d->%(next_state)d\t%(line)s" % vars()
- if state == NONE:
- # expect msgid or comment or old stuff
- if next_state == CMNT:
- state = CMNT
- msg = Msgid( msgnr, lineno ) # Start with an empty new item
- msgnr += 1
- msgs.append( msg )
- msg.add_cmnt( line )
-
- elif next_state == MSGID:
- state = MSGID
- msg = Msgid( msgnr, lineno ) # Start with an empty new item
- msgnr += 1
- msgs.append( msg )
- msg.add_msgid( line, lineno )
-
- elif next_state == MSGSTR:
- print 'WARNING: Wild msgstr at %(fname)s:%(lineno)d' % vars()
- state = MSGSTR
- msg = Msgid( msgnr, lineno ) # Start with an empty new item
- msgnr += 1
- msgs.append( msg )
- msg.add_msgstr( line, lineno )
-
- elif next_state == STR:
- print 'WARNING: Wild string at %(fname)s:%(lineno)d' % vars()
-
- elif next_state == OLD:
- pass # Just skip
-
- elif state == CMNT:
- if next_state == CMNT:
- if msg:
- msg.add_cmnt( line )
- else:
- # Note. We may need to do something about these comments
- # Skip for now
- pass
-
- elif next_state == MSGID:
- state = MSGID
- if not msg:
- msg = Msgid( msgnr, lineno ) # Start with an empty new item
- msgnr += 1
- msgs.append( msg )
- msg.add_msgid( line, lineno )
-
- elif next_state == MSGSTR:
- print 'WARNING: Wild msgstr at %(fname)s:%(lineno)d' % vars()
- state = MSGSTR
- msg = Msgid( msgnr, lineno ) # Start with an empty new item
- msgnr += 1
- msgs.append( msg )
- msg.add_msgstr( line, lineno )
-
- elif next_state == STR:
- print 'WARNING: Wild string at %(fname)s:%(lineno)d' % vars()
-
- elif next_state == OLD:
- msg = None
- pass # Just skip
-
- elif state == MSGID:
- if next_state == CMNT:
- # Hmmm. A comment here?
- print 'WARNING: Unexpted comment at %(fname)s:%(lineno)d' % vars()
-
- elif next_state == MSGID:
- raise Exception( 'Unexpected msgid at %(fname)s:%(lineno)d' % vars() )
-
- elif next_state == MSGSTR:
- state = MSGSTR
- msg.add_msgstr( line, lineno )
-
- elif next_state == STR:
- msg.add_msgid( line, lineno )
-
- elif next_state == OLD:
- msg = None
- pass # Just skip
-
- elif state == MSGSTR:
- if next_state == CMNT:
- # A comment probably starts a new item
- state = CMNT
- msg = Msgid( msgnr, lineno )
- msgnr += 1
- msgs.append( msg )
- msg.add_cmnt( line )
-
- elif next_state == MSGID:
- state = MSGID
- msg = Msgid( msgnr, lineno )
- msgnr += 1
- msgs.append( msg )
- msg.add_msgid( line, lineno )
-
- elif next_state == MSGSTR:
- raise Exception( 'Unexpected msgstr at %(fname)s:%(lineno)d' % vars() )
-
- elif next_state == STR:
- msg.add_msgstr( line, lineno )
-
- elif next_state == OLD:
- msg = None
- pass # Just skip
-
- else:
- raise Exception( 'Unexpected state in po parsing (state = %d)' % state )
-
- # Strip items with just comments. (Can this happen?)
- msgs1 = []
- for m in msgs:
- if not m.msgid and not m.msgstr:
- #print "INFO: No msgid or msgstr at %s:%s" % ( fname, m.lineno )
- pass
- else:
- msgs1.append( m )
- msgs = msgs1
- return msgs
-
-def analyze_msgs( options, fname, msgs, nr_templates = None, nth = 0 ):
- nr_fuzzy = 0
- nr_untranslated = 0
-
- checks = []
- checks.append( Check_fmt( '%s' ) )
- checks.append( Check_fmt( '%d' ) )
- checks.append( Check_named_fmt() )
- checks.append( Check_missing_sd() )
- checks.append( Check_runaway() )
- checks.append( Check_xml_chars() )
- checks.append( Check_last_char() )
- checks.append( Check_shortcut_trans() )
-
- for msg in msgs:
- msgid = msg.msgid
- msgstr = msg.msgstr
- #print
- #print "msgid: %(msgid)s" % vars()
- #print "msgstr: %(msgstr)s" % vars()
-
- if not msgstr:
- nr_untranslated += 1
- continue
-
- if msg.is_fuzzy:
- nr_fuzzy += 1
- if options.skip_fuzzy:
- continue
-
- for c in checks:
- c.process( msg )
-
- nr_msgs = len(msgs)
- if nth > 0:
- print
- print "====================================="
- print "%-20s%s" % ( "File:", fname )
- print "%-20s%d" % ( "Template total:", nr_templates )
- print "%-20s%d" % ( "PO total:", nr_msgs )
- print "%-20s%d" % ( "Fuzzy:", nr_fuzzy )
- print "%-20s%d" % ( "Untranslated:", nr_untranslated )
-
- for c in checks:
- c.summary()
-
- po_coverage = (1.0 - (float(nr_untranslated) / float(nr_msgs))) * 100
- print "%-20s%5.2f%%" % ( "PO Coverage:", po_coverage )
-
- template_coverage = po_coverage * float(nr_msgs) / float(nr_templates)
- print "%-20s%5.2f%%" % ( "Template Coverage:", template_coverage )
-
- if not options.only_summary:
- for c in checks:
- c.diag()
-
-def main(args):
- if len(sys.argv) < 2:
- print "Error: Especify the umit.pot file path"
- sys.exit(1)
-
- parser = OptionParser(description="This program validates a PO file for "
- "%s." % APP, usage='%prog [options] po-file...' )
-
- parser.add_option("", "--skip-fuzzy",
- action="store_true", dest="skip_fuzzy", default=False,
- help="skip fuzzies")
-
- parser.add_option("-s", "--only-summary",
- action="store_true", dest="only_summary", default=False,
- help="only give the summary")
-
- options, args = parser.parse_args()
-
- try:
- pot_msgs = read_msgs(sys.argv[1])
- nr_templates = len(pot_msgs)
- nth = 0
- for fname in args:
- msgs = read_msgs(fname)
- analyze_msgs(options, fname, msgs, nr_templates, nth)
- nth += 1
-
- except Exception, e:
- print e
-
-if __name__ == "__main__":
- main(sys.argv)