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-l... + 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" "$@" {} +
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.
This looks fine.
- Chris
anaconda-patches@lists.fedorahosted.org