summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaiki Ueno <dueno@src.gnome.org>2015-01-17 11:28:50 +0900
committerDaiki Ueno <dueno@src.gnome.org>2015-01-18 02:58:48 +0900
commit661175c3f49d7857aa6c29e43d88766553f39c60 (patch)
treecf222469e73fa1f8071ef298417e5ef77f5bb20b
parentea59738334de0afcc5a03228614a4be4d9423f35 (diff)
downloadcaribou-661175c3f49d7857aa6c29e43d88766553f39c60.tar.gz
tools: Add converter from CLDR keyboard data
The convert_cldr.py script reads a CLDR keyboard data and produces a Caribou layout file. It can be used as: ./tools/convert_cldr.py fr-t-k0-android-qwertz.xml > fr.xml For simplicity, the script adds only the keys defined in the input file. To make the generated layout really useful, you will need to add 'Caribou_ShiftUp' etc.
-rw-r--r--tools/Makefile.am1
-rwxr-xr-xtools/convert_cldr.py111
2 files changed, 112 insertions, 0 deletions
diff --git a/tools/Makefile.am b/tools/Makefile.am
index ba84708..3a9bcdb 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -1,6 +1,7 @@
EXTRA_DIST = \
fix_gir.py \
make_schema.py \
+ convert_cldr.py \
basic.xsl
-include $(top_srcdir)/git.mk
diff --git a/tools/convert_cldr.py b/tools/convert_cldr.py
new file mode 100755
index 0000000..cd65754
--- /dev/null
+++ b/tools/convert_cldr.py
@@ -0,0 +1,111 @@
+#!/usr/bin/python
+
+from gi.repository import Gdk
+from xml.etree.ElementTree import ElementTree, Element
+import re
+
+ESCAPE_PATTERN = re.compile(r'\\u\{([0-9A-Fa-f]+?)\}')
+ISO_PATTERN = re.compile(r'[A-E]([0-9]+)')
+
+
+def parse_single_key(value):
+ key = Element('key')
+ uc = 0
+ if hasattr(__builtins__, 'unichr'):
+ def unescape(m):
+ return unichr(int(m.group(1), 16))
+ else:
+ def unescape(m):
+ return chr(int(m.group(1), 16))
+ value = ESCAPE_PATTERN.sub(unescape, value)
+ if len(value) > 1:
+ key.set('text', value)
+ uc = ord(value[0])
+ keyval = Gdk.unicode_to_keyval(uc)
+ name = Gdk.keyval_name(keyval)
+ key.set('name', name)
+ return key
+
+
+def convert(source, tree):
+ root = Element('layout')
+ for index, keymap in enumerate(tree.iter('keyMap')):
+ level = Element('level')
+ rows = {}
+ root.append(level)
+ level.set('name', 'level%d' % (index+1))
+ # FIXME: heuristics here
+ modifiers = keymap.get('modifiers')
+ if not modifiers:
+ mode = 'default'
+ elif 'shift' in modifiers.split(' ') or 'lock' in modifiers.split(' '):
+ mode = 'latched'
+ else:
+ mode = 'locked'
+ level.set('mode', mode)
+ for _map in keymap.iter('map'):
+ value = _map.get('to')
+ key = parse_single_key(value)
+ iso = _map.get('iso')
+ if not ISO_PATTERN.match(iso):
+ sys.stderr.write('invalid ISO key name: %s\n' % iso)
+ continue
+ if not iso[0] in rows:
+ rows[iso[0]] = []
+ rows[iso[0]].append((int(iso[1:]), key))
+ # add attribute to certain keys
+ name = key.get('name')
+ if name == 'space':
+ key.set('align', 'center')
+ key.set('width', '6.0')
+ if name in ('space', 'BackSpace'):
+ key.set('repeatable', 'yes')
+ # add subkeys
+ longPress = _map.get('longPress')
+ if longPress:
+ for value in longPress.split(' '):
+ subkey = parse_single_key(value)
+ key.append(subkey)
+ for k, v in sorted(rows.items(), key=lambda x: x[0], reverse=True):
+ row = Element('row')
+ for key in sorted(v, key=lambda x: x):
+ row.append(key[1])
+ level.append(row)
+ return root
+
+
+def indent(elem, level=0):
+ i = "\n" + level*" "
+ if len(elem):
+ if not elem.text or not elem.text.strip():
+ elem.text = i + " "
+ if not elem.tail or not elem.tail.strip():
+ elem.tail = i
+ for elem in elem:
+ indent(elem, level+1)
+ if not elem.tail or not elem.tail.strip():
+ elem.tail = i
+ else:
+ if level and (not elem.tail or not elem.tail.strip()):
+ elem.tail = i
+
+if __name__ == "__main__":
+ import sys
+
+ if len(sys.argv) != 2:
+ print("supply a CLDR keyboard file")
+ sys.exit(1)
+
+ source = sys.argv[-1]
+ itree = ElementTree()
+ itree.parse(source)
+
+ root = convert(source, itree)
+ indent(root)
+
+ otree = ElementTree(root)
+ if hasattr(sys.stdout, 'buffer'):
+ out = sys.stdout.buffer
+ else:
+ out = sys.stdout
+ otree.write(out, xml_declaration=True, encoding='UTF-8')