Bug 6268: Fix check_po for python3

svn: r20933
This commit is contained in:
John Ralls 2013-01-01 23:13:45 +00:00
parent c8708ae3bc
commit edc74447d1

View File

@ -41,558 +41,558 @@ all_coverage = {}
all_template_coverage = {} all_template_coverage = {}
def strip_quotes(st): def strip_quotes(st):
if len(st) >= 2 and st[0] == '"' and st[len(st)-1] == '"': if len(st) >= 2 and st[0] == '"' and st[len(st)-1] == '"':
st = st.strip()[1:-1] st = st.strip()[1:-1]
return st return st
class CheckException( Exception ): class CheckException( Exception ):
pass pass
# This is a base class for all checks # This is a base class for all checks
class Check: class Check:
def __init__( self ): def __init__( self ):
self.msgs = [] self.msgs = []
def diag( self ): def diag( self ):
if len( self.msgs ): if len( self.msgs ):
print print
print self.diag_header print(self.diag_header)
for m in self.msgs: for m in self.msgs:
m.diag() m.diag()
def summary( self ): def summary( self ):
print "%-20s%d" % ( self.summary_text, len(self.msgs) ) print("%-20s%d" % ( self.summary_text, len(self.msgs) ))
class Check_fmt( Check ): class Check_fmt( Check ):
def __init__( self, fmt ): def __init__( self, fmt ):
Check.__init__( self ) Check.__init__( self )
self.diag_header = "-------- %s mismatches --------------" % fmt self.diag_header = "-------- %s mismatches --------------" % fmt
self.summary_text = "%s mismatches:" % fmt self.summary_text = "%s mismatches:" % fmt
self.fmt = fmt self.fmt = fmt
def __process( self, msg, msgid, msgstr ): def __process( self, msg, msgid, msgstr ):
cnt1 = msgid.count( self.fmt ) cnt1 = msgid.count( self.fmt )
cnt2 = msgstr.count( self.fmt ) cnt2 = msgstr.count( self.fmt )
if cnt1 != cnt2: if cnt1 != cnt2:
self.msgs.append( msg ) self.msgs.append( msg )
def process( self, msg ): def process( self, msg ):
msgid = msg.msgid msgid = msg.msgid
msgstr = msg.msgstr[0] msgstr = msg.msgstr[0]
self.__process( msg, msgid, msgstr ) self.__process( msg, msgid, msgstr )
if msg.msgidp and len(msg.msgstr) >= 2: if msg.msgidp and len(msg.msgstr) >= 2:
msgid = msg.msgidp msgid = msg.msgidp
msgstr = msg.msgstr[1] msgstr = msg.msgstr[1]
self.__process( msg, msgid, msgstr ) self.__process( msg, msgid, msgstr )
class Check_named_fmt( Check ): class Check_named_fmt( Check ):
# A pattern to find all %() # A pattern to find all %()
find_named_fmt_pat = re.compile('% \( \w+ \) \d* \D', re.VERBOSE) find_named_fmt_pat = re.compile('% \( \w+ \) \d* \D', re.VERBOSE)
def __init__( self ): def __init__( self ):
Check.__init__( self ) Check.__init__( self )
self.diag_header = "-------- %() name mismatches --------------" self.diag_header = "-------- %() name mismatches --------------"
self.summary_text = "%() name mismatches:" self.summary_text = "%() name mismatches:"
def __process( self, msg, msgid, msgstr ): def __process( self, msg, msgid, msgstr ):
# Same number of named formats? # Same number of named formats?
fmts1 = self.find_named_fmt_pat.findall( msgid ) fmts1 = self.find_named_fmt_pat.findall( msgid )
fmts2 = self.find_named_fmt_pat.findall( msgstr ) fmts2 = self.find_named_fmt_pat.findall( msgstr )
if len( fmts1 ) != len( fmts2 ): if len( fmts1 ) != len( fmts2 ):
self.msgs.append( msg ) self.msgs.append( msg )
else: else:
# Do we have the same named formats? # Do we have the same named formats?
fmts1.sort() fmts1.sort()
fmts2.sort() fmts2.sort()
if fmts1 != fmts2: if fmts1 != fmts2:
self.msgs.append( msg ) self.msgs.append( msg )
def process( self, msg ): def process( self, msg ):
msgid = msg.msgid msgid = msg.msgid
msgstr = msg.msgstr[0] msgstr = msg.msgstr[0]
self.__process( msg, msgid, msgstr ) self.__process( msg, msgid, msgstr )
if msg.msgidp and len(msg.msgstr) >= 2: if msg.msgidp and len(msg.msgstr) >= 2:
msgid = msg.msgidp msgid = msg.msgidp
msgstr = msg.msgstr[1] msgstr = msg.msgstr[1]
self.__process( msg, msgid, msgstr ) self.__process( msg, msgid, msgstr )
class Check_missing_sd( Check ): class Check_missing_sd( Check ):
# A pattern to find %() without s or d # A pattern to find %() without s or d
# Here is a command to use for testing # Here is a command to use for testing
# print re.compile('% \( \w+ \) \d* (\D|$)', re.VERBOSE).findall( '%(event_name)s: %(place)s%(endnotes)s. ' ) # 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) find_named_fmt_pat2 = re.compile('% \( \w+ \) \d* (\D|$)', re.VERBOSE)
def __init__( self ): def __init__( self ):
Check.__init__( self ) Check.__init__( self )
self.diag_header = "-------- %() without 's' or 'd' mismatches --------------" self.diag_header = "-------- %() without 's' or 'd' mismatches --------------"
self.summary_text = "%() missing s/d:" self.summary_text = "%() missing s/d:"
def process( self, msg ): def process( self, msg ):
for msgstr in msg.msgstr: for msgstr in msg.msgstr:
fmts = self.find_named_fmt_pat2.findall( msgstr ) fmts = self.find_named_fmt_pat2.findall( msgstr )
for f in fmts: for f in fmts:
if not f in ('s', 'd'): if not f in ('s', 'd'):
self.msgs.append( msg ) self.msgs.append( msg )
break break
class Check_runaway( Check ): class Check_runaway( Check ):
def __init__( self ): def __init__( self ):
Check.__init__( self ) Check.__init__( self )
self.diag_header = "-------- Runaway context in translation ---------" self.diag_header = "-------- Runaway context in translation ---------"
self.summary_text = "Runaway context:" self.summary_text = "Runaway context:"
def __process( self, msg, msgid, msgstr ): def __process( self, msg, msgid, msgstr ):
# Runaway context. In the translated part we only to see # Runaway context. In the translated part we only to see
# the translation of the word after the | # the translation of the word after the |
if msgid.count('|') > 0 and msgstr.count('|') > 0 and msgid != msgstr: if msgid.count('|') > 0 and msgstr.count('|') > 0 and msgid != msgstr:
self.msgs.append( msg ) self.msgs.append( msg )
def process( self, msg ): def process( self, msg ):
msgid = msg.msgid msgid = msg.msgid
msgstr = msg.msgstr[0] msgstr = msg.msgstr[0]
self.__process( msg, msgid, msgstr ) self.__process( msg, msgid, msgstr )
if msg.msgidp and len(msg.msgstr) >= 2: if msg.msgidp and len(msg.msgstr) >= 2:
msgid = msg.msgidp msgid = msg.msgidp
msgstr = msg.msgstr[1] msgstr = msg.msgstr[1]
self.__process( msg, msgid, msgstr ) self.__process( msg, msgid, msgstr )
class Check_xml_chars( Check ): class Check_xml_chars( Check ):
# Special XML characters # Special XML characters
# It is not allowed to have a quote, an ampersand or an angle bracket # It is not allowed to have a quote, an ampersand or an angle bracket
xml_chars_pat = re.compile( r'<(?!(b>|/b>|i>|/i>|br/>)) | (?<=!(<b|/b|<i|/i|r/))> | " | & (?!(quot|nbsp|gt|amp);)', re.VERBOSE ) xml_chars_pat = re.compile( r'<(?!(b>|/b>|i>|/i>|br/>)) | (?<=!(<b|/b|<i|/i|r/))> | " | & (?!(quot|nbsp|gt|amp);)', re.VERBOSE )
def __init__( self ): def __init__( self ):
Check.__init__( self ) Check.__init__( self )
self.diag_header = "-------- unescaped XML special characters ---------" self.diag_header = "-------- unescaped XML special characters ---------"
self.summary_text = "XML special chars:" self.summary_text = "XML special chars:"
def process( self, msg ): def process( self, msg ):
msgid = msg.msgid msgid = msg.msgid
msgstr = msg.msgstr[0] msgstr = msg.msgstr[0]
# XML errors # XML errors
# Only look at messages in the tips.xml # Only look at messages in the tips.xml
if msg.is_tips_xml: if msg.is_tips_xml:
if self.xml_chars_pat.search( msgstr ): if self.xml_chars_pat.search( msgstr ):
self.msgs.append( msg ) self.msgs.append( msg )
class Check_last_char( Check ): class Check_last_char( Check ):
def __init__( self ): def __init__( self ):
Check.__init__( self ) Check.__init__( self )
self.diag_header = "-------- last character not identical ---------" self.diag_header = "-------- last character not identical ---------"
self.summary_text = "Last character:" self.summary_text = "Last character:"
def __process( self, msg, msgid, msgstr ): def __process( self, msg, msgid, msgstr ):
msgid_last = msgid[-1:] msgid_last = msgid[-1:]
msgstr_last = msgstr[-1:] msgstr_last = msgstr[-1:]
if msgid_last.isspace() != msgstr_last.isspace(): if msgid_last.isspace() != msgstr_last.isspace():
self.msgs.append( msg ) self.msgs.append( msg )
elif (msgid_last == '.') != (msgstr_last == '.'): elif (msgid_last == '.') != (msgstr_last == '.'):
self.msgs.append( msg ) self.msgs.append( msg )
def process( self, msg ): def process( self, msg ):
# Last character of msgid? White space? Period? # Last character of msgid? White space? Period?
if msg.is_fuzzy: if msg.is_fuzzy:
return return
msgid = msg.msgid msgid = msg.msgid
msgstr = msg.msgstr[0] msgstr = msg.msgstr[0]
self.__process( msg, msgid, msgstr ) self.__process( msg, msgid, msgstr )
if msg.msgidp and len(msg.msgstr) >= 2: if msg.msgidp and len(msg.msgstr) >= 2:
msgid = msg.msgidp msgid = msg.msgidp
msgstr = msg.msgstr[1] msgstr = msg.msgstr[1]
self.__process( msg, msgid, msgstr ) self.__process( msg, msgid, msgstr )
class Check_shortcut_trans( Check ): class Check_shortcut_trans( Check ):
def __init__( self ): def __init__( self ):
Check.__init__( self ) Check.__init__( self )
self.diag_header = "-------- shortcut key in translation ---------" self.diag_header = "-------- shortcut key in translation ---------"
self.summary_text = "Shortcut in msgstr:" self.summary_text = "Shortcut in msgstr:"
def __process( self, msg, msgid, msgstr ): def __process( self, msg, msgid, msgstr ):
if msgid.count('_') == 0 and msgstr.count('_') > 0: if msgid.count('_') == 0 and msgstr.count('_') > 0:
self.msgs.append( msg ) self.msgs.append( msg )
def process( self, msg ): def process( self, msg ):
msgid = msg.msgid msgid = msg.msgid
msgstr = msg.msgstr[0] msgstr = msg.msgstr[0]
self.__process( msg, msgid, msgstr ) self.__process( msg, msgid, msgstr )
if msg.msgidp and len(msg.msgstr) >= 2: if msg.msgidp and len(msg.msgstr) >= 2:
msgid = msg.msgidp msgid = msg.msgidp
msgstr = msg.msgstr[1] msgstr = msg.msgstr[1]
self.__process( msg, msgid, msgstr ) self.__process( msg, msgid, msgstr )
class Msgid: class Msgid:
fuzzy_pat = re.compile( 'fuzzy' ) fuzzy_pat = re.compile( 'fuzzy' )
tips_xml_pat = re.compile( r'tips\.xml' ) tips_xml_pat = re.compile( r'tips\.xml' )
def __init__( self, msgnr, lineno ): def __init__( self, msgnr, lineno ):
self._msgid = [] # For debugging purpose the original text self._msgid = [] # For debugging purpose the original text
self._msgidp = [] # For debugging purpose the original text self._msgidp = [] # For debugging purpose the original text
self._msgstr = [] # For debugging purpose the original text self._msgstr = [] # For debugging purpose the original text
self.msgid = '' self.msgid = ''
self.msgidp = '' self.msgidp = ''
self.msgstr = [] # This is a list to support plural self.msgstr = [] # This is a list to support plural
self._cmnt = [] self._cmnt = []
self.nr = msgnr self.nr = msgnr
self.lineno = lineno self.lineno = lineno
self.is_fuzzy = 0 self.is_fuzzy = 0
self.is_tips_xml = 0 self.is_tips_xml = 0
def diag( self ): def diag( self ):
print print
print "msg nr: %d, lineno: %d%s" % ( self.nr, self.lineno, self.is_fuzzy and " (fuzzy)" or "" ) 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._msgid ) )
sys.stdout.write( ''.join( self._msgidp ) ) sys.stdout.write( ''.join( self._msgidp ) )
sys.stdout.write( ''.join( self._msgstr ) ) sys.stdout.write( ''.join( self._msgstr ) )
def add_msgid( self, line, lineno ): def add_msgid( self, line, lineno ):
self._msgid.append( line ) self._msgid.append( line )
line = re.sub( r'msgid\s+', '', line ) line = re.sub( r'msgid\s+', '', line )
line = line.strip() line = line.strip()
if line[0] != '"' or line[-1:] != '"': if line[0] != '"' or line[-1:] != '"':
print "ERROR at line %d: Missing quote." % lineno print("ERROR at line %d: Missing quote." % lineno)
line = strip_quotes( line ) line = strip_quotes( line )
self.msgid += line self.msgid += line
def add_msgidp( self, line, lineno ): def add_msgidp( self, line, lineno ):
self._msgidp.append( line ) self._msgidp.append( line )
line = re.sub( r'msgid_plural\s+', '', line ) line = re.sub( r'msgid_plural\s+', '', line )
line = line.strip() line = line.strip()
if line[0] != '"' or line[-1:] != '"': if line[0] != '"' or line[-1:] != '"':
print "ERROR at line %d: Missing quote." % lineno print("ERROR at line %d: Missing quote." % lineno)
line = strip_quotes( line ) line = strip_quotes( line )
self.msgidp += line self.msgidp += line
def add_new_msgstr( self, line, lineno ): def add_new_msgstr( self, line, lineno ):
self.msgstr.append( '' ) # Start a new msgstr self.msgstr.append( '' ) # Start a new msgstr
self.add_msgstr( line, lineno ) self.add_msgstr( line, lineno )
def add_msgstr( self, line, lineno ): def add_msgstr( self, line, lineno ):
self._msgstr.append( line ) self._msgstr.append( line )
line = re.sub( r'msgstr(\[\d\])?\s+', '', line ) line = re.sub( r'msgstr(\[\d\])?\s+', '', line )
line = line.strip() line = line.strip()
if line[0] != '"' or line[-1:] != '"': if line[0] != '"' or line[-1:] != '"':
print "ERROR at line %d: Missing quote." % lineno print("ERROR at line %d: Missing quote." % lineno)
line = strip_quotes( line ) line = strip_quotes( line )
self.msgstr[-1] += line self.msgstr[-1] += line
def add_cmnt( self, line ): def add_cmnt( self, line ):
self._cmnt.append( line ) self._cmnt.append( line )
if not self.is_fuzzy and self.fuzzy_pat.search( line ): if not self.is_fuzzy and self.fuzzy_pat.search( line ):
self.is_fuzzy = 1 self.is_fuzzy = 1
if not self.is_tips_xml and self.tips_xml_pat.search( line ): if not self.is_tips_xml and self.tips_xml_pat.search( line ):
self.is_tips_xml = 1 self.is_tips_xml = 1
def create_new_Msgid( msgs, lineno ): def create_new_Msgid( msgs, lineno ):
msg = Msgid( len(msgs), lineno ) msg = Msgid( len(msgs), lineno )
msgs.append( msg ) msgs.append( msg )
return msg return msg
def read_msgs( fname ): def read_msgs( fname ):
empty_pat = re.compile( r'^ \s* $', re.VERBOSE ) empty_pat = re.compile( r'^ \s* $', re.VERBOSE )
comment_pat = re.compile( r'\#', re.VERBOSE ) comment_pat = re.compile( r'\#', re.VERBOSE )
msgid_pat = re.compile( r'msgid \s+ "', re.VERBOSE ) msgid_pat = re.compile( r'msgid \s+ "', re.VERBOSE )
msgid_plural_pat = re.compile( r'msgid_plural \s+ "', re.VERBOSE ) msgid_plural_pat = re.compile( r'msgid_plural \s+ "', re.VERBOSE )
msgstr_pat = re.compile( r'msgstr (\[\d\])? \s+ "', re.VERBOSE ) msgstr_pat = re.compile( r'msgstr (\[\d\])? \s+ "', re.VERBOSE )
str_pat = re.compile( r'"', re.VERBOSE ) str_pat = re.compile( r'"', re.VERBOSE )
old_pat = re.compile( r'\#~ \s+ ', re.VERBOSE ) old_pat = re.compile( r'\#~ \s+ ', re.VERBOSE )
f = open( fname ) f = open( fname )
lines = f.readlines() lines = f.readlines()
# parse it like a statemachine
NONE = 'NONE' # Nothing detected, yet
CMNT = 'CMNT' # Inside comment part
MSGID = 'msgid' # Inside msgid part
MSGIDP = 'msgid_plural' # Inside msgid_plural part
MSGSTR = 'msgstr' # Inside msgstr part
STR = 'STR' # A continuation string
OLD = 'OLD' # An old pattern with #~
global msgs # parse it like a statemachine
state = NONE NONE = 'NONE' # Nothing detected, yet
msg = None CMNT = 'CMNT' # Inside comment part
MSGID = 'msgid' # Inside msgid part
MSGIDP = 'msgid_plural' # Inside msgid_plural part
MSGSTR = 'msgstr' # Inside msgstr part
STR = 'STR' # A continuation string
OLD = 'OLD' # An old pattern with #~
msgs = [] global msgs
for ix, line in enumerate( lines ): # Use line numbers for messages state = NONE
lineno = ix + 1 msg = None
m = empty_pat.match( line ) msgs = []
if m: for ix, line in enumerate( lines ): # Use line numbers for messages
continue # Empty lines are not interesting lineno = ix + 1
# What's the next state? m = empty_pat.match( line )
if old_pat.match( line ): if m:
next_state = OLD continue # Empty lines are not interesting
elif comment_pat.match( line ):
next_state = CMNT
elif msgid_pat.match( line ):
next_state = MSGID
elif msgid_plural_pat.match( line ):
next_state = MSGIDP
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() # What's the next state?
if state == NONE: if old_pat.match( line ):
# expect msgid or comment or old stuff next_state = OLD
if next_state == CMNT: elif comment_pat.match( line ):
state = CMNT next_state = CMNT
msg = create_new_Msgid( msgs, lineno ) # Start with an empty new item elif msgid_pat.match( line ):
msg.add_cmnt( line ) next_state = MSGID
elif msgid_plural_pat.match( line ):
next_state = MSGIDP
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
elif next_state == MSGID: #print("%(state)d->%(next_state)d\t%(line)s" % vars())
state = MSGID if state == NONE:
msg = create_new_Msgid( msgs, lineno ) # Start with an empty new item # expect msgid or comment or old stuff
msg.add_msgid( line, lineno ) if next_state == CMNT:
state = CMNT
msg = create_new_Msgid( msgs, lineno ) # Start with an empty new item
msg.add_cmnt( line )
elif next_state == MSGIDP: elif next_state == MSGID:
raise CheckException( 'Unexpected %(next_state)s at %(fname)s:%(lineno)d' % vars() ) state = MSGID
msg = create_new_Msgid( msgs, lineno ) # Start with an empty new item
msg.add_msgid( line, lineno )
elif next_state == MSGSTR: elif next_state == MSGIDP:
print 'WARNING: Wild msgstr at %(fname)s:%(lineno)d' % vars() raise CheckException( 'Unexpected %(next_state)s at %(fname)s:%(lineno)d' % vars() )
state = MSGSTR
msg = create_new_Msgid( msgs, lineno ) # Start with an empty new item
msg.add_new_msgstr( line, lineno )
elif next_state == STR: elif next_state == MSGSTR:
print 'WARNING: Wild string at %(fname)s:%(lineno)d' % vars() print('WARNING: Wild msgstr at %(fname)s:%(lineno)d' % vars())
state = MSGSTR
msg = create_new_Msgid( msgs, lineno ) # Start with an empty new item
msg.add_new_msgstr( line, lineno )
elif next_state == OLD: elif next_state == STR:
pass # Just skip print('WARNING: Wild string at %(fname)s:%(lineno)d' % vars())
else: elif next_state == OLD:
raise CheckException( 'Unexpected state in po parsing (state = %(state)s)' % vars() ) pass # Just skip
elif state == CMNT: else:
# Expect more comment, or msgid. If msgstr or string it is flagged as error. raise CheckException( 'Unexpected state in po parsing (state = %(state)s)' % vars() )
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: elif state == CMNT:
state = MSGID # Expect more comment, or msgid. If msgstr or string it is flagged as error.
if not msg: if next_state == CMNT:
msg = create_new_Msgid( msgs, lineno ) # Start with an empty new item if msg:
msg.add_msgid( line, lineno ) msg.add_cmnt( line )
else:
# Note. We may need to do something about these comments
# Skip for now
pass
elif next_state == MSGIDP: elif next_state == MSGID:
raise CheckException( 'Unexpected %(next_state)s at %(fname)s:%(lineno)d' % vars() ) state = MSGID
if not msg:
msg = create_new_Msgid( msgs, lineno ) # Start with an empty new item
msg.add_msgid( line, lineno )
elif next_state == MSGSTR: elif next_state == MSGIDP:
print 'WARNING: Wild msgstr at %(fname)s:%(lineno)d' % vars() raise CheckException( 'Unexpected %(next_state)s at %(fname)s:%(lineno)d' % vars() )
state = MSGSTR
msg = create_new_Msgid( msgs, lineno ) # Start with an empty new item
msg.add_new_msgstr( line, lineno )
elif next_state == STR: elif next_state == MSGSTR:
print 'WARNING: Wild string at %(fname)s:%(lineno)d' % vars() print('WARNING: Wild msgstr at %(fname)s:%(lineno)d' % vars())
state = MSGSTR
msg = create_new_Msgid( msgs, lineno ) # Start with an empty new item
msg.add_new_msgstr( line, lineno )
elif next_state == OLD: elif next_state == STR:
msg = None print('WARNING: Wild string at %(fname)s:%(lineno)d' % vars())
pass # Just skip
else: elif next_state == OLD:
raise CheckException( 'Unexpected state in po parsing (state = %(state)s)' % vars() ) msg = None
pass # Just skip
elif state == MSGID: else:
# Expect msgstr or msgid_plural or string raise CheckException( 'Unexpected state in po parsing (state = %(state)s)' % vars() )
if next_state == CMNT:
# Hmmm. A comment here?
print 'WARNING: Unexpted comment at %(fname)s:%(lineno)d' % vars()
elif next_state == MSGID: elif state == MSGID:
raise CheckException( 'Unexpected %(next_state)s at %(fname)s:%(lineno)d' % vars() ) # Expect msgstr or msgid_plural or string
if next_state == CMNT:
# Hmmm. A comment here?
print('WARNING: Unexpted comment at %(fname)s:%(lineno)d' % vars())
elif next_state == MSGIDP: elif next_state == MSGID:
state = MSGIDP raise CheckException( 'Unexpected %(next_state)s at %(fname)s:%(lineno)d' % vars() )
msg.add_msgidp( line, lineno )
elif next_state == MSGSTR: elif next_state == MSGIDP:
state = MSGSTR state = MSGIDP
msg.add_new_msgstr( line, lineno ) msg.add_msgidp( line, lineno )
elif next_state == STR: elif next_state == MSGSTR:
# Continuation of msgid, stay in state MSGID state = MSGSTR
msg.add_msgid( line, lineno ) msg.add_new_msgstr( line, lineno )
elif next_state == OLD: elif next_state == STR:
msg = None # Continuation of msgid, stay in state MSGID
pass # Just skip msg.add_msgid( line, lineno )
else: elif next_state == OLD:
raise CheckException( 'Unexpected state in po parsing (state = %(state)s)' % vars() ) msg = None
pass # Just skip
elif state == MSGIDP: else:
# Expect msgstr or string or comment raise CheckException( 'Unexpected state in po parsing (state = %(state)s)' % vars() )
if next_state == CMNT:
# Hmmm. A comment here?
print 'WARNING: Unexpted comment at %(fname)s:%(lineno)d' % vars()
elif next_state == MSGID: elif state == MSGIDP:
raise CheckException( 'Unexpected %(next_state)s at %(fname)s:%(lineno)d' % vars() ) # Expect msgstr or string or comment
if next_state == CMNT:
# Hmmm. A comment here?
print('WARNING: Unexpted comment at %(fname)s:%(lineno)d' % vars())
elif next_state == MSGIDP: elif next_state == MSGID:
raise CheckException( 'Unexpected %(next_state)s at %(fname)s:%(lineno)d' % vars() ) raise CheckException( 'Unexpected %(next_state)s at %(fname)s:%(lineno)d' % vars() )
elif next_state == MSGSTR: elif next_state == MSGIDP:
state = MSGSTR raise CheckException( 'Unexpected %(next_state)s at %(fname)s:%(lineno)d' % vars() )
msg.add_new_msgstr( line, lineno )
elif next_state == STR: elif next_state == MSGSTR:
# Continuation of msgid_plural, stay in state MSGIDP state = MSGSTR
msg.add_msgidp( line, lineno ) msg.add_new_msgstr( line, lineno )
elif next_state == OLD: elif next_state == STR:
msg = None # Continuation of msgid_plural, stay in state MSGIDP
pass # Just skip msg.add_msgidp( line, lineno )
else: elif next_state == OLD:
raise CheckException( 'Unexpected state in po parsing (state = %(state)s)' % vars() ) msg = None
pass # Just skip
elif state == MSGSTR: else:
# Expect comment, or msgid, or string. raise CheckException( 'Unexpected state in po parsing (state = %(state)s)' % vars() )
if next_state == CMNT:
# A comment probably starts a new item
state = CMNT
msg = create_new_Msgid( msgs, lineno )
msg.add_cmnt( line )
elif next_state == MSGID: elif state == MSGSTR:
state = MSGID # Expect comment, or msgid, or string.
msg = create_new_Msgid( msgs, lineno ) if next_state == CMNT:
msg.add_msgid( line, lineno ) # A comment probably starts a new item
state = CMNT
msg = create_new_Msgid( msgs, lineno )
msg.add_cmnt( line )
elif next_state == MSGIDP: elif next_state == MSGID:
raise CheckException( 'Unexpected %(next_state)s at %(fname)s:%(lineno)d' % vars() ) state = MSGID
msg = create_new_Msgid( msgs, lineno )
msg.add_msgid( line, lineno )
elif next_state == MSGSTR: elif next_state == MSGIDP:
# New msgstr, probably for plural form raise CheckException( 'Unexpected %(next_state)s at %(fname)s:%(lineno)d' % vars() )
# Stay in MSGSTR state
msg.add_new_msgstr( line, lineno )
elif next_state == STR: elif next_state == MSGSTR:
msg.add_msgstr( line, lineno ) # New msgstr, probably for plural form
# Stay in MSGSTR state
msg.add_new_msgstr( line, lineno )
elif next_state == OLD: elif next_state == STR:
msg = None msg.add_msgstr( line, lineno )
pass # Just skip
else: elif next_state == OLD:
raise CheckException( 'Unexpected state in po parsing (state = %(state)s)' % vars() ) msg = None
pass # Just skip
else: else:
raise CheckException( 'Unexpected state in po parsing (state = %(state)s)' % vars() ) raise CheckException( 'Unexpected state in po parsing (state = %(state)s)' % vars() )
# Strip items with just comments. (Can this happen?) else:
msgs1 = [] raise CheckException( 'Unexpected state in po parsing (state = %(state)s)' % vars() )
for m in msgs:
if not m.msgid and not m.msgstr: # Strip items with just comments. (Can this happen?)
#print "INFO: No msgid or msgstr at %s:%s" % ( fname, m.lineno ) msgs1 = []
pass for m in msgs:
else: if not m.msgid and not m.msgstr:
msgs1.append( m ) #print("INFO: No msgid or msgstr at %s:%s" % ( fname, m.lineno ))
msgs = msgs1 pass
return msgs else:
msgs1.append( m )
msgs = msgs1
return msgs
def analyze_msgs( args, fname, msgs, nr_templates = None, nth = 0 ): def analyze_msgs( args, fname, msgs, nr_templates = None, nth = 0 ):
nr_fuzzy = 0 nr_fuzzy = 0
nr_untranslated = 0 nr_untranslated = 0
checks = [] checks = []
checks.append( Check_fmt( '%s' ) ) checks.append( Check_fmt( '%s' ) )
checks.append( Check_fmt( '%d' ) ) checks.append( Check_fmt( '%d' ) )
checks.append( Check_named_fmt() ) checks.append( Check_named_fmt() )
checks.append( Check_missing_sd() ) checks.append( Check_missing_sd() )
checks.append( Check_runaway() ) checks.append( Check_runaway() )
checks.append( Check_xml_chars() ) checks.append( Check_xml_chars() )
checks.append( Check_last_char() ) checks.append( Check_last_char() )
checks.append( Check_shortcut_trans() ) checks.append( Check_shortcut_trans() )
for msg in msgs: for msg in msgs:
msgid = msg.msgid msgid = msg.msgid
msgstr = msg.msgstr msgstr = msg.msgstr
#print #print
#print "msgid: %(msgid)s" % vars() #print("msgid: %(msgid)s" % vars())
#print "msgstr: %(msgstr)s" % vars() #print("msgstr: %(msgstr)s" % vars())
if ''.join(msgstr) == '': if ''.join(msgstr) == '':
nr_untranslated += 1 nr_untranslated += 1
continue continue
if msg.is_fuzzy: if msg.is_fuzzy:
nr_fuzzy += 1 nr_fuzzy += 1
continue continue
for c in checks: for c in checks:
c.process( msg ) c.process( msg )
nr_msgs = len(msgs) nr_msgs = len(msgs)
if nth > 0: if nth > 0:
print print
print "=====================================" print("=====================================")
print "%-20s%s" % ( "File:", fname ) print("%-20s%s" % ( "File:", fname ))
print "%-20s%d" % ( "Template total:", nr_templates ) print("%-20s%d" % ( "Template total:", nr_templates ))
print "%-20s%d" % ( "PO total:", nr_msgs ) print("%-20s%d" % ( "PO total:", nr_msgs ))
print "%-20s%d" % ( "Fuzzy:", nr_fuzzy ) print("%-20s%d" % ( "Fuzzy:", nr_fuzzy ))
print "%-20s%d" % ( "Untranslated:", nr_untranslated ) print("%-20s%d" % ( "Untranslated:", nr_untranslated ))
for c in checks: for c in checks:
c.summary() c.summary()
po_coverage = (1.0 - (float(nr_untranslated) / float(nr_msgs))) * 100 po_coverage = (1.0 - (float(nr_untranslated) / float(nr_msgs))) * 100
print "%-20s%5.2f%%" % ( "PO Coverage:", po_coverage ) print("%-20s%5.2f%%" % ( "PO Coverage:", po_coverage ))
template_coverage = po_coverage * float(nr_msgs) / float(nr_templates) template_coverage = po_coverage * float(nr_msgs) / float(nr_templates)
print "%-20s%5.2f%%" % ( "Template Coverage:", template_coverage ) print("%-20s%5.2f%%" % ( "Template Coverage:", template_coverage ))
not_displayed = nr_untranslated + nr_fuzzy not_displayed = nr_untranslated + nr_fuzzy
translation = (1.0 - (float(not_displayed) / float(nr_templates))) * 100 translation = (1.0 - (float(not_displayed) / float(nr_templates))) * 100
text = "%-20s%5.2f%%" % ( "Localized at:", translation) text = "%-20s%5.2f%%" % ( "Localized at:", translation)
if int(template_coverage*1000) == int(po_coverage*1000): if int(template_coverage*1000) == int(po_coverage*1000):
print text print(text)
else: else:
print text + ' (previous gramps.pot)' print(text + ' (previous gramps.pot)')
for c in checks: for c in checks:
c.diag() c.diag()
def main(): def main():
parser = ArgumentParser( description='This program validates a PO file for GRAMPS.') parser = ArgumentParser( description='This program validates a PO file for GRAMPS.')
parser.add_argument("-s", dest="summary", parser.add_argument("-s", dest="summary",
choices=[file for file in os.listdir('.') if file.endswith('.po')], choices=[file for file in os.listdir('.') if file.endswith('.po')],
default=False, help="the summary of check, and if need, it gives details") default=False, help="the summary of check, and if need, it gives details")
args = parser.parse_args() args = parser.parse_args()
if args.summary: if args.summary:
files = sys.argv[2:] files = sys.argv[2:]
try: try:
pot_msgs = read_msgs( 'gramps.pot' ) pot_msgs = read_msgs( 'gramps.pot' )
nr_templates = len( pot_msgs ) nr_templates = len( pot_msgs )
nth = 0 nth = 0
for fname in files: for fname in files:
msgs = read_msgs( fname ) msgs = read_msgs( fname )
analyze_msgs( files, fname, msgs, nr_templates, nth ) analyze_msgs( files, fname, msgs, nr_templates, nth )
nth += 1 nth += 1
except CheckException, e: except CheckException as e:
print 'Oops.', e print('Oops.', e)
print 'Bailing out' print('Bailing out')
if __name__ == "__main__": if __name__ == "__main__":
main() main()