[PATCH] Add translation support to check_accelerators
David Shea
dshea at redhat.com
Mon Sep 23 19:01:24 UTC 2013
Added a --translate option to check_accelerators.py. When set, it will
check all of the translated keyboard accelerators in addition to the
English ones. Changed the exception check so that language information
can be included as <!-- check_accelerators(<lang>): comment -->. Added
support for the new parameter to the run_check_accelerators.sh wrapper
script.
`make check' will not check translations.
Requires python-polib to parse the .po files.
---
tests/accelerators/check_accelerators.py | 127 +++++++++++++++++++++++----
tests/accelerators/run_check_accelerators.sh | 18 +++-
2 files changed, 126 insertions(+), 19 deletions(-)
diff --git a/tests/accelerators/check_accelerators.py b/tests/accelerators/check_accelerators.py
index 892ff2b..461220a 100755
--- a/tests/accelerators/check_accelerators.py
+++ b/tests/accelerators/check_accelerators.py
@@ -22,6 +22,8 @@ import argparse
import re
import os.path
import copy
+import collections
+import locale
try:
from lxml import etree
@@ -30,33 +32,80 @@ except ImportError:
sys.exit(1)
accel_re = re.compile(r'_(?P<accel>.)')
-
success = True
-def add_check_accel(glade_filename, accels, label):
+
+# Only used when --translate is requested.
+class PODict(collections.Mapping):
+ def __init__(self, filename):
+ try:
+ import polib
+ except ImportError:
+ print("You need to install the python-polib package to check translations")
+ sys.exit(1)
+
+ self._dict = {}
+
+ pofile = polib.pofile(filename)
+ self.metadata = pofile.metadata
+ for entry in pofile.translated_entries():
+ self._dict[entry.msgid] = entry.msgstr
+
+ def __getitem__(self, key):
+ return self._dict[key]
+
+ def __iter__(self):
+ return self._dict.__iter__()
+
+ def __len__(self):
+ return len(self._dict)
+
+def is_exception(node, language=None):
+ if language:
+ comment_str = "check_accelerators(%s)" % language
+ else:
+ comment_str = "check_accelerators"
+
+ return bool(node.xpath("./parent::*/comment()[contains(., '%s')]" % comment_str))
+
+def add_check_accel(glade_filename, accels, label, po_map):
"""Check whether an accelerator conflicts with existing accelerators.
and add it to the current accelerator context.
"""
global success
+ language = None
+ if po_map:
+ if label.text not in po_map:
+ return
+ label.text = po_map[label.text]
+ language = po_map.metadata['Language']
+
match = accel_re.search(label.text)
if match:
accel = match.group('accel').lower()
if accel in accels:
# Check for an exception comment
- prev = label.getprevious()
- if (prev is not None) and (prev.tag == etree.Comment) and \
- prev.text.strip().startswith('check_accelerators:'):
+ if is_exception(label, language):
return
- print("Accelerator collision for key %s in %s\n line %d: %s\n line %d: %s" %\
- (accel, os.path.normpath(glade_filename),
- accels[accel].sourceline, accels[accel].text,
- label.sourceline, label.text))
+ if language:
+ lang_str = " for language %s" % language
+ else:
+ lang_str = ""
+
+ try:
+ print(("Accelerator collision for key %s in %s%s\n line %d: %s\n line %d: %s" %\
+ (accel, os.path.normpath(glade_filename), lang_str,
+ accels[accel].sourceline, accels[accel].text,
+ label.sourceline, label.text)).encode("utf-8"))
+ except UnicodeEncodeError:
+ import pdb; pdb.set_trace()
+ raise
success = False
else:
accels[accel] = label
-def combine_accels(glade_filename, list_a, list_b):
+def combine_accels(glade_filename, list_a, list_b, po_map):
if not list_a:
return list_b
if not list_b:
@@ -67,7 +116,7 @@ def combine_accels(glade_filename, list_a, list_b):
for accels_b in list_b:
new_accels = copy.copy(accels_a)
for accel in accels_b.keys():
- add_check_accel(glade_filename, new_accels, accels_b[accel])
+ add_check_accel(glade_filename, new_accels, accels_b[accel], po_map)
newlist.append(new_accels)
return newlist
@@ -80,7 +129,7 @@ def combine_accels(glade_filename, list_a, list_b):
# these is compared against the list of accelerators returned for the object's
# other GtkNotebook children.
-def process_object(glade_filename, interface_object):
+def process_object(glade_filename, interface_object, po_map):
"""Process keyboard shortcuts for a given glade object.
The return value from this function is a list of accelerator
@@ -94,12 +143,12 @@ def process_object(glade_filename, interface_object):
# Add everything that isn't a child of a GtkNotebook
for label in interface_object.xpath(".//property[@name='label' and ../property[@name='use_underline']/text() = 'True' and not(ancestor::object[@class='GtkNotebook'])]"):
- add_check_accel(glade_filename, accels[0], label)
+ add_check_accel(glade_filename, accels[0], label, po_map)
# For each GtkNotebook tab that is not a child of another notebook,
# add the tab to the top-level context
for notebook_label in interface_object.xpath(".//object[@class='GtkNotebook' and not(ancestor::object[@class='GtkNotebook'])]/child[@type='tab']//property[@name='label' and ../property[@name='use_underline']/text() = 'True']"):
- add_check_accel(glade_filename, accels[0], notebook_label)
+ add_check_accel(glade_filename, accels[0], notebook_label, po_map)
# Now process each non-tab object in each Gtknotebook that is not a child
# of another notebook. For each Gtk notebook, each non-tab child represents
@@ -129,34 +178,76 @@ def process_object(glade_filename, interface_object):
# Create the list of dictionaries for the notebook
notebook_list = []
for child in notebook.xpath("./child[not(@type='tab')]"):
- notebook_list.extend(process_object(glade_filename, copy.deepcopy(child)))
+ notebook_list.extend(process_object(glade_filename, copy.deepcopy(child), po_map))
# Now combine this with our list of accelerators
- accels = combine_accels(glade_filename, accels, notebook_list)
+ accels = combine_accels(glade_filename, accels, notebook_list, po_map)
return accels
-def check_glade(glade_filename):
+def check_glade(glade_filename, po_map=None):
with open(glade_filename) as glade_file:
# Parse the XML
glade_tree = etree.parse(glade_file)
# Treat each top-level object as a separate context
for interface_object in glade_tree.xpath("/interface/object"):
- process_object(glade_filename, interface_object)
+ process_object(glade_filename, interface_object, po_map)
def main(argv=None):
if argv is None:
argv = sys.argv
parser = argparse.ArgumentParser("Check for duplicated accelerators")
+ parser.add_argument("-t", "--translate", action='store_true',
+ help="Check translated strings")
+ parser.add_argument("-p", "--podir", action='store', type=str,
+ metavar='PODIR', help='Directory containing po files', default='./po')
parser.add_argument("glade_files", nargs="+", metavar="GLADE-FILE",
help='The glade file to check')
args = parser.parse_args(args=argv)
+ # First check the untranslated strings in each file
for glade_file in args.glade_files:
check_glade(glade_file)
+ # Now loop over all of the translations
+ if args.translate:
+ import langtable
+
+ with open(os.path.join(args.podir, 'LINGUAS')) as linguas:
+ for line in linguas.readlines():
+ if re.match(r'^#', line):
+ continue
+
+ for lang in line.strip().split(" "):
+ # Reset the locale to C before parsing the po file because
+ # polib has erroneous uses of lower().
+ # See https://bitbucket.org/izi/polib/issue/54/pofile-parsing-crashes-in-turkish-locale
+ locale.setlocale(locale.LC_ALL, 'C')
+ po_map = PODict(os.path.join(args.podir, lang + ".po"))
+
+ # Set the locale so that we can use lower() on accelerator keys.
+ # If the language is of the form xx_XX, use that as the
+ # locale name. Otherwise use the first locale that
+ # langtable returns for the language. If that doesn't work,
+ # just use C and hope for the best.
+ if '_' in lang:
+ locale.setlocale(locale.LC_ALL, lang)
+ else:
+ locale_list = langtable.list_locales(languageId=lang)
+ if locale_list:
+ try:
+ locale.setlocale(locale.LC_ALL, locale_list[0])
+ except locale.Error:
+ print("No such locale %s, using C" % locale_list[0])
+ locale.setlocale(locale.LC_ALL, 'C')
+ else:
+ locale.setlocale(locale.LC_ALL, 'C')
+
+ for glade_file in args.glade_files:
+ check_glade(glade_file, po_map)
+
if __name__ == "__main__":
main(sys.argv[1:])
diff --git a/tests/accelerators/run_check_accelerators.sh b/tests/accelerators/run_check_accelerators.sh
index a49cda1..401f081 100755
--- a/tests/accelerators/run_check_accelerators.sh
+++ b/tests/accelerators/run_check_accelerators.sh
@@ -3,4 +3,20 @@
: "${top_srcdir:=$(dirname "$0")/../..}"
srcdir="${top_srcdir}/tests/accelerators"
-find "${top_srcdir}" -name '*.glade' -exec "${srcdir}/check_accelerators.py" {} +
+# If --translate was specified but not --podir, add --podir
+i=0
+translate_set=0
+podir_set=0
+for arg in "$@" ; do
+ if [ "$arg" = "--translate" -o "$arg" = "-t" ]; then
+ translate_set=1
+ elif [ "$arg" = "--podir" -o "$arg" = "-p" ]; then
+ podir_set=1
+ fi
+done
+
+if [ "$translate_set" -eq 1 -a "$podir_set" -eq 0 ]; then
+ set -- "$@" --podir "${top_srcdir}/po"
+fi
+
+find "${top_srcdir}" -name '*.glade' -exec "${srcdir}/check_accelerators.py" "$@" {} +
--
1.8.3.1
More information about the anaconda-patches
mailing list