Blame cldr2json.py

Packit Service ed5168
#!/usr/bin/python3
Packit Service ed5168
#
Packit Service ed5168
# Copyright 2015  Daiki Ueno <dueno@src.gnome.org>
Packit Service ed5168
#           2016  Parag Nemade <pnemade@redhat.com>
Packit Service ed5168
#           2017  Alan <alan@boum.org>
Packit Service ed5168
#
Packit Service ed5168
# This program is free software; you can redistribute it and/or modify
Packit Service ed5168
# it under the terms of the GNU Lesser General Public License as
Packit Service ed5168
# published by the Free Software Foundation; either version 2 of the
Packit Service ed5168
# License, or (at your option) any later version.
Packit Service ed5168
#
Packit Service ed5168
# This program is distributed in the hope that it will be useful, but
Packit Service ed5168
# WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service ed5168
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service ed5168
# Lesser General Public License for more details.
Packit Service ed5168
#
Packit Service ed5168
# You should have received a copy of the GNU Lesser General Public
Packit Service ed5168
# License along with this program; if not, see
Packit Service ed5168
# <http://www.gnu.org/licenses/>.
Packit Service ed5168
Packit Service ed5168
import glob
Packit Service ed5168
import json
Packit Service ed5168
import locale
Packit Service ed5168
import logging
Packit Service ed5168
import os
Packit Service ed5168
import re
Packit Service ed5168
import sys
Packit Service ed5168
import xml.etree.ElementTree
Packit Service ed5168
Packit Service ed5168
import gi
Packit Service ed5168
gi.require_version('GnomeDesktop', '3.0')   # NOQA: E402
Packit Service ed5168
from gi.repository import GnomeDesktop
Packit Service ed5168
Packit Service ed5168
ESCAPE_PATTERN = re.compile(r'\\u\{([0-9A-Fa-f]+?)\}')
Packit Service ed5168
ISO_PATTERN = re.compile(r'[A-E]([0-9]+)')
Packit Service ed5168
Packit Service ed5168
LOCALE_TO_XKB_OVERRIDES = {
Packit Service ed5168
    'af':    'za',
Packit Service ed5168
    'en':    'us',
Packit Service ed5168
    'en-GB': 'uk',
Packit Service ed5168
    'es-US': 'latam',
Packit Service ed5168
    'fr-CA': 'ca',
Packit Service ed5168
    'hi':    'in+bolnagri',
Packit Service ed5168
    'ky':    'kg',
Packit Service ed5168
    'nl-BE': 'be',
Packit Service ed5168
    'zu':    None
Packit Service ed5168
}
Packit Service ed5168
Packit Service ed5168
Packit Service ed5168
def parse_single_key(value):
Packit Service ed5168
    def unescape(m):
Packit Service ed5168
        return chr(int(m.group(1), 16))
Packit Service ed5168
    value = ESCAPE_PATTERN.sub(unescape, value)
Packit Service ed5168
    return value
Packit Service ed5168
Packit Service ed5168
Packit Service ed5168
def parse_rows(keymap):
Packit Service ed5168
    unsorted_rows = {}
Packit Service ed5168
    for _map in keymap.iter('map'):
Packit Service ed5168
        value = _map.get('to')
Packit Service ed5168
        key = [parse_single_key(value)]
Packit Service ed5168
        iso = _map.get('iso')
Packit Service ed5168
        if not ISO_PATTERN.match(iso):
Packit Service ed5168
            sys.stderr.write('invalid ISO key name: %s\n' % iso)
Packit Service ed5168
            continue
Packit Service ed5168
        if not iso[0] in unsorted_rows:
Packit Service ed5168
            unsorted_rows[iso[0]] = []
Packit Service ed5168
        unsorted_rows[iso[0]].append((int(iso[1:]), key))
Packit Service ed5168
        # add subkeys
Packit Service ed5168
        longPress = _map.get('longPress')
Packit Service ed5168
        if longPress:
Packit Service ed5168
            for value in longPress.split(' '):
Packit Service ed5168
                subkey = parse_single_key(value)
Packit Service ed5168
                key.append(subkey)
Packit Service ed5168
Packit Service ed5168
    rows = []
Packit Service ed5168
    for k, v in sorted(list(unsorted_rows.items()),
Packit Service ed5168
                       key=lambda x: x[0],
Packit Service ed5168
                       reverse=True):
Packit Service ed5168
        row = []
Packit Service ed5168
        for key in sorted(v, key=lambda x: x):
Packit Service ed5168
            row.append(key[1])
Packit Service ed5168
        rows.append(row)
Packit Service ed5168
Packit Service ed5168
    return rows
Packit Service ed5168
Packit Service ed5168
Packit Service ed5168
def convert_xml(tree):
Packit Service ed5168
    root = {}
Packit Service ed5168
    for xml_keyboard in tree.iter("keyboard"):
Packit Service ed5168
        locale_full = xml_keyboard.get("locale")
Packit Service ed5168
        locale, sep, end = locale_full.partition("-t-")
Packit Service ed5168
    root["locale"] = locale
Packit Service ed5168
    for xml_name in tree.iter("name"):
Packit Service ed5168
        name = xml_name.get("value")
Packit Service ed5168
    root["name"] = name
Packit Service ed5168
    root["levels"] = []
Packit Service ed5168
    # parse levels
Packit Service ed5168
    for index, keymap in enumerate(tree.iter('keyMap')):
Packit Service ed5168
        # FIXME: heuristics here
Packit Service ed5168
        modifiers = keymap.get('modifiers')
Packit Service ed5168
        if not modifiers:
Packit Service ed5168
            mode = 'default'
Packit Service ed5168
            modifiers = ''
Packit Service ed5168
        elif 'shift' in modifiers.split(' '):
Packit Service ed5168
            mode = 'latched'
Packit Service ed5168
            modifiers = 'shift'
Packit Service ed5168
        else:
Packit Service ed5168
            mode = 'locked'
Packit Service ed5168
        level = {}
Packit Service ed5168
        level["level"] = modifiers
Packit Service ed5168
        level["mode"] = mode
Packit Service ed5168
        level["rows"] = parse_rows(keymap)
Packit Service ed5168
        root["levels"].append(level)
Packit Service ed5168
    return root
Packit Service ed5168
Packit Service ed5168
Packit Service ed5168
def locale_to_xkb(locale, name):
Packit Service ed5168
    if locale in sorted(LOCALE_TO_XKB_OVERRIDES.keys()):
Packit Service ed5168
        xkb = LOCALE_TO_XKB_OVERRIDES[locale]
Packit Service ed5168
        logging.debug("override for %s → %s",
Packit Service ed5168
                      locale, xkb)
Packit Service ed5168
        if xkb:
Packit Service ed5168
            return xkb
Packit Service ed5168
        else:
Packit Service ed5168
            raise KeyError("layout %s explicitely disabled in overrides"
Packit Service ed5168
                           % locale)
Packit Service ed5168
    xkb_names = sorted(name_to_xkb.keys())
Packit Service ed5168
    if name in xkb_names:
Packit Service ed5168
        return name_to_xkb[name]
Packit Service ed5168
    else:
Packit Service ed5168
        logging.debug("name %s failed" % name)
Packit Service ed5168
    for sub_name in name.split(' '):
Packit Service ed5168
        if sub_name in xkb_names:
Packit Service ed5168
            xkb = name_to_xkb[sub_name]
Packit Service ed5168
            logging.debug("dumb mapping failed but match with locale word: "
Packit Service ed5168
                          "%s (%s) → %s (%s)",
Packit Service ed5168
                          locale, name, xkb, sub_name)
Packit Service ed5168
            return xkb
Packit Service ed5168
        else:
Packit Service ed5168
            logging.debug("sub_name failed")
Packit Service ed5168
    for xkb_name in xkb_names:
Packit Service ed5168
        for xkb_sub_name in xkb_name.split(' '):
Packit Service ed5168
            if xkb_sub_name.strip('()') == name:
Packit Service ed5168
                xkb = name_to_xkb[xkb_name]
Packit Service ed5168
                logging.debug("dumb mapping failed but match with xkb word: "
Packit Service ed5168
                              "%s (%s) → %s (%s)",
Packit Service ed5168
                              locale, name, xkb, xkb_name)
Packit Service ed5168
                return xkb
Packit Service ed5168
    raise KeyError("failed to find XKB mapping for %s" % locale)
Packit Service ed5168
Packit Service ed5168
Packit Service ed5168
def convert_file(source_file, destination_path):
Packit Service ed5168
    logging.info("Parsing %s", source_file)
Packit Service ed5168
Packit Service ed5168
    itree = xml.etree.ElementTree.ElementTree()
Packit Service ed5168
    itree.parse(source_file)
Packit Service ed5168
Packit Service ed5168
    root = convert_xml(itree)
Packit Service ed5168
Packit Service ed5168
    try:
Packit Service ed5168
        xkb_name = locale_to_xkb(root["locale"], root["name"])
Packit Service ed5168
    except KeyError as e:
Packit Service ed5168
        logging.warn(e)
Packit Service ed5168
        return False
Packit Service ed5168
    destination_file = os.path.join(destination_path, xkb_name + ".json")
Packit Service ed5168
Packit Service ed5168
    with open(destination_file, 'w', encoding="utf-8") as dest_fd:
Packit Service ed5168
        json.dump(root, dest_fd, ensure_ascii=False, indent=2, sort_keys=True)
Packit Service ed5168
Packit Service ed5168
    logging.debug("written %s", destination_file)
Packit Service ed5168
Packit Service ed5168
Packit Service ed5168
def load_xkb_mappings():
Packit Service ed5168
    xkb = GnomeDesktop.XkbInfo()
Packit Service ed5168
    layouts = xkb.get_all_layouts()
Packit Service ed5168
    name_to_xkb = {}
Packit Service ed5168
Packit Service ed5168
    for layout in layouts:
Packit Service ed5168
        name = xkb.get_layout_info(layout).display_name
Packit Service ed5168
        name_to_xkb[name] = layout
Packit Service ed5168
Packit Service ed5168
    return name_to_xkb
Packit Service ed5168
Packit Service ed5168
Packit Service ed5168
locale.setlocale(locale.LC_ALL, "C")
Packit Service ed5168
name_to_xkb = load_xkb_mappings()
Packit Service ed5168
Packit Service ed5168
Packit Service ed5168
if __name__ == "__main__":
Packit Service ed5168
    if "DEBUG" in os.environ:
Packit Service ed5168
        logging.basicConfig(level=logging.DEBUG)
Packit Service ed5168
Packit Service ed5168
    if len(sys.argv) < 2:
Packit Service ed5168
        print("supply a CLDR keyboard file")
Packit Service ed5168
        sys.exit(1)
Packit Service ed5168
Packit Service ed5168
    if len(sys.argv) < 3:
Packit Service ed5168
        print("supply an output directory")
Packit Service ed5168
        sys.exit(1)
Packit Service ed5168
Packit Service ed5168
    source = sys.argv[1]
Packit Service ed5168
    destination = sys.argv[2]
Packit Service ed5168
    if os.path.isfile(source):
Packit Service ed5168
        convert_file(source, destination)
Packit Service ed5168
    elif os.path.isdir(source):
Packit Service ed5168
        for path in glob.glob(source + "/*-t-k0-android.xml"):
Packit Service ed5168
            convert_file(path, destination)