Permalink
Browse files

Yak-shaving on the localelinter

* can now lint entire directories
* only runs on .po files
* produces stats so it's easier to see problem locales
  • Loading branch information...
1 parent 93a6c6e commit 3b5326be740bd6abd8491ee713f1af377918b876 @willkg willkg committed Feb 28, 2013
Showing with 68 additions and 13 deletions.
  1. +68 −13 scripts/localelinter.py
View
@@ -2,6 +2,7 @@
import itertools
import optparse
+import os
import re
import sys
@@ -14,7 +15,7 @@
sys.exit()
-USAGE = 'usage: %prog FILENAME'
+USAGE = 'usage: %prog [FILE|DIR]'
INTERP_RE = re.compile(
@@ -25,6 +26,14 @@
r')')
+def asciify(thing):
+ if isinstance(thing, basestring):
+ return thing.encode('ascii', 'replace')
+ elif isinstance(thing, (list, tuple)):
+ return [asciify(s) for s in thing]
+ return repr(thing)
+
+
def extract_tokens(msg):
try:
tokens = [token for token in INTERP_RE.findall(msg)]
@@ -53,39 +62,53 @@ def equal(id_tokens, str_tokens):
def verify(msgid, id_text, id_tokens, str_text, str_tokens, index):
- # If the token lists aren't equal and there's a msgstr, then that's
- # a problem. If there's no msgstr, it means it hasn't been translated.
+ # If the token lists aren't equal and there's a msgstr, then
+ # that's a problem. If there's no msgstr, it means it hasn't been
+ # translated.
if not equal(id_tokens, str_tokens) and str_text.strip():
print ('\nError for msgid: {msgid}\n'
'tokens: {id_tokens} VS. {str_tokens}\n'
'{key}: {id_text}\n'
'msgstr{index}: {str_text}'.format(
- index='[{index}]'.format(index=index) if index is not None else '',
- key='id' if index in (None, '0') else 'plural',
- msgid=msgid,
- id_text=id_text,
- id_tokens=', '.join(id_tokens),
- str_text=str_text.encode('ascii', 'replace'),
- str_tokens=', '.join(str_tokens)))
-
+ index='[{index}]'.format(index=index) if index is not None else '',
+ key='id' if index in (None, '0') else 'plural',
+ msgid=asciify(msgid),
+ id_text=asciify(id_text),
+ id_tokens=', '.join(asciify(id_tokens)),
+ str_text=asciify(str_text),
+ str_tokens=', '.join(asciify(str_tokens))))
return False
return True
def verify_file(fname):
+ """Verifies file fname
+
+ This prints to stdout errors it found in fname. It returns the
+ number of errors.
+
+ """
+ if not fname.endswith('.po'):
+ print '{fname} is not a .po file.'.format(fname=fname)
+ return 1
+
+ print 'Working on {fname}'.format(fname=fname)
+
po = polib.pofile(fname)
count = 0
bad_count = 0
+
for entry in po:
if not entry.msgid_plural:
if not entry.msgid and entry.msgstr:
continue
id_tokens = extract_tokens(entry.msgid)
str_tokens = extract_tokens(entry.msgstr)
+
if not verify(entry.msgid, entry.msgid, id_tokens, entry.msgstr,
- str_tokens, None):
+ str_tokens, None):
bad_count += 1
else:
@@ -108,6 +131,34 @@ def verify_file(fname):
'{badcount} possible errors.'.format(
count=count, fname=fname, badcount=bad_count))
+ return bad_count
+
+
+def verify_directory(dir):
+ po_files = {}
+ for root, dirs, files in os.walk(dir):
+ for fn in files:
+ if not fn.endswith('.po'):
+ continue
+
+ fn = os.path.join(root, fn)
+
+ po_files[fn] = verify_file(fn)
+ print '---'
+
+ total_errors = sum(val for key, val in po_files.items())
+ if total_errors == 0:
+ return 0
+
+ print 'Problem locale files:'
+ po_files = sorted([(val, key) for key, val in po_files.items()],
+ reverse=True)
+ for val, key in po_files:
+ if val:
+ print '{val:>5} {key}'.format(key=key, val=val)
+
+ return 1
+
if __name__ == '__main__':
parser = optparse.OptionParser(usage=USAGE)
@@ -117,4 +168,8 @@ def verify_file(fname):
parser.print_help()
sys.exit(1)
- verify_file(args[0])
+ if os.path.isdir(args[0]):
+ sys.exit(verify_directory(args[0]))
+
+ # Return 0 if everything was fine or 1 if there were errors.
+ sys.exit(verify_file(args[0]) != 0)

0 comments on commit 3b5326b

Please sign in to comment.