7b21e5c
diff --git a/Makefile.am b/Makefile.am
7b21e5c
index d4253d6..e95006a 100644
7b21e5c
--- a/Makefile.am
7b21e5c
+++ b/Makefile.am
7b21e5c
@@ -14,7 +14,7 @@
7b21e5c
 # You should have received a copy of the GNU General Public License
7b21e5c
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
7b21e5c
 
7b21e5c
-SUBDIRS = marisa-glib libkkc data tools tests po
7b21e5c
+SUBDIRS = marisa-glib libkkc tools data tests po
7b21e5c
 DISTCHECK_CONFIGURE_FLAGS = --enable-docs
7b21e5c
 ACLOCAL_AMFLAGS = -I m4
7b21e5c
 
7b21e5c
@@ -22,20 +22,22 @@ if HAVE_VALADOC
7b21e5c
 SUBDIRS += docs
7b21e5c
 endif
7b21e5c
 
7b21e5c
-GITIGNOREFILES =				\
7b21e5c
-	INSTALL					\
7b21e5c
-	aclocal.m4				\
7b21e5c
-	compile					\
7b21e5c
-	config.guess				\
7b21e5c
-	config.h.in				\
7b21e5c
-	config.sub				\
7b21e5c
-	depcomp					\
7b21e5c
-	install-sh				\
7b21e5c
-	ltmain.sh				\
7b21e5c
-	missing					\
7b21e5c
-	mkinstalldirs				\
7b21e5c
-	`find "m4" -type f -name "*.m4" ! -name "vala.m4" -print` \
7b21e5c
-	$(top_builddir)/lcov.html/*		\
7b21e5c
+GITIGNOREFILES =							\
7b21e5c
+	INSTALL								\
7b21e5c
+	aclocal.m4							\
7b21e5c
+	compile								\
7b21e5c
+	config.guess							\
7b21e5c
+	config.h.in							\
7b21e5c
+	config.sub							\
7b21e5c
+	depcomp								\
7b21e5c
+	install-sh							\
7b21e5c
+	ltmain.sh							\
7b21e5c
+	missing								\
7b21e5c
+	mkinstalldirs							\
7b21e5c
+	`find "m4" -type f -name "*.m4" ! -name "vala.m4" -print`	\
7b21e5c
+	$(top_builddir)/lcov.html/*					\
7b21e5c
+	data/rules/*/*.pot						\
7b21e5c
+	test-driver							\
7b21e5c
 	$(NULL)
7b21e5c
 
7b21e5c
 distclean-local:
7b21e5c
diff --git a/README b/README
7b21e5c
deleted file mode 100644
7b21e5c
index bec5c53..0000000
7b21e5c
--- a/README
7b21e5c
+++ /dev/null
7b21e5c
@@ -1,58 +0,0 @@
7b21e5c
-libkkc - Japanese Kana Kanji conversion library -*- coding: utf-8 -*-
7b21e5c
-
7b21e5c
-* What's this?
7b21e5c
-
7b21e5c
-libkkc provides a converter from Kana-string to
7b21e5c
-Kana-Kanji-mixed-string.  It was named after kkc.el in GNU Emacs, a
7b21e5c
-simple Kana Kanji converter, while libkkc tries to convert sentences
7b21e5c
-in a bit more complex way using N-gram language models.
7b21e5c
-
7b21e5c
-* Install
7b21e5c
-
7b21e5c
-1. compile and install marisa-trie 0.2.1
7b21e5c
-
7b21e5c
- https://code.google.com/p/marisa-trie/
7b21e5c
-
7b21e5c
-2. compile and install
7b21e5c
-
7b21e5c
- $ ./autogen.sh
7b21e5c
- $ make
7b21e5c
- $ make install
7b21e5c
-
7b21e5c
-3. run kkc program
7b21e5c
-
7b21e5c
- $ kkc
7b21e5c
- Type kana sentence in the following form:
7b21e5c
- SENTENCE [N-BEST [SEGMENT-BOUNDARY...]]
7b21e5c
-
7b21e5c
- >> わたしのなまえはなかのです
7b21e5c
- 0: <わたし/わたし><の/の><名前/なまえ><は/は><中野/なかの><で/で><す/す>
7b21e5c
-
7b21e5c
- # get 3 matches instead of 1
7b21e5c
- >> わたしのなまえはなかのです 3
7b21e5c
- 0: <わたし/わたし><の/の><名前/なまえ><は/は><中野/なかの><で/で><す/す>
7b21e5c
- 1: <私/わたし><の/の><名前/なまえ><は/は><中野/なかの><で/で><す/す>
7b21e5c
- 2: <わたし/わたし><の/の><名前/なまえ><は/は><中野/なかの><デス/です>
7b21e5c
-
7b21e5c
- # enlarge the second segment (の -> のな)
7b21e5c
- >> わたしのなまえはなかのです 1 3 5
7b21e5c
- 0: <わたし/わたし><のな/のな><前/まえ><は/は><中野/なかの><で/で><す/す>
7b21e5c
-
7b21e5c
- # shrink the fourth segment (なかの -> なか)
7b21e5c
- >> わたしのなまえはなかのです 1 3 4 7 8 10
7b21e5c
- 0: <わたし/わたし><の/の><名前/なまえ><は/は><中/なか><の/の><で/で><す/す>
7b21e5c
-
7b21e5c
-License:
7b21e5c
-
7b21e5c
-GPLv3+
7b21e5c
-
7b21e5c
-Copyright (C) 2011-2014 Daiki Ueno <ueno@gnu.org>
7b21e5c
-Copyright (C) 2011-2014 Red Hat, Inc.
7b21e5c
-
7b21e5c
-This file is free software; as a special exception the author gives
7b21e5c
-unlimited permission to copy and/or distribute it, with or without
7b21e5c
-modifications, as long as this notice is preserved.
7b21e5c
-
7b21e5c
-This file is distributed in the hope that it will be useful, but
7b21e5c
-WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
7b21e5c
-implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7b21e5c
diff --git a/README b/README
7b21e5c
new file mode 120000
7b21e5c
index 0000000..42061c0
7b21e5c
--- /dev/null
7b21e5c
+++ b/README
7b21e5c
@@ -0,0 +1 @@
7b21e5c
+README.md
7b21e5c
\ No newline at end of file
7b21e5c
diff --git a/configure.ac b/configure.ac
7b21e5c
index e044965..a36d98c 100644
7b21e5c
--- a/configure.ac
7b21e5c
+++ b/configure.ac
7b21e5c
@@ -176,6 +176,8 @@ if test "x$found_introspection" = "xyes" -a "x$enable_vapigen" != "xno"; then
7b21e5c
 fi
7b21e5c
 AM_CONDITIONAL(ENABLE_VAPIGEN, [test "x$enable_vapigen" = "xyes"])
7b21e5c
 
7b21e5c
+AC_CHECK_PROGS(JSON_VALIDATE, json-validate, true)
7b21e5c
+
7b21e5c
 AC_CONFIG_HEADERS([config.h])
7b21e5c
 AC_CONFIG_FILES([Makefile
7b21e5c
 marisa-glib/Makefile
7b21e5c
@@ -185,9 +187,11 @@ tools/Makefile
7b21e5c
 tests/Makefile
7b21e5c
 tests/lib/Makefile
7b21e5c
 data/Makefile
7b21e5c
+data/dbus/Makefile
7b21e5c
 data/rules/Makefile
7b21e5c
 data/rules/default/Makefile
7b21e5c
 data/rules/act/Makefile
7b21e5c
+data/rules/atok/Makefile
7b21e5c
 data/rules/azik/Makefile
7b21e5c
 data/rules/azik-jp106/Makefile
7b21e5c
 data/rules/kzik/Makefile
7b21e5c
diff --git a/data/Makefile.am b/data/Makefile.am
7b21e5c
index 794738f..9c03b32 100644
7b21e5c
--- a/data/Makefile.am
7b21e5c
+++ b/data/Makefile.am
7b21e5c
@@ -14,6 +14,6 @@
7b21e5c
 # You should have received a copy of the GNU General Public License
7b21e5c
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
7b21e5c
 
7b21e5c
-SUBDIRS = rules templates
7b21e5c
+SUBDIRS = dbus rules templates
7b21e5c
 
7b21e5c
 -include $(top_srcdir)/git.mk
7b21e5c
diff --git a/data/dbus/Makefile.am b/data/dbus/Makefile.am
7b21e5c
new file mode 100644
7b21e5c
index 0000000..a68fa73
7b21e5c
--- /dev/null
7b21e5c
+++ b/data/dbus/Makefile.am
7b21e5c
@@ -0,0 +1,24 @@
7b21e5c
+# Copyright (C) 2011-2015 Daiki Ueno <ueno@gnu.org>
7b21e5c
+# Copyright (C) 2011-2015 Red Hat, Inc.
7b21e5c
+
7b21e5c
+# This program is free software: you can redistribute it and/or modify
7b21e5c
+# it under the terms of the GNU General Public License as published by
7b21e5c
+# the Free Software Foundation, either version 3 of the License, or
7b21e5c
+# (at your option) any later version.
7b21e5c
+
7b21e5c
+# This program is distributed in the hope that it will be useful,
7b21e5c
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
7b21e5c
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
7b21e5c
+# GNU General Public License for more details.
7b21e5c
+
7b21e5c
+# You should have received a copy of the GNU General Public License
7b21e5c
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
7b21e5c
+
7b21e5c
+EXTRA_DIST =					\
7b21e5c
+	org.du_a.Kkc.CandidateList.xml		\
7b21e5c
+	org.du_a.Kkc.Context.xml		\
7b21e5c
+	org.du_a.Kkc.SegmentList.xml		\
7b21e5c
+	org.du_a.Kkc.Server.xml			\
7b21e5c
+	$(NULL)
7b21e5c
+
7b21e5c
+-include $(top_srcdir)/git.mk
7b21e5c
diff --git a/data/dbus/org.du_a.Kkc.CandidateList.xml b/data/dbus/org.du_a.Kkc.CandidateList.xml
7b21e5c
new file mode 100644
7b21e5c
index 0000000..684d6fd
7b21e5c
--- /dev/null
7b21e5c
+++ b/data/dbus/org.du_a.Kkc.CandidateList.xml
7b21e5c
@@ -0,0 +1,54 @@
7b21e5c
+
7b21e5c
+                      "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
7b21e5c
+<node>
7b21e5c
+  <interface name="org.du_a.Kkc.CandidateList">
7b21e5c
+    <method name="SelectAt">
7b21e5c
+      <arg type="u" name="index_in_page" direction="in"/>
7b21e5c
+      <arg type="b" name="result" direction="out"/>
7b21e5c
+    </method>
7b21e5c
+    <method name="Select">
7b21e5c
+    </method>
7b21e5c
+    <method name="First">
7b21e5c
+      <arg type="b" name="result" direction="out"/>
7b21e5c
+    </method>
7b21e5c
+    <method name="Next">
7b21e5c
+      <arg type="b" name="result" direction="out"/>
7b21e5c
+    </method>
7b21e5c
+    <method name="Previous">
7b21e5c
+      <arg type="b" name="result" direction="out"/>
7b21e5c
+    </method>
7b21e5c
+    <method name="CursorUp">
7b21e5c
+      <arg type="b" name="result" direction="out"/>
7b21e5c
+    </method>
7b21e5c
+    <method name="CursorDown">
7b21e5c
+      <arg type="b" name="result" direction="out"/>
7b21e5c
+    </method>
7b21e5c
+    <method name="PageUp">
7b21e5c
+      <arg type="b" name="result" direction="out"/>
7b21e5c
+    </method>
7b21e5c
+    <method name="PageDown">
7b21e5c
+      <arg type="b" name="result" direction="out"/>
7b21e5c
+    </method>
7b21e5c
+    <method name="Get">
7b21e5c
+      <arg type="i" name="index" direction="in"/>
7b21e5c
+      <arg type="s" name="midasi" direction="out"/>
7b21e5c
+      <arg type="b" name="okuri" direction="out"/>
7b21e5c
+      <arg type="s" name="text" direction="out"/>
7b21e5c
+      <arg type="s" name="annotation" direction="out"/>
7b21e5c
+    </method>
7b21e5c
+    <signal name="Populated">
7b21e5c
+    </signal>
7b21e5c
+    <signal name="Selected">
7b21e5c
+      <arg type="s" name="midasi"/>
7b21e5c
+      <arg type="b" name="okuri"/>
7b21e5c
+      <arg type="s" name="text"/>
7b21e5c
+      <arg type="s" name="annotation"/>
7b21e5c
+    </signal>
7b21e5c
+    <property type="i" name="CursorPos" access="read"/>
7b21e5c
+    <property type="i" name="Size" access="read"/>
7b21e5c
+    <property type="u" name="PageStart" access="read"/>
7b21e5c
+    <property type="u" name="PageSize" access="read"/>
7b21e5c
+    <property type="b" name="Round" access="read"/>
7b21e5c
+    <property type="b" name="PageVisible" access="read"/>
7b21e5c
+  </interface>
7b21e5c
+</node>
7b21e5c
diff --git a/data/dbus/org.du_a.Kkc.Context.xml b/data/dbus/org.du_a.Kkc.Context.xml
7b21e5c
new file mode 100644
7b21e5c
index 0000000..f989227
7b21e5c
--- /dev/null
7b21e5c
+++ b/data/dbus/org.du_a.Kkc.Context.xml
7b21e5c
@@ -0,0 +1,34 @@
7b21e5c
+
7b21e5c
+                      "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
7b21e5c
+<node>
7b21e5c
+  <interface name="org.du_a.Kkc.Context">
7b21e5c
+    <method name="ProcessKeyEvent">
7b21e5c
+      <arg type="u" name="keyval" direction="in"/>
7b21e5c
+      <arg type="u" name="keycode" direction="in"/>
7b21e5c
+      <arg type="u" name="modifiers" direction="in"/>
7b21e5c
+      <arg type="b" name="result" direction="out"/>
7b21e5c
+    </method>
7b21e5c
+    <method name="ProcessCommandEvent">
7b21e5c
+      <arg type="s" name="command" direction="in"/>
7b21e5c
+      <arg type="b" name="result" direction="out"/>
7b21e5c
+    </method>
7b21e5c
+    <method name="Reset">
7b21e5c
+    </method>
7b21e5c
+    <method name="HasOutput">
7b21e5c
+      <arg type="b" name="result" direction="out"/>
7b21e5c
+    </method>
7b21e5c
+    <method name="PeekOutput">
7b21e5c
+      <arg type="s" name="result" direction="out"/>
7b21e5c
+    </method>
7b21e5c
+    <method name="PollOutput">
7b21e5c
+      <arg type="s" name="result" direction="out"/>
7b21e5c
+    </method>
7b21e5c
+    <method name="ClearOutput">
7b21e5c
+    </method>
7b21e5c
+    <property type="s" name="Input" access="read"/>
7b21e5c
+    <property type="i" name="InputCursorPos" access="read"/>
7b21e5c
+    <property type="u" name="InputMode" access="readwrite"/>
7b21e5c
+    <property type="u" name="PunctuationStyle" access="readwrite"/>
7b21e5c
+    <property type="b" name="AutoCorrect" access="readwrite"/>
7b21e5c
+  </interface>
7b21e5c
+</node>
7b21e5c
diff --git a/data/dbus/org.du_a.Kkc.SegmentList.xml b/data/dbus/org.du_a.Kkc.SegmentList.xml
7b21e5c
new file mode 100644
7b21e5c
index 0000000..43d578b
7b21e5c
--- /dev/null
7b21e5c
+++ b/data/dbus/org.du_a.Kkc.SegmentList.xml
7b21e5c
@@ -0,0 +1,29 @@
7b21e5c
+
7b21e5c
+                      "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
7b21e5c
+<node>
7b21e5c
+  <interface name="org.du_a.Kkc.SegmentList">
7b21e5c
+    <method name="Get">
7b21e5c
+      <arg type="i" name="index" direction="in"/>
7b21e5c
+      <arg type="s" name="input" direction="out"/>
7b21e5c
+      <arg type="s" name="output" direction="out"/>
7b21e5c
+    </method>
7b21e5c
+    <method name="FirstSegment">
7b21e5c
+      <arg type="b" name="result" direction="out"/>
7b21e5c
+    </method>
7b21e5c
+    <method name="LastSegment">
7b21e5c
+      <arg type="b" name="result" direction="out"/>
7b21e5c
+    </method>
7b21e5c
+    <method name="NextSegment">
7b21e5c
+    </method>
7b21e5c
+    <method name="PreviousSegment">
7b21e5c
+    </method>
7b21e5c
+    <method name="GetOutput">
7b21e5c
+      <arg type="s" name="result" direction="out"/>
7b21e5c
+    </method>
7b21e5c
+    <method name="GetInput">
7b21e5c
+      <arg type="s" name="result" direction="out"/>
7b21e5c
+    </method>
7b21e5c
+    <property type="i" name="CursorPos" access="read"/>
7b21e5c
+    <property type="i" name="Size" access="read"/>
7b21e5c
+  </interface>
7b21e5c
+</node>
7b21e5c
diff --git a/data/dbus/org.du_a.Kkc.Server.xml b/data/dbus/org.du_a.Kkc.Server.xml
7b21e5c
new file mode 100644
7b21e5c
index 0000000..749abb4
7b21e5c
--- /dev/null
7b21e5c
+++ b/data/dbus/org.du_a.Kkc.Server.xml
7b21e5c
@@ -0,0 +1,12 @@
7b21e5c
+
7b21e5c
+                      "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
7b21e5c
+<node>
7b21e5c
+  <interface name="org.du_a.Kkc.Server">
7b21e5c
+    <method name="CreateContext">
7b21e5c
+      <arg type="s" name="result" direction="out"/>
7b21e5c
+    </method>
7b21e5c
+    <method name="DestroyContext">
7b21e5c
+      <arg type="s" name="object_path" direction="in"/>
7b21e5c
+    </method>
7b21e5c
+  </interface>
7b21e5c
+</node>
7b21e5c
diff --git a/data/rules/Makefile.am b/data/rules/Makefile.am
7b21e5c
index 0e88d21..d1a1293 100644
7b21e5c
--- a/data/rules/Makefile.am
7b21e5c
+++ b/data/rules/Makefile.am
7b21e5c
@@ -17,6 +17,7 @@
7b21e5c
 SUBDIRS =					\
7b21e5c
 	default					\
7b21e5c
 	act					\
7b21e5c
+	atok					\
7b21e5c
 	azik					\
7b21e5c
 	azik-jp106				\
7b21e5c
 	kzik					\
7b21e5c
@@ -31,6 +32,9 @@ SUBDIRS =					\
7b21e5c
 EXTRA_DIST =					\
7b21e5c
 	README.rules				\
7b21e5c
 	rule.mk					\
7b21e5c
+	metadata-schema.json			\
7b21e5c
+	keymap-schema.json			\
7b21e5c
+	rom-kana-schema.json			\
7b21e5c
 	$(NULL)
7b21e5c
 
7b21e5c
 -include $(top_srcdir)/git.mk
7b21e5c
diff --git a/data/rules/act/Makefile.am b/data/rules/act/Makefile.am
7b21e5c
index 2524bd6..16ad93c 100644
7b21e5c
--- a/data/rules/act/Makefile.am
7b21e5c
+++ b/data/rules/act/Makefile.am
7b21e5c
@@ -8,24 +8,16 @@ files =						\
7b21e5c
 	keymap/latin.json			\
7b21e5c
 	keymap/wide-latin.json			\
7b21e5c
 	rom-kana/default.json			\
7b21e5c
-	$(NULL)
7b21e5c
-metadata_in =					\
7b21e5c
-	metadata.json.in			\
7b21e5c
+	metadata.json				\
7b21e5c
 	$(NULL)
7b21e5c
 
7b21e5c
 nobase_rules_DATA =				\
7b21e5c
 	$(files)				\
7b21e5c
-	$(metadata_in:.in=)			\
7b21e5c
 	$(NULL)
7b21e5c
 
7b21e5c
 EXTRA_DIST =					\
7b21e5c
 	$(files)				\
7b21e5c
-	$(metadata_in)				\
7b21e5c
-	$(NULL)
7b21e5c
-
7b21e5c
-CLEANFILES =					\
7b21e5c
-	metadata.json				\
7b21e5c
 	$(NULL)
7b21e5c
 
7b21e5c
--include $(top_srcdir)/data/rules/rule.mk
7b21e5c
+include $(top_srcdir)/data/rules/rule.mk
7b21e5c
 -include $(top_srcdir)/git.mk
7b21e5c
diff --git a/data/rules/act/metadata.json b/data/rules/act/metadata.json
7b21e5c
new file mode 100644
7b21e5c
index 0000000..0136cc4
7b21e5c
--- /dev/null
7b21e5c
+++ b/data/rules/act/metadata.json
7b21e5c
@@ -0,0 +1,5 @@
7b21e5c
+{
7b21e5c
+    "name": "ACT",
7b21e5c
+    "description": "Extended romaji input method based on AZIK for Dvorak keyboard layout, developed by Kiyoshi Kimura <http://hp.vector.co.jp/authors/VA002116/azik/azikindx.htm#act>",
7b21e5c
+    "priority": 10
7b21e5c
+}
7b21e5c
diff --git a/data/rules/act/metadata.json.in b/data/rules/act/metadata.json.in
7b21e5c
deleted file mode 100644
7b21e5c
index f4b7721..0000000
7b21e5c
--- a/data/rules/act/metadata.json.in
7b21e5c
+++ /dev/null
7b21e5c
@@ -1,5 +0,0 @@
7b21e5c
-{
7b21e5c
-    "name": _("ACT"),
7b21e5c
-    "description": _("Extended romaji input method based on AZIK for Dvorak keyboard layout, developed by Kiyoshi Kimura <http://hp.vector.co.jp/authors/VA002116/azik/azikindx.htm#act>"),
7b21e5c
-    "priority": 10
7b21e5c
-}
7b21e5c
diff --git a/data/rules/atok/Makefile.am b/data/rules/atok/Makefile.am
7b21e5c
new file mode 100644
7b21e5c
index 0000000..d6fe7eb
7b21e5c
--- /dev/null
7b21e5c
+++ b/data/rules/atok/Makefile.am
7b21e5c
@@ -0,0 +1,24 @@
7b21e5c
+rulesdir = $(pkgdatadir)/rules/atok
7b21e5c
+
7b21e5c
+files =						\
7b21e5c
+	keymap/default.json			\
7b21e5c
+	keymap/hankaku-katakana.json		\
7b21e5c
+	keymap/hiragana.json			\
7b21e5c
+	keymap/katakana.json			\
7b21e5c
+	keymap/latin.json			\
7b21e5c
+	keymap/wide-latin.json			\
7b21e5c
+	keymap/direct.json			\
7b21e5c
+	rom-kana/default.json			\
7b21e5c
+	metadata.json                           \
7b21e5c
+	$(NULL)
7b21e5c
+
7b21e5c
+nobase_rules_DATA =				\
7b21e5c
+	$(files)				\
7b21e5c
+	$(NULL)
7b21e5c
+
7b21e5c
+EXTRA_DIST =					\
7b21e5c
+	$(files)				\
7b21e5c
+	$(NULL)
7b21e5c
+
7b21e5c
+include $(top_srcdir)/data/rules/rule.mk
7b21e5c
+-include $(top_srcdir)/git.mk
7b21e5c
diff --git a/data/rules/atok/keymap/default.json b/data/rules/atok/keymap/default.json
7b21e5c
new file mode 100644
7b21e5c
index 0000000..a2e091b
7b21e5c
--- /dev/null
7b21e5c
+++ b/data/rules/atok/keymap/default.json
7b21e5c
@@ -0,0 +1,31 @@
7b21e5c
+{
7b21e5c
+    "include": [
7b21e5c
+	"default/default"
7b21e5c
+    ],
7b21e5c
+    "define": {
7b21e5c
+        "keymap": {
7b21e5c
+            "C-[": "abort",
7b21e5c
+            "C-g": "delete-forward",
7b21e5c
+            "C-q": null,
7b21e5c
+            "C-F7": "register",
7b21e5c
+            "C-Down": "next-candidate",
7b21e5c
+            "S-space": "next-candidate",
7b21e5c
+            "S-Henkan_Mode": "next-candidate",
7b21e5c
+            "C-Up": "previous-candidate",
7b21e5c
+            "Muhenkan": null,
7b21e5c
+            "C-l": "expand-segment",
7b21e5c
+            "C-k": "shrink-segment",
7b21e5c
+            "Right": "expand-segment",
7b21e5c
+            "Left": "shrink-segment",
7b21e5c
+            "S-Left": "previous-segment",
7b21e5c
+            "S-Right": "next-segment",
7b21e5c
+            "C-Left": "first-segment",
7b21e5c
+            "C-Right": "last-segment",
7b21e5c
+            "C-u": "convert-hiragana",
7b21e5c
+            "C-i": "convert-katakana",
7b21e5c
+            "C-o": "convert-hankaku-katakana",
7b21e5c
+            "C-p": "convert-wide-latin",
7b21e5c
+            "C-@": "convert-latin"
7b21e5c
+        }
7b21e5c
+    }
7b21e5c
+}
7b21e5c
diff --git a/data/rules/atok/keymap/direct.json b/data/rules/atok/keymap/direct.json
7b21e5c
new file mode 100644
7b21e5c
index 0000000..b11a387
7b21e5c
--- /dev/null
7b21e5c
+++ b/data/rules/atok/keymap/direct.json
7b21e5c
@@ -0,0 +1,9 @@
7b21e5c
+{
7b21e5c
+    "define": {
7b21e5c
+        "keymap": {
7b21e5c
+            "Hiragana_Katakana": "set-input-mode-hiragana",
7b21e5c
+            "Zenkaku_Hankaku": "set-input-mode-hiragana",
7b21e5c
+            "Henkan_Mode": "set-input-mode-hiragana"
7b21e5c
+        }
7b21e5c
+    }
7b21e5c
+}
7b21e5c
diff --git a/data/rules/atok/keymap/hankaku-katakana.json b/data/rules/atok/keymap/hankaku-katakana.json
7b21e5c
new file mode 100644
7b21e5c
index 0000000..84e46f5
7b21e5c
--- /dev/null
7b21e5c
+++ b/data/rules/atok/keymap/hankaku-katakana.json
7b21e5c
@@ -0,0 +1,10 @@
7b21e5c
+{
7b21e5c
+    "include": [
7b21e5c
+        "default"
7b21e5c
+    ],
7b21e5c
+    "define": {
7b21e5c
+        "keymap": {
7b21e5c
+            "Hiragana_Katakana": "set-input-mode-hiragana"
7b21e5c
+        }
7b21e5c
+    }
7b21e5c
+}
7b21e5c
diff --git a/data/rules/atok/keymap/hiragana.json b/data/rules/atok/keymap/hiragana.json
7b21e5c
new file mode 100644
7b21e5c
index 0000000..be63908
7b21e5c
--- /dev/null
7b21e5c
+++ b/data/rules/atok/keymap/hiragana.json
7b21e5c
@@ -0,0 +1,12 @@
7b21e5c
+{
7b21e5c
+    "include": [
7b21e5c
+        "default"
7b21e5c
+    ],
7b21e5c
+    "define": {
7b21e5c
+        "keymap": {
7b21e5c
+            "Hiragana_Katakana": "set-input-mode-katakana",
7b21e5c
+            "Henkan_Mode": "set-input-mode-direct",
7b21e5c
+            "Muhenkan": "set-input-mode-latin"
7b21e5c
+        }
7b21e5c
+    }
7b21e5c
+}
7b21e5c
diff --git a/data/rules/atok/keymap/katakana.json b/data/rules/atok/keymap/katakana.json
7b21e5c
new file mode 100644
7b21e5c
index 0000000..84e46f5
7b21e5c
--- /dev/null
7b21e5c
+++ b/data/rules/atok/keymap/katakana.json
7b21e5c
@@ -0,0 +1,10 @@
7b21e5c
+{
7b21e5c
+    "include": [
7b21e5c
+        "default"
7b21e5c
+    ],
7b21e5c
+    "define": {
7b21e5c
+        "keymap": {
7b21e5c
+            "Hiragana_Katakana": "set-input-mode-hiragana"
7b21e5c
+        }
7b21e5c
+    }
7b21e5c
+}
7b21e5c
diff --git a/data/rules/atok/keymap/latin.json b/data/rules/atok/keymap/latin.json
7b21e5c
new file mode 100644
7b21e5c
index 0000000..c3f7a78
7b21e5c
--- /dev/null
7b21e5c
+++ b/data/rules/atok/keymap/latin.json
7b21e5c
@@ -0,0 +1,11 @@
7b21e5c
+{
7b21e5c
+    "include": [
7b21e5c
+        "default"
7b21e5c
+    ],
7b21e5c
+    "define": {
7b21e5c
+        "keymap": {
7b21e5c
+            "Hiragana_Katakana": "set-input-mode-hiragana",
7b21e5c
+            "Muhenkan": "set-input-mode-hiragana"
7b21e5c
+        }
7b21e5c
+    }
7b21e5c
+}
7b21e5c
diff --git a/data/rules/atok/keymap/wide-latin.json b/data/rules/atok/keymap/wide-latin.json
7b21e5c
new file mode 100644
7b21e5c
index 0000000..84e46f5
7b21e5c
--- /dev/null
7b21e5c
+++ b/data/rules/atok/keymap/wide-latin.json
7b21e5c
@@ -0,0 +1,10 @@
7b21e5c
+{
7b21e5c
+    "include": [
7b21e5c
+        "default"
7b21e5c
+    ],
7b21e5c
+    "define": {
7b21e5c
+        "keymap": {
7b21e5c
+            "Hiragana_Katakana": "set-input-mode-hiragana"
7b21e5c
+        }
7b21e5c
+    }
7b21e5c
+}
7b21e5c
diff --git a/data/rules/atok/metadata.json b/data/rules/atok/metadata.json
7b21e5c
new file mode 100644
7b21e5c
index 0000000..8c0d82b
7b21e5c
--- /dev/null
7b21e5c
+++ b/data/rules/atok/metadata.json
7b21e5c
@@ -0,0 +1,5 @@
7b21e5c
+{
7b21e5c
+    "name": "ATOK",
7b21e5c
+    "description": "The commercial Input Method ATOK like style",
7b21e5c
+    "priority": 90
7b21e5c
+}
7b21e5c
diff --git a/data/rules/atok/rom-kana/default.json b/data/rules/atok/rom-kana/default.json
7b21e5c
new file mode 100644
7b21e5c
index 0000000..c179331
7b21e5c
--- /dev/null
7b21e5c
+++ b/data/rules/atok/rom-kana/default.json
7b21e5c
@@ -0,0 +1,28 @@
7b21e5c
+{
7b21e5c
+    "include": [
7b21e5c
+        "default/default"
7b21e5c
+    ],
7b21e5c
+    "define": {
7b21e5c
+        "rom-kana": {
7b21e5c
+            "dwu": ["", "どぅ" ],
7b21e5c
+            "kwa": ["", "くぁ" ],
7b21e5c
+            "lka": ["", "ヵ" ],
7b21e5c
+            "lke": ["", "ヶ" ],
7b21e5c
+            "ltu": ["", "っ" ],
7b21e5c
+            "ltsu": ["", "っ" ],
7b21e5c
+            "lwa": ["", "ゎ" ],
7b21e5c
+            "tha": ["", "てゃ" ],
7b21e5c
+            "tsa": ["", "つぁ" ],
7b21e5c
+            "tsi": ["", "つぃ" ],
7b21e5c
+            "tse": ["", "つぇ" ],
7b21e5c
+            "tso": ["", "つぉ" ],
7b21e5c
+            "twu": ["", "とぅ" ],
7b21e5c
+            "wye": ["", "ゑ" ],
7b21e5c
+            "wyi": ["", "ゐ" ],
7b21e5c
+            "xye": ["", "ぇ" ],
7b21e5c
+            "yi": ["", "い" ],
7b21e5c
+            "zya": null,
7b21e5c
+            "/": ["", "・", "/"]
7b21e5c
+       }
7b21e5c
+    }
7b21e5c
+}
7b21e5c
diff --git a/data/rules/azik-jp106/Makefile.am b/data/rules/azik-jp106/Makefile.am
7b21e5c
index 7311917..8d5e739 100644
7b21e5c
--- a/data/rules/azik-jp106/Makefile.am
7b21e5c
+++ b/data/rules/azik-jp106/Makefile.am
7b21e5c
@@ -8,24 +8,16 @@ files =						\
7b21e5c
 	keymap/latin.json			\
7b21e5c
 	keymap/wide-latin.json			\
7b21e5c
 	rom-kana/default.json			\
7b21e5c
-	$(NULL)
7b21e5c
-metadata_in =					\
7b21e5c
-	metadata.json.in			\
7b21e5c
+	metadata.json				\
7b21e5c
 	$(NULL)
7b21e5c
 
7b21e5c
 nobase_rules_DATA =				\
7b21e5c
 	$(files)				\
7b21e5c
-	$(metadata_in:.in=)			\
7b21e5c
 	$(NULL)
7b21e5c
 
7b21e5c
 EXTRA_DIST =					\
7b21e5c
 	$(files)				\
7b21e5c
-	$(metadata_in)				\
7b21e5c
-	$(NULL)
7b21e5c
-
7b21e5c
-CLEANFILES =					\
7b21e5c
-	metadata.json				\
7b21e5c
 	$(NULL)
7b21e5c
 
7b21e5c
--include $(top_srcdir)/data/rules/rule.mk
7b21e5c
+include $(top_srcdir)/data/rules/rule.mk
7b21e5c
 -include $(top_srcdir)/git.mk
7b21e5c
diff --git a/data/rules/azik-jp106/keymap/hiragana.json b/data/rules/azik-jp106/keymap/hiragana.json
7b21e5c
index 60bffc8..e94115d 100644
7b21e5c
--- a/data/rules/azik-jp106/keymap/hiragana.json
7b21e5c
+++ b/data/rules/azik-jp106/keymap/hiragana.json
7b21e5c
@@ -1,7 +1,7 @@
7b21e5c
 {
7b21e5c
     "include": [
7b21e5c
         "default/default"
7b21e5c
-    ]
7b21e5c
+    ],
7b21e5c
     "define": {
7b21e5c
         "keymap": {
7b21e5c
             "[": null,
7b21e5c
diff --git a/data/rules/azik-jp106/metadata.json b/data/rules/azik-jp106/metadata.json
7b21e5c
new file mode 100644
7b21e5c
index 0000000..75efc94
7b21e5c
--- /dev/null
7b21e5c
+++ b/data/rules/azik-jp106/metadata.json
7b21e5c
@@ -0,0 +1,5 @@
7b21e5c
+{
7b21e5c
+    "name": "AZIK (Japanese 106 keyboard)",
7b21e5c
+    "description": "Extended romaji input method developed by Kiyoshi Kimura <http://hp.vector.co.jp/authors/VA002116/azik/azikindx.htm>",
7b21e5c
+    "priority": 10
7b21e5c
+}
7b21e5c
diff --git a/data/rules/azik-jp106/metadata.json.in b/data/rules/azik-jp106/metadata.json.in
7b21e5c
deleted file mode 100644
7b21e5c
index a7fe226..0000000
7b21e5c
--- a/data/rules/azik-jp106/metadata.json.in
7b21e5c
+++ /dev/null
7b21e5c
@@ -1,5 +0,0 @@
7b21e5c
-{
7b21e5c
-    "name": _("AZIK (Japanese 106 keyboard)"),
7b21e5c
-    "description": _("Extended romaji input method developed by Kiyoshi Kimura <http://hp.vector.co.jp/authors/VA002116/azik/azikindx.htm>"),
7b21e5c
-    "priority": 10
7b21e5c
-}
7b21e5c
diff --git a/data/rules/azik/Makefile.am b/data/rules/azik/Makefile.am
7b21e5c
index acc54bc..e4ada1c 100644
7b21e5c
--- a/data/rules/azik/Makefile.am
7b21e5c
+++ b/data/rules/azik/Makefile.am
7b21e5c
@@ -8,24 +8,16 @@ files =						\
7b21e5c
 	keymap/latin.json			\
7b21e5c
 	keymap/wide-latin.json			\
7b21e5c
 	rom-kana/default.json			\
7b21e5c
-	$(NULL)
7b21e5c
-metadata_in =					\
7b21e5c
-	metadata.json.in			\
7b21e5c
+	metadata.json				\
7b21e5c
 	$(NULL)
7b21e5c
 
7b21e5c
 nobase_rules_DATA =				\
7b21e5c
 	$(files)				\
7b21e5c
-	$(metadata_in:.in=)			\
7b21e5c
 	$(NULL)
7b21e5c
 
7b21e5c
 EXTRA_DIST =					\
7b21e5c
 	$(files)				\
7b21e5c
-	$(metadata_in)				\
7b21e5c
-	$(NULL)
7b21e5c
-
7b21e5c
-CLEANFILES =					\
7b21e5c
-	metadata.json				\
7b21e5c
 	$(NULL)
7b21e5c
 
7b21e5c
--include $(top_srcdir)/data/rules/rule.mk
7b21e5c
+include $(top_srcdir)/data/rules/rule.mk
7b21e5c
 -include $(top_srcdir)/git.mk
7b21e5c
diff --git a/data/rules/azik/keymap/default.json b/data/rules/azik/keymap/default.json
7b21e5c
index 307b53a..828a178 100644
7b21e5c
--- a/data/rules/azik/keymap/default.json
7b21e5c
+++ b/data/rules/azik/keymap/default.json
7b21e5c
@@ -1,10 +1,5 @@
7b21e5c
 {
7b21e5c
     "include": [
7b21e5c
         "default/default"
7b21e5c
-    ],
7b21e5c
-    "define": {
7b21e5c
-        "keymap": {
7b21e5c
-            ":": "upper-;"
7b21e5c
-        }
7b21e5c
-    }
7b21e5c
+    ]
7b21e5c
 }
7b21e5c
diff --git a/data/rules/azik/metadata.json b/data/rules/azik/metadata.json
7b21e5c
new file mode 100644
7b21e5c
index 0000000..a7421dd
7b21e5c
--- /dev/null
7b21e5c
+++ b/data/rules/azik/metadata.json
7b21e5c
@@ -0,0 +1,5 @@
7b21e5c
+{
7b21e5c
+    "name": "AZIK",
7b21e5c
+    "description": "Extended romaji input method developed by Kiyoshi Kimura <http://hp.vector.co.jp/authors/VA002116/azik/azikindx.htm>",
7b21e5c
+    "priority": 10
7b21e5c
+}
7b21e5c
diff --git a/data/rules/azik/metadata.json.in b/data/rules/azik/metadata.json.in
7b21e5c
deleted file mode 100644
7b21e5c
index 6c4fa9d..0000000
7b21e5c
--- a/data/rules/azik/metadata.json.in
7b21e5c
+++ /dev/null
7b21e5c
@@ -1,5 +0,0 @@
7b21e5c
-{
7b21e5c
-    "name": _("AZIK"),
7b21e5c
-    "description": _("Extended romaji input method developed by Kiyoshi Kimura <http://hp.vector.co.jp/authors/VA002116/azik/azikindx.htm>"),
7b21e5c
-    "priority": 10
7b21e5c
-}
7b21e5c
diff --git a/data/rules/default/Makefile.am b/data/rules/default/Makefile.am
7b21e5c
index e2e0a56..77d220a 100644
7b21e5c
--- a/data/rules/default/Makefile.am
7b21e5c
+++ b/data/rules/default/Makefile.am
7b21e5c
@@ -9,24 +9,16 @@ files =						\
7b21e5c
 	keymap/wide-latin.json			\
7b21e5c
 	keymap/direct.json			\
7b21e5c
 	rom-kana/default.json			\
7b21e5c
-	$(NULL)
7b21e5c
-metadata_in =					\
7b21e5c
-	metadata.json.in			\
7b21e5c
+	metadata.json				\
7b21e5c
 	$(NULL)
7b21e5c
 
7b21e5c
 nobase_rules_DATA =				\
7b21e5c
 	$(files)				\
7b21e5c
-	$(metadata_in:.in=)			\
7b21e5c
 	$(NULL)
7b21e5c
 
7b21e5c
 EXTRA_DIST =					\
7b21e5c
 	$(files)				\
7b21e5c
-	$(metadata_in)				\
7b21e5c
-	$(NULL)
7b21e5c
-
7b21e5c
-CLEANFILES =					\
7b21e5c
-	metadata.json				\
7b21e5c
 	$(NULL)
7b21e5c
 
7b21e5c
--include $(top_srcdir)/data/rules/rule.mk
7b21e5c
+include $(top_srcdir)/data/rules/rule.mk
7b21e5c
 -include $(top_srcdir)/git.mk
7b21e5c
diff --git a/data/rules/default/metadata.json b/data/rules/default/metadata.json
7b21e5c
new file mode 100644
7b21e5c
index 0000000..78f492f
7b21e5c
--- /dev/null
7b21e5c
+++ b/data/rules/default/metadata.json
7b21e5c
@@ -0,0 +1,5 @@
7b21e5c
+{
7b21e5c
+    "name": "Default",
7b21e5c
+    "description": "Default typing rule",
7b21e5c
+    "priority": 99
7b21e5c
+}
7b21e5c
diff --git a/data/rules/default/metadata.json.in b/data/rules/default/metadata.json.in
7b21e5c
deleted file mode 100644
7b21e5c
index f9aa8dc..0000000
7b21e5c
--- a/data/rules/default/metadata.json.in
7b21e5c
+++ /dev/null
7b21e5c
@@ -1,5 +0,0 @@
7b21e5c
-{
7b21e5c
-    "name": _("Default"),
7b21e5c
-    "description": _("Default typing rule"),
7b21e5c
-    "priority": 99
7b21e5c
-}
7b21e5c
diff --git a/data/rules/kana/Makefile.am b/data/rules/kana/Makefile.am
7b21e5c
index 559ef16..b00df0b 100644
7b21e5c
--- a/data/rules/kana/Makefile.am
7b21e5c
+++ b/data/rules/kana/Makefile.am
7b21e5c
@@ -9,24 +9,16 @@ files =						\
7b21e5c
 	keymap/wide-latin.json			\
7b21e5c
 	keymap/direct.json			\
7b21e5c
 	rom-kana/default.json			\
7b21e5c
-	$(NULL)
7b21e5c
-metadata_in =					\
7b21e5c
-	metadata.json.in			\
7b21e5c
+	metadata.json				\
7b21e5c
 	$(NULL)
7b21e5c
 
7b21e5c
 nobase_rules_DATA =				\
7b21e5c
 	$(files)				\
7b21e5c
-	$(metadata_in:.in=)			\
7b21e5c
 	$(NULL)
7b21e5c
 
7b21e5c
 EXTRA_DIST =					\
7b21e5c
 	$(files)				\
7b21e5c
-	$(metadata_in)				\
7b21e5c
-	$(NULL)
7b21e5c
-
7b21e5c
-CLEANFILES =					\
7b21e5c
-	metadata.json				\
7b21e5c
 	$(NULL)
7b21e5c
 
7b21e5c
--include $(top_srcdir)/data/rules/rule.mk
7b21e5c
+include $(top_srcdir)/data/rules/rule.mk
7b21e5c
 -include $(top_srcdir)/git.mk
7b21e5c
diff --git a/data/rules/kana/metadata.json b/data/rules/kana/metadata.json
7b21e5c
new file mode 100644
7b21e5c
index 0000000..e2dbe8f
7b21e5c
--- /dev/null
7b21e5c
+++ b/data/rules/kana/metadata.json
7b21e5c
@@ -0,0 +1,6 @@
7b21e5c
+{
7b21e5c
+    "name": "Kana",
7b21e5c
+    "description": "Direct Kana typing",
7b21e5c
+    "filter": "kana",
7b21e5c
+    "priority": 99
7b21e5c
+}
7b21e5c
diff --git a/data/rules/kana/metadata.json.in b/data/rules/kana/metadata.json.in
7b21e5c
deleted file mode 100644
7b21e5c
index 47efecc..0000000
7b21e5c
--- a/data/rules/kana/metadata.json.in
7b21e5c
+++ /dev/null
7b21e5c
@@ -1,6 +0,0 @@
7b21e5c
-{
7b21e5c
-    "name": _("Kana"),
7b21e5c
-    "description": _("Direct Kana typing"),
7b21e5c
-    "filter": "kana",
7b21e5c
-    "priority": 99
7b21e5c
-}
7b21e5c
diff --git a/data/rules/kana/rom-kana/default.json b/data/rules/kana/rom-kana/default.json
7b21e5c
index e5c573e..d68ca78 100644
7b21e5c
--- a/data/rules/kana/rom-kana/default.json
7b21e5c
+++ b/data/rules/kana/rom-kana/default.json
7b21e5c
@@ -29,40 +29,70 @@
7b21e5c
             "^@": ["", "べ"],
7b21e5c
             "^[": ["", "ぺ"],
7b21e5c
             "q": ["", "た", "タ", "た", "タ"],
7b21e5c
+            "Q": ["", "た", "タ", "た", "タ"],
7b21e5c
             "q@": ["", "だ"],
7b21e5c
+            "Q@": ["", "だ"],
7b21e5c
             "w": ["", "て", "テ", "て", "テ"],
7b21e5c
+            "W": ["", "て", "テ", "て", "テ"],
7b21e5c
             "w@": ["", "で"],
7b21e5c
+            "W@": ["", "で"],
7b21e5c
             "e": ["", "い"],
7b21e5c
             "E": ["", "ぃ"],
7b21e5c
             "r": ["", "す", "ス", "す", "ス"],
7b21e5c
+            "R": ["", "す", "ス", "す", "ス"],
7b21e5c
             "r@": ["", "ず"],
7b21e5c
+            "R@": ["", "ず"],
7b21e5c
             "t": ["", "か", "カ", "か", "カ"],
7b21e5c
+            "T": ["", "か", "カ", "か", "カ"],
7b21e5c
             "t@": ["", "が"],
7b21e5c
+            "T@": ["", "が"],
7b21e5c
             "y": ["", "ん"],
7b21e5c
+            "Y": ["", "ん"],
7b21e5c
             "u": ["", "な"],
7b21e5c
+            "U": ["", "な"],
7b21e5c
             "i": ["", "に"],
7b21e5c
+            "I": ["", "に"],
7b21e5c
             "o": ["", "ら"],
7b21e5c
+            "O": ["", "ら"],
7b21e5c
             "p": ["", "せ", "セ", "せ", "セ"],
7b21e5c
+            "P": ["", "せ", "セ", "せ", "セ"],
7b21e5c
             "p@": ["", "ぜ"],
7b21e5c
+            "P@": ["", "ぜ"],
7b21e5c
             "@": ["", "゛"],
7b21e5c
             "[": ["", "゜"],
7b21e5c
             "{": ["", "「"],
7b21e5c
             "a": ["", "ち", "チ", "ち", "チ"],
7b21e5c
+            "A": ["", "ち", "チ", "ち", "チ"],
7b21e5c
             "a@": ["", "ぢ"],
7b21e5c
+            "A@": ["", "ぢ"],
7b21e5c
             "s": ["", "と", "ト", "と", "ト"],
7b21e5c
+            "S": ["", "と", "ト", "と", "ト"],
7b21e5c
             "s@": ["", "ど"],
7b21e5c
+            "S@": ["", "ど"],
7b21e5c
             "d": ["", "し", "シ", "し", "シ"],
7b21e5c
+            "D": ["", "し", "シ", "し", "シ"],
7b21e5c
             "d@": ["", "じ"],
7b21e5c
+            "D@": ["", "じ"],
7b21e5c
             "f": ["", "は", "ハ", "は", "ハ"],
7b21e5c
+            "F": ["", "は", "ハ", "は", "ハ"],
7b21e5c
             "f@": ["", "ば"],
7b21e5c
+            "F@": ["", "ば"],
7b21e5c
             "f[": ["", "ぱ"],
7b21e5c
+            "F[": ["", "ぱ"],
7b21e5c
             "g": ["", "き", "キ", "き", "キ"],
7b21e5c
+            "G": ["", "き", "キ", "き", "キ"],
7b21e5c
             "g@": ["", "ぎ"],
7b21e5c
+            "G@": ["", "ぎ"],
7b21e5c
             "h": ["", "く", "ク", "く", "ク"],
7b21e5c
+            "H": ["", "く", "ク", "く", "ク"],
7b21e5c
             "h@": ["", "ぐ"],
7b21e5c
+            "H@": ["", "ぐ"],
7b21e5c
             "j": ["", "ま"],
7b21e5c
+            "J": ["", "ま"],
7b21e5c
             "k": ["", "の"],
7b21e5c
+            "K": ["", "の"],
7b21e5c
             "l": ["", "り"],
7b21e5c
+            "L": ["", "り"],
7b21e5c
             ";": ["", "れ"],
7b21e5c
             ":": ["", "け", "ケ", "け", "ケ"],
7b21e5c
             ":@": ["", "げ"],
7b21e5c
@@ -70,18 +100,30 @@
7b21e5c
             "}": ["", "」"],
7b21e5c
             "z": ["", "つ", "ツ", "つ", "ツ"],
7b21e5c
             "z@": ["", "づ"],
7b21e5c
+            "Z@": ["", "づ"],
7b21e5c
             "Z": ["", "っ"],
7b21e5c
             "x": ["", "さ", "サ", "さ", "サ"],
7b21e5c
+            "X": ["", "さ", "サ", "さ", "サ"],
7b21e5c
             "x@": ["", "ざ"],
7b21e5c
+            "X@": ["", "ざ"],
7b21e5c
             "c": ["", "そ", "ソ", "そ", "ソ"],
7b21e5c
+            "C": ["", "そ", "ソ", "そ", "ソ"],
7b21e5c
             "c@": ["", "ぞ"],
7b21e5c
+            "C@": ["", "ぞ"],
7b21e5c
             "v": ["", "ひ", "ヒ", "ひ", "ヒ"],
7b21e5c
+            "V": ["", "ひ", "ヒ", "ひ", "ヒ"],
7b21e5c
             "v@": ["", "び"],
7b21e5c
+            "V@": ["", "び"],
7b21e5c
             "v[": ["", "ぴ"],
7b21e5c
+            "V[": ["", "ぴ"],
7b21e5c
             "b": ["", "こ", "コ", "こ", "コ"],
7b21e5c
+            "B": ["", "こ", "コ", "こ", "コ"],
7b21e5c
             "b@": ["", "ご"],
7b21e5c
+            "B@": ["", "ご"],
7b21e5c
             "n": ["", "み"],
7b21e5c
+            "N": ["", "み"],
7b21e5c
             "m": ["", "も"],
7b21e5c
+            "M": ["", "も"],
7b21e5c
             ",": ["", "ね"],
7b21e5c
             "<": ["", "、"],
7b21e5c
             ".": ["", "る"],
7b21e5c
diff --git a/data/rules/keymap-schema.json b/data/rules/keymap-schema.json
7b21e5c
new file mode 100644
7b21e5c
index 0000000..34c945e
7b21e5c
--- /dev/null
7b21e5c
+++ b/data/rules/keymap-schema.json
7b21e5c
@@ -0,0 +1,54 @@
7b21e5c
+{
7b21e5c
+    "type": "object",
7b21e5c
+    "properties": {
7b21e5c
+	"include": {
7b21e5c
+	    "type": "array",
7b21e5c
+	    "items": {
7b21e5c
+		"type": "string"
7b21e5c
+	    }
7b21e5c
+	},
7b21e5c
+	"define" : {
7b21e5c
+	    "type": "object",
7b21e5c
+	    "properties": {
7b21e5c
+		"keymap": {
7b21e5c
+		    "type": "object",
7b21e5c
+		    "patternProperties": {
7b21e5c
+			".*": {
7b21e5c
+			    "enum": [
7b21e5c
+				null,
7b21e5c
+				"abort",
7b21e5c
+				"first-segment",
7b21e5c
+				"last-segment",
7b21e5c
+				"commit",
7b21e5c
+				"complete",
7b21e5c
+				"delete",
7b21e5c
+				"delete-forward",
7b21e5c
+				"quote",
7b21e5c
+				"register",
7b21e5c
+				"next-candidate"
7b21e5c
+				"previous-candidate",
7b21e5c
+				"purge-candidate",
7b21e5c
+				"next-segment",
7b21e5c
+				"previous-segment",
7b21e5c
+				"expand-segment",
7b21e5c
+				"shrink-segment",
7b21e5c
+				"set-input-mode-hiragana",
7b21e5c
+				"set-input-mode-katakana",
7b21e5c
+				"set-input-mode-hankaku-katakana",
7b21e5c
+				"set-input-mode-latin",
7b21e5c
+				"set-input-mode-wide-latin",
7b21e5c
+				"set-input-mode-direct",
7b21e5c
+				"convert-hiragana",
7b21e5c
+				"convert-katakana",
7b21e5c
+				"convert-hankaku-katakana",
7b21e5c
+				"convert-latin",
7b21e5c
+				"convert-wide-latin",
7b21e5c
+				"original-candidate"
7b21e5c
+			    ]
7b21e5c
+			}
7b21e5c
+		    }
7b21e5c
+		}
7b21e5c
+	    }
7b21e5c
+	}
7b21e5c
+    }
7b21e5c
+}
7b21e5c
diff --git a/data/rules/kzik/Makefile.am b/data/rules/kzik/Makefile.am
7b21e5c
index fb38c42..fa900b5 100644
7b21e5c
--- a/data/rules/kzik/Makefile.am
7b21e5c
+++ b/data/rules/kzik/Makefile.am
7b21e5c
@@ -8,24 +8,16 @@ files =						\
7b21e5c
 	keymap/latin.json			\
7b21e5c
 	keymap/wide-latin.json			\
7b21e5c
 	rom-kana/default.json			\
7b21e5c
-	$(NULL)
7b21e5c
-metadata_in =					\
7b21e5c
-	metadata.json.in			\
7b21e5c
+	metadata.json				\
7b21e5c
 	$(NULL)
7b21e5c
 
7b21e5c
 nobase_rules_DATA =				\
7b21e5c
 	$(files)				\
7b21e5c
-	$(metadata_in:.in=)			\
7b21e5c
 	$(NULL)
7b21e5c
 
7b21e5c
 EXTRA_DIST =					\
7b21e5c
 	$(files)				\
7b21e5c
-	$(metadata_in)				\
7b21e5c
-	$(NULL)
7b21e5c
-
7b21e5c
-CLEANFILES =					\
7b21e5c
-	metadata.json				\
7b21e5c
 	$(NULL)
7b21e5c
 
7b21e5c
--include $(top_srcdir)/data/rules/rule.mk
7b21e5c
+include $(top_srcdir)/data/rules/rule.mk
7b21e5c
 -include $(top_srcdir)/git.mk
7b21e5c
diff --git a/data/rules/kzik/keymap/default.json b/data/rules/kzik/keymap/default.json
7b21e5c
index 307b53a..828a178 100644
7b21e5c
--- a/data/rules/kzik/keymap/default.json
7b21e5c
+++ b/data/rules/kzik/keymap/default.json
7b21e5c
@@ -1,10 +1,5 @@
7b21e5c
 {
7b21e5c
     "include": [
7b21e5c
         "default/default"
7b21e5c
-    ],
7b21e5c
-    "define": {
7b21e5c
-        "keymap": {
7b21e5c
-            ":": "upper-;"
7b21e5c
-        }
7b21e5c
-    }
7b21e5c
+    ]
7b21e5c
 }
7b21e5c
diff --git a/data/rules/kzik/metadata.json b/data/rules/kzik/metadata.json
7b21e5c
new file mode 100644
7b21e5c
index 0000000..5f79ac1
7b21e5c
--- /dev/null
7b21e5c
+++ b/data/rules/kzik/metadata.json
7b21e5c
@@ -0,0 +1,5 @@
7b21e5c
+{
7b21e5c
+    "name": "KZIK",
7b21e5c
+    "description": "Extended romaji input method based on AZIK, developed by OHASHI Hideya <http://ohac.sytes.net/pukiwiki.php?kzik>",
7b21e5c
+    "priority": 10
7b21e5c
+}
7b21e5c
diff --git a/data/rules/kzik/metadata.json.in b/data/rules/kzik/metadata.json.in
7b21e5c
deleted file mode 100644
7b21e5c
index 6472671..0000000
7b21e5c
--- a/data/rules/kzik/metadata.json.in
7b21e5c
+++ /dev/null
7b21e5c
@@ -1,5 +0,0 @@
7b21e5c
-{
7b21e5c
-    "name": _("KZIK"),
7b21e5c
-    "description": _("Extended romaji input method based on AZIK, developed by OHASHI Hideya <http://ohac.sytes.net/pukiwiki.php?kzik>"),
7b21e5c
-    "priority": 10
7b21e5c
-}
7b21e5c
diff --git a/data/rules/metadata-schema.json b/data/rules/metadata-schema.json
7b21e5c
new file mode 100644
7b21e5c
index 0000000..584b3c2
7b21e5c
--- /dev/null
7b21e5c
+++ b/data/rules/metadata-schema.json
7b21e5c
@@ -0,0 +1,16 @@
7b21e5c
+{
7b21e5c
+    "type": "object",
7b21e5c
+    "properties": {
7b21e5c
+	"name": {
7b21e5c
+	    "type": "string"
7b21e5c
+	},
7b21e5c
+	"description": {
7b21e5c
+	    "type": "string"
7b21e5c
+	},
7b21e5c
+	"priority": {
7b21e5c
+	    "type": "integer",
7b21e5c
+	    "minimum": 0,
7b21e5c
+	    "maximum": 100
7b21e5c
+	}
7b21e5c
+    }
7b21e5c
+}
7b21e5c
diff --git a/data/rules/nicola/Makefile.am b/data/rules/nicola/Makefile.am
7b21e5c
index 045ced1..ae7dbe9 100644
7b21e5c
--- a/data/rules/nicola/Makefile.am
7b21e5c
+++ b/data/rules/nicola/Makefile.am
7b21e5c
@@ -9,24 +9,16 @@ files =						\
7b21e5c
 	keymap/wide-latin.json			\
7b21e5c
 	keymap/direct.json			\
7b21e5c
 	rom-kana/default.json			\
7b21e5c
-	$(NULL)
7b21e5c
-metadata_in =					\
7b21e5c
-	metadata.json.in			\
7b21e5c
+	metadata.json				\
7b21e5c
 	$(NULL)
7b21e5c
 
7b21e5c
 nobase_rules_DATA =				\
7b21e5c
 	$(files)				\
7b21e5c
-	$(metadata_in:.in=)			\
7b21e5c
 	$(NULL)
7b21e5c
 
7b21e5c
 EXTRA_DIST =					\
7b21e5c
 	$(files)				\
7b21e5c
-	$(metadata_in)				\
7b21e5c
-	$(NULL)
7b21e5c
-
7b21e5c
-CLEANFILES =					\
7b21e5c
-	metadata.json				\
7b21e5c
 	$(NULL)
7b21e5c
 
7b21e5c
--include $(top_srcdir)/data/rules/rule.mk
7b21e5c
+include $(top_srcdir)/data/rules/rule.mk
7b21e5c
 -include $(top_srcdir)/git.mk
7b21e5c
diff --git a/data/rules/nicola/metadata.json b/data/rules/nicola/metadata.json
7b21e5c
new file mode 100644
7b21e5c
index 0000000..85b1398
7b21e5c
--- /dev/null
7b21e5c
+++ b/data/rules/nicola/metadata.json
7b21e5c
@@ -0,0 +1,6 @@
7b21e5c
+{
7b21e5c
+    "name": "NICOLA",
7b21e5c
+    "description": "Input method using thumb shift keyboard developed by the NICOLA (NIhongo-nyuuryoku COnsortium LAyout) project <http://nicola.sunicom.co.jp/index.html>",
7b21e5c
+    "filter": "nicola",
7b21e5c
+    "priority": 10
7b21e5c
+}
7b21e5c
diff --git a/data/rules/nicola/metadata.json.in b/data/rules/nicola/metadata.json.in
7b21e5c
deleted file mode 100644
7b21e5c
index 2458e40..0000000
7b21e5c
--- a/data/rules/nicola/metadata.json.in
7b21e5c
+++ /dev/null
7b21e5c
@@ -1,6 +0,0 @@
7b21e5c
-{
7b21e5c
-    "name": _("NICOLA"),
7b21e5c
-    "description": _("Input method using thumb shift keyboard developed by the NICOLA (NIhongo-nyuuryoku COnsortium LAyout) project <http://nicola.sunicom.co.jp/index.html>"),
7b21e5c
-    "filter": "nicola",
7b21e5c
-    "priority": 10
7b21e5c
-}
7b21e5c
diff --git a/data/rules/rom-kana-schema.json b/data/rules/rom-kana-schema.json
7b21e5c
new file mode 100644
7b21e5c
index 0000000..84b0fde
7b21e5c
--- /dev/null
7b21e5c
+++ b/data/rules/rom-kana-schema.json
7b21e5c
@@ -0,0 +1,36 @@
7b21e5c
+{
7b21e5c
+    "type": "object",
7b21e5c
+    "properties": {
7b21e5c
+	"include": {
7b21e5c
+	    "type": "array",
7b21e5c
+	    "items": {
7b21e5c
+		"type": "string"
7b21e5c
+	    }
7b21e5c
+	},
7b21e5c
+	"define" : {
7b21e5c
+	    "type": "object",
7b21e5c
+	    "properties": {
7b21e5c
+		"rom-kana": {
7b21e5c
+		    "type": "object",
7b21e5c
+		    "patternProperties": {
7b21e5c
+			".*": {
7b21e5c
+			    "anyOf": [
7b21e5c
+				{
7b21e5c
+				    "type": "array",
7b21e5c
+				    "minItems": 2,
7b21e5c
+				    "maxItems": 5,
7b21e5c
+				    "items": {
7b21e5c
+					"type": "string"
7b21e5c
+				    }
7b21e5c
+				},
7b21e5c
+				{
7b21e5c
+				    "type": "null"
7b21e5c
+				}
7b21e5c
+			    ]
7b21e5c
+			}
7b21e5c
+		    }
7b21e5c
+		}
7b21e5c
+	    }
7b21e5c
+	}
7b21e5c
+    }
7b21e5c
+}
7b21e5c
diff --git a/data/rules/rule.mk b/data/rules/rule.mk
7b21e5c
index 1da19d1..f125d80 100644
7b21e5c
--- a/data/rules/rule.mk
7b21e5c
+++ b/data/rules/rule.mk
7b21e5c
@@ -1,9 +1,26 @@
7b21e5c
-.SUFFIXES: .json .json.in
7b21e5c
+SUFFIXES = .json .pot
7b21e5c
 
7b21e5c
-edit = sed -e 's!\(^ *"[^"]*": *\)_(\("[^"]*"\))!\1\2!g'
7b21e5c
-.json.in.json:
7b21e5c
+.json.pot:
7b21e5c
 	$(AM_V_GEN) rm -f $@ $@.tmp; \
7b21e5c
 	srcdir=''; \
7b21e5c
 	  test -f ./$< || srcdir=$(srcdir)/; \
7b21e5c
-	  $(edit) $${srcdir}$< >$@.tmp; \
7b21e5c
-	mv $@.tmp $@
7b21e5c
+	  $(top_builddir)/tools/gen-metadata-pot $${srcdir}$< \
7b21e5c
+            '$$.name' '$$.description' >$@.tmp && mv $@.tmp $@
7b21e5c
+
7b21e5c
+# 'make check' in po/ requires metadata.pot
7b21e5c
+all-local: metadata.pot
7b21e5c
+
7b21e5c
+check-local:
7b21e5c
+	$(AM_V_at)$(JSON_VALIDATE) \
7b21e5c
+		--schema $(top_srcdir)/data/rules/metadata-schema.json \
7b21e5c
+		metadata.json
7b21e5c
+	$(AM_V_at)$(JSON_VALIDATE) \
7b21e5c
+		--schema $(top_srcdir)/data/rules/keymap-schema.json \
7b21e5c
+		keymap/*.json
7b21e5c
+	$(AM_V_at)$(JSON_VALIDATE) \
7b21e5c
+		--schema $(top_srcdir)/data/rules/rom-kana-schema.json \
7b21e5c
+		rom-kana/*.json
7b21e5c
+
7b21e5c
+metadata.pot: metadata.json $(top_srcdir)/tools/gen-metadata-pot.c
7b21e5c
+
7b21e5c
+EXTRA_DIST += metadata.pot
7b21e5c
diff --git a/data/rules/tcode/Makefile.am b/data/rules/tcode/Makefile.am
7b21e5c
index 02e8fce..9e0f9ef 100644
7b21e5c
--- a/data/rules/tcode/Makefile.am
7b21e5c
+++ b/data/rules/tcode/Makefile.am
7b21e5c
@@ -7,24 +7,16 @@ files =						\
7b21e5c
 	keymap/latin.json			\
7b21e5c
 	keymap/wide-latin.json			\
7b21e5c
 	rom-kana/default.json			\
7b21e5c
-	$(NULL)
7b21e5c
-metadata_in =					\
7b21e5c
-	metadata.json.in			\
7b21e5c
+	metadata.json				\
7b21e5c
 	$(NULL)
7b21e5c
 
7b21e5c
 nobase_rules_DATA =				\
7b21e5c
 	$(files)				\
7b21e5c
-	$(metadata_in:.in=)			\
7b21e5c
 	$(NULL)
7b21e5c
 
7b21e5c
 EXTRA_DIST =					\
7b21e5c
 	$(files)				\
7b21e5c
-	$(metadata_in)				\
7b21e5c
-	$(NULL)
7b21e5c
-
7b21e5c
-CLEANFILES =					\
7b21e5c
-	metadata.json				\
7b21e5c
 	$(NULL)
7b21e5c
 
7b21e5c
--include $(top_srcdir)/data/rules/rule.mk
7b21e5c
+include $(top_srcdir)/data/rules/rule.mk
7b21e5c
 -include $(top_srcdir)/git.mk
7b21e5c
diff --git a/data/rules/tcode/metadata.json b/data/rules/tcode/metadata.json
7b21e5c
new file mode 100644
7b21e5c
index 0000000..1ea7221
7b21e5c
--- /dev/null
7b21e5c
+++ b/data/rules/tcode/metadata.json
7b21e5c
@@ -0,0 +1,5 @@
7b21e5c
+{
7b21e5c
+    "name": "T-Code",
7b21e5c
+    "description": "Japanese direct input method developed by the T-Code project <http://openlab.jp/tcode/>",
7b21e5c
+    "priority": 10
7b21e5c
+}
7b21e5c
diff --git a/data/rules/tcode/metadata.json.in b/data/rules/tcode/metadata.json.in
7b21e5c
deleted file mode 100644
7b21e5c
index e127f3b..0000000
7b21e5c
--- a/data/rules/tcode/metadata.json.in
7b21e5c
+++ /dev/null
7b21e5c
@@ -1,5 +0,0 @@
7b21e5c
-{
7b21e5c
-    "name": _("T-Code"),
7b21e5c
-    "description": _("Japanese direct input method developed by the T-Code project <http://openlab.jp/tcode/>"),
7b21e5c
-    "priority": 10
7b21e5c
-}
7b21e5c
diff --git a/data/rules/trycode/Makefile.am b/data/rules/trycode/Makefile.am
7b21e5c
index f17c9e9..23eef01 100644
7b21e5c
--- a/data/rules/trycode/Makefile.am
7b21e5c
+++ b/data/rules/trycode/Makefile.am
7b21e5c
@@ -7,24 +7,16 @@ files =						\
7b21e5c
 	keymap/latin.json			\
7b21e5c
 	keymap/wide-latin.json			\
7b21e5c
 	rom-kana/default.json			\
7b21e5c
-	$(NULL)
7b21e5c
-metadata_in =					\
7b21e5c
-	metadata.json.in			\
7b21e5c
+	metadata.json				\
7b21e5c
 	$(NULL)
7b21e5c
 
7b21e5c
 nobase_rules_DATA =				\
7b21e5c
 	$(files)				\
7b21e5c
-	$(metadata_in:.in=)			\
7b21e5c
 	$(NULL)
7b21e5c
 
7b21e5c
 EXTRA_DIST =					\
7b21e5c
 	$(files)				\
7b21e5c
-	$(metadata_in)				\
7b21e5c
-	$(NULL)
7b21e5c
-
7b21e5c
-CLEANFILES =					\
7b21e5c
-	metadata.json				\
7b21e5c
 	$(NULL)
7b21e5c
 
7b21e5c
--include $(top_srcdir)/data/rules/rule.mk
7b21e5c
+include $(top_srcdir)/data/rules/rule.mk
7b21e5c
 -include $(top_srcdir)/git.mk
7b21e5c
diff --git a/data/rules/trycode/metadata.json b/data/rules/trycode/metadata.json
7b21e5c
new file mode 100644
7b21e5c
index 0000000..7b9ad7a
7b21e5c
--- /dev/null
7b21e5c
+++ b/data/rules/trycode/metadata.json
7b21e5c
@@ -0,0 +1,5 @@
7b21e5c
+{
7b21e5c
+    "name": "TRY-CODE",
7b21e5c
+    "description": "Japanese direct input method based on T-Code, developed by Naoto Takahashi <http://www.m17n.org/ntakahas/npx/aggressive/aggressive4.en.html>",
7b21e5c
+    "priority": 10
7b21e5c
+}
7b21e5c
diff --git a/data/rules/trycode/metadata.json.in b/data/rules/trycode/metadata.json.in
7b21e5c
deleted file mode 100644
7b21e5c
index c95340f..0000000
7b21e5c
--- a/data/rules/trycode/metadata.json.in
7b21e5c
+++ /dev/null
7b21e5c
@@ -1,5 +0,0 @@
7b21e5c
-{
7b21e5c
-    "name": _("TRY-CODE"),
7b21e5c
-    "description": _("Japanese direct input method based on T-Code, developed by Naoto Takahashi <http://www.m17n.org/ntakahas/npx/aggressive/aggressive4.en.html>"),
7b21e5c
-    "priority": 10
7b21e5c
-}
7b21e5c
diff --git a/data/rules/tutcode-touch16x/Makefile.am b/data/rules/tutcode-touch16x/Makefile.am
7b21e5c
index d432306..af14949 100644
7b21e5c
--- a/data/rules/tutcode-touch16x/Makefile.am
7b21e5c
+++ b/data/rules/tutcode-touch16x/Makefile.am
7b21e5c
@@ -7,24 +7,16 @@ files =						\
7b21e5c
 	keymap/latin.json			\
7b21e5c
 	keymap/wide-latin.json			\
7b21e5c
 	rom-kana/default.json			\
7b21e5c
-	$(NULL)
7b21e5c
-metadata_in =					\
7b21e5c
-	metadata.json.in			\
7b21e5c
+	metadata.json				\
7b21e5c
 	$(NULL)
7b21e5c
 
7b21e5c
 nobase_rules_DATA =				\
7b21e5c
 	$(files)				\
7b21e5c
-	$(metadata_in:.in=)			\
7b21e5c
 	$(NULL)
7b21e5c
 
7b21e5c
 EXTRA_DIST =					\
7b21e5c
 	$(files)				\
7b21e5c
-	$(metadata_in)				\
7b21e5c
-	$(NULL)
7b21e5c
-
7b21e5c
-CLEANFILES =					\
7b21e5c
-	metadata.json				\
7b21e5c
 	$(NULL)
7b21e5c
 
7b21e5c
--include $(top_srcdir)/data/rules/rule.mk
7b21e5c
+include $(top_srcdir)/data/rules/rule.mk
7b21e5c
 -include $(top_srcdir)/git.mk
7b21e5c
diff --git a/data/rules/tutcode-touch16x/metadata.json b/data/rules/tutcode-touch16x/metadata.json
7b21e5c
new file mode 100644
7b21e5c
index 0000000..d8a41b1
7b21e5c
--- /dev/null
7b21e5c
+++ b/data/rules/tutcode-touch16x/metadata.json
7b21e5c
@@ -0,0 +1,5 @@
7b21e5c
+{
7b21e5c
+    "name": "TUT-Code (Touch16+)",
7b21e5c
+    "description": "TUT-Code with Touch16+ extension",
7b21e5c
+    "priority": 10
7b21e5c
+}
7b21e5c
diff --git a/data/rules/tutcode-touch16x/metadata.json.in b/data/rules/tutcode-touch16x/metadata.json.in
7b21e5c
deleted file mode 100644
7b21e5c
index 1112c7b..0000000
7b21e5c
--- a/data/rules/tutcode-touch16x/metadata.json.in
7b21e5c
+++ /dev/null
7b21e5c
@@ -1,5 +0,0 @@
7b21e5c
-{
7b21e5c
-    "name": _("TUT-Code (Touch16+)"),
7b21e5c
-    "description": _("TUT-Code with Touch16+ extension"),
7b21e5c
-    "priority": 10
7b21e5c
-}
7b21e5c
diff --git a/data/rules/tutcode/Makefile.am b/data/rules/tutcode/Makefile.am
7b21e5c
index 486f003..ce4b99f 100644
7b21e5c
--- a/data/rules/tutcode/Makefile.am
7b21e5c
+++ b/data/rules/tutcode/Makefile.am
7b21e5c
@@ -7,24 +7,16 @@ files =						\
7b21e5c
 	keymap/latin.json			\
7b21e5c
 	keymap/wide-latin.json			\
7b21e5c
 	rom-kana/default.json			\
7b21e5c
-	$(NULL)
7b21e5c
-metadata_in =					\
7b21e5c
-	metadata.json.in			\
7b21e5c
+	metadata.json				\
7b21e5c
 	$(NULL)
7b21e5c
 
7b21e5c
 nobase_rules_DATA =				\
7b21e5c
 	$(files)				\
7b21e5c
-	$(metadata_in:.in=)			\
7b21e5c
 	$(NULL)
7b21e5c
 
7b21e5c
 EXTRA_DIST =					\
7b21e5c
 	$(files)				\
7b21e5c
-	$(metadata_in)				\
7b21e5c
-	$(NULL)
7b21e5c
-
7b21e5c
-CLEANFILES =					\
7b21e5c
-	metadata.json				\
7b21e5c
 	$(NULL)
7b21e5c
 
7b21e5c
--include $(top_srcdir)/data/rules/rule.mk
7b21e5c
+include $(top_srcdir)/data/rules/rule.mk
7b21e5c
 -include $(top_srcdir)/git.mk
7b21e5c
diff --git a/data/rules/tutcode/metadata.json b/data/rules/tutcode/metadata.json
7b21e5c
new file mode 100644
7b21e5c
index 0000000..1d1685f
7b21e5c
--- /dev/null
7b21e5c
+++ b/data/rules/tutcode/metadata.json
7b21e5c
@@ -0,0 +1,5 @@
7b21e5c
+{
7b21e5c
+    "name": "TUT-Code",
7b21e5c
+    "description": "Japanese direct input method developed by Hajime Ohiwa and Takaaki Takashima <http://www.crew.sfc.keio.ac.jp/~chk/intro.html>",
7b21e5c
+    "priority": 10
7b21e5c
+}
7b21e5c
diff --git a/data/rules/tutcode/metadata.json.in b/data/rules/tutcode/metadata.json.in
7b21e5c
deleted file mode 100644
7b21e5c
index 8bc11ff..0000000
7b21e5c
--- a/data/rules/tutcode/metadata.json.in
7b21e5c
+++ /dev/null
7b21e5c
@@ -1,5 +0,0 @@
7b21e5c
-{
7b21e5c
-    "name": _("TUT-Code"),
7b21e5c
-    "description": _("Japanese direct input method developed by Hajime Ohiwa and Takaaki Takashima <http://www.crew.sfc.keio.ac.jp/~chk/intro.html>"),
7b21e5c
-    "priority": 10
7b21e5c
-}
5151fb2
diff --git a/data/templates/libkkc-data/tools/genfilter.py b/data/templates/libkkc-data/tools/genfilter.py
5151fb2
index 5ffab32..0c5f75a 100644
5151fb2
--- a/data/templates/libkkc-data/tools/genfilter.py
5151fb2
+++ b/data/templates/libkkc-data/tools/genfilter.py
5151fb2
@@ -84,24 +84,24 @@ class FilterGenerator(object):
5151fb2
 
5151fb2
     def generate(self):
5151fb2
         size = os.fstat(self.infile.fileno()).st_size
5151fb2
-        n = size / self.record_size
5151fb2
+        n = size // self.record_size
5151fb2
         m = int(math.ceil(-n*math.log10(ERROR_RATE) /
5151fb2
                           math.pow(math.log10(2), 2)))
5151fb2
-        m = (m/8 + 1)*8
5151fb2
+        m = (m//8 + 1)*8
5151fb2
         inmem = mmap.mmap(self.infile.fileno(),
5151fb2
                           size,
5151fb2
                           access=mmap.ACCESS_READ)
5151fb2
-        outmem = bytearray(m/8)
5151fb2
-        for i in xrange(0, n):
5151fb2
+        outmem = bytearray(m//8)
5151fb2
+        for i in range(0, n):
5151fb2
             offset = i*self.record_size
5151fb2
             b0, b1 = struct.unpack("=LL", inmem[offset:offset+8])
5151fb2
-            for k in xrange(0, 4):
5151fb2
+            for k in range(0, 4):
5151fb2
                 h = murmur_hash3_32(b0, b1, k)
5151fb2
                 h = int(h * (m / float(0xFFFFFFFF)))
5151fb2
-                outmem[h/8] |= (1 << (h%8))
5151fb2
+                outmem[h//8] |= (1 << (h%8))
5151fb2
         inmem.close()
5151fb2
-        # Convert bytearray to str, for Python 2.6 compatibility.
5151fb2
-        self.outfile.write(str(outmem))
5151fb2
+        # Convert bytearray to bytes, for Python 3 compatibility.
5151fb2
+        self.outfile.write(bytes(outmem))
5151fb2
 
5151fb2
 if __name__ == '__main__':
5151fb2
     import sys
5151fb2
@@ -110,7 +110,7 @@ if __name__ == '__main__':
5151fb2
     parser = argparse.ArgumentParser(description='filter')
5151fb2
     parser.add_argument('infile', type=argparse.FileType('r'),
5151fb2
                         help='input file')
5151fb2
-    parser.add_argument('outfile', type=argparse.FileType('w'),
5151fb2
+    parser.add_argument('outfile', type=argparse.FileType('wb'),
5151fb2
                         help='output file')
5151fb2
     parser.add_argument('record_size', type=int,
5151fb2
                         help='record size')
5151fb2
diff --git a/data/templates/libkkc-data/tools/sortlm.py b/data/templates/libkkc-data/tools/sortlm.py
5151fb2
index a0dd8fe..40f0837 100644
5151fb2
--- a/data/templates/libkkc-data/tools/sortlm.py
5151fb2
+++ b/data/templates/libkkc-data/tools/sortlm.py
5151fb2
@@ -40,10 +40,10 @@ class SortedGenerator(object):
5151fb2
         self.__min_cost = 0.0
5151fb2
 
5151fb2
     def read(self):
5151fb2
-        print "reading N-grams"
5151fb2
+        print("reading N-grams")
5151fb2
         self.__read_tries()
5151fb2
         self.__read_ngrams()
5151fb2
-        print "min cost = %lf" % self.__min_cost
5151fb2
+        print("min cost = %lf" % self.__min_cost)
5151fb2
 
5151fb2
     def __read_tries(self):
5151fb2
         while True:
5151fb2
@@ -58,7 +58,7 @@ class SortedGenerator(object):
5151fb2
             line = self.__infile.readline()
5151fb2
             if line == "":
5151fb2
                 break
5151fb2
-            line = line.strip()
5151fb2
+            line = line.strip('\n')
5151fb2
             if line == "":
5151fb2
                 break
5151fb2
             match = self.__ngram_line_regex.match(line)
5151fb2
@@ -89,7 +89,7 @@ class SortedGenerator(object):
5151fb2
                 line = self.__infile.readline()
5151fb2
                 if line == "":
5151fb2
                     break
5151fb2
-                line = line.strip()
5151fb2
+                line = line.strip('\n')
5151fb2
                 if line == "":
5151fb2
                     break
5151fb2
                 match = self.__ngram_line_regex.match(line)
5151fb2
@@ -125,14 +125,11 @@ class SortedGenerator(object):
5151fb2
         def quantize(cost, min_cost):
5151fb2
             return max(0, min(65535, int(cost * 65535 / min_cost)))
5151fb2
 
5151fb2
-        def cmp_header(a, b):
5151fb2
-            return cmp(a[0], b[0])
5151fb2
-
5151fb2
-        print "writing 1-gram file"
5151fb2
+        print("writing 1-gram file")
5151fb2
         unigram_offsets = {}
5151fb2
         unigram_file = open("%s.1gram" % self.__output_prefix, "wb")
5151fb2
         offset = 0
5151fb2
-        for ids, value in sorted(self.__ngram_entries[0].iteritems()):
5151fb2
+        for ids, value in sorted(self.__ngram_entries[0].items()):
5151fb2
             unigram_offsets[ids[0]] = offset
5151fb2
             s = struct.pack("=HHH",
5151fb2
                             quantize(value[0], self.__min_cost),
5151fb2
@@ -143,13 +140,13 @@ class SortedGenerator(object):
5151fb2
             offset += 1
5151fb2
         unigram_file.close()
5151fb2
 
5151fb2
-        print "writing 2-gram file"
5151fb2
+        print("writing 2-gram file")
5151fb2
         bigram_offsets = {}
5151fb2
         bigram_file = open("%s.2gram" % self.__output_prefix, "wb")
5151fb2
         keys = self.__ngram_entries[1].keys()
5151fb2
         items = [(struct.pack("=LL", ids[1], unigram_offsets[ids[0]]), ids) for ids in keys]
5151fb2
         offset = 0
5151fb2
-        for header, ids in sorted(items, cmp=cmp_header):
5151fb2
+        for header, ids in sorted(items, key=lambda x: x[0]):
5151fb2
             value = self.__ngram_entries[1][ids]
5151fb2
             bigram_offsets[ids] = offset
5151fb2
             s = struct.pack("=HH",
5151fb2
@@ -160,11 +157,11 @@ class SortedGenerator(object):
5151fb2
         bigram_file.close()
5151fb2
 
5151fb2
         if len(self.__ngram_entries[2]) > 0:
5151fb2
-            print "writing 3-gram file"
5151fb2
+            print("writing 3-gram file")
5151fb2
             trigram_file = open("%s.3gram" % self.__output_prefix, "wb")
5151fb2
             keys = self.__ngram_entries[2].keys()
5151fb2
             items = [(struct.pack("=LL", ids[2], bigram_offsets[(ids[0], ids[1])]), ids) for ids in keys]
5151fb2
-            for header, ids in sorted(items, cmp=cmp_header):
5151fb2
+            for header, ids in sorted(items, key=lambda x: x[0]):
5151fb2
                 value = self.__ngram_entries[2][ids]
5151fb2
                 s = struct.pack("=H",
5151fb2
                                 quantize(value[0], self.__min_cost))
7b21e5c
diff --git a/libkkc/Makefile.am b/libkkc/Makefile.am
7b21e5c
index 02ca2ab..28a9f68 100644
7b21e5c
--- a/libkkc/Makefile.am
7b21e5c
+++ b/libkkc/Makefile.am
7b21e5c
@@ -108,6 +108,7 @@ libkkc_shell_sources =				\
7b21e5c
 	template.vala				\
7b21e5c
 	numeric-template.vala			\
7b21e5c
 	expression.vala				\
7b21e5c
+	server.vala				\
7b21e5c
 	$(NULL)
7b21e5c
 
7b21e5c
 libkkc_la_SOURCES =				\
7b21e5c
diff --git a/libkkc/candidate-list.vala b/libkkc/candidate-list.vala
7b21e5c
index 92ddb79..e6e6bfe 100644
7b21e5c
--- a/libkkc/candidate-list.vala
7b21e5c
+++ b/libkkc/candidate-list.vala
7b21e5c
@@ -122,7 +122,8 @@ namespace Kkc {
7b21e5c
         }
7b21e5c
 
7b21e5c
         uint get_page_start_cursor_pos (uint pos) {
7b21e5c
-            return (pos / page_size) * page_size;
7b21e5c
+            var page_index = (pos - page_start) / page_size;
7b21e5c
+            return page_index * page_size + page_start;
7b21e5c
         }
7b21e5c
 
7b21e5c
         /**
7b21e5c
@@ -210,23 +211,33 @@ namespace Kkc {
7b21e5c
             }
7b21e5c
         }
7b21e5c
 
7b21e5c
+        bool update_cursor_pos (uint pos) {
7b21e5c
+            if (0 <= pos && pos < _candidates.size && pos != _cursor_pos) {
7b21e5c
+                _cursor_pos = (int) pos;
7b21e5c
+                notify_property ("cursor-pos");
7b21e5c
+                return true;
7b21e5c
+            }
7b21e5c
+            return false;
7b21e5c
+        }
7b21e5c
+
7b21e5c
         bool cursor_move (int step) {
7b21e5c
             if (_candidates.is_empty || step == 0)
7b21e5c
                 return false;
7b21e5c
 
7b21e5c
+            int start = _cursor_pos - (int) page_start;
7b21e5c
+            int total = (int) _candidates.size - (int) page_start;
7b21e5c
+
7b21e5c
             if (round) {
7b21e5c
-                var pos = (_cursor_pos + step) % _candidates.size;
7b21e5c
+                int pos = (start + step) % total;
7b21e5c
                 if (pos < 0)
7b21e5c
-                    pos += _candidates.size;
7b21e5c
-                _cursor_pos = pos;
7b21e5c
-                notify_property ("cursor-pos");
7b21e5c
-                return true;
7b21e5c
-            } else {
7b21e5c
-                var pos = _cursor_pos + step;
7b21e5c
-                if (0 <= pos && pos < _candidates.size) {
7b21e5c
-                    _cursor_pos = pos;
7b21e5c
-                    notify_property ("cursor-pos");
7b21e5c
+                    pos += total;
7b21e5c
+                if (update_cursor_pos (pos + page_start))
7b21e5c
                     return true;
7b21e5c
+            } else {
7b21e5c
+                var pos = start + step;
7b21e5c
+                if (0 <= pos && pos < total) {
7b21e5c
+                    if (update_cursor_pos (pos + page_start))
7b21e5c
+                        return true;
7b21e5c
                 }
7b21e5c
             }
7b21e5c
 
7b21e5c
@@ -239,7 +250,11 @@ namespace Kkc {
7b21e5c
          * @return `true` if cursor position has changed, `false` otherwise.
7b21e5c
          */
7b21e5c
         public bool cursor_up () {
7b21e5c
-            return cursor_move (-1);
7b21e5c
+            if (_cursor_pos >= page_start)
7b21e5c
+                return cursor_move (-1);
7b21e5c
+            else if (update_cursor_pos (_cursor_pos - 1))
7b21e5c
+                return true;
7b21e5c
+            return false;
7b21e5c
         }
7b21e5c
 
7b21e5c
         /**
7b21e5c
@@ -248,32 +263,35 @@ namespace Kkc {
7b21e5c
          * @return `true` if cursor position has changed, `false` otherwise
7b21e5c
          */
7b21e5c
         public bool cursor_down () {
7b21e5c
-            return cursor_move (1);
7b21e5c
+            if (_cursor_pos >= page_start)
7b21e5c
+                return cursor_move (1);
7b21e5c
+            else if (update_cursor_pos (_cursor_pos + 1))
7b21e5c
+                return true;
7b21e5c
+            return false;
7b21e5c
         }
7b21e5c
 
7b21e5c
         bool page_move (int step) {
7b21e5c
             if (_candidates.is_empty || step == 0)
7b21e5c
                 return false;
7b21e5c
 
7b21e5c
+            int start = _cursor_pos - (int) page_start;
7b21e5c
+            int total = (int) _candidates.size - (int) page_start;
7b21e5c
+
7b21e5c
             if (round) {
7b21e5c
-                var pos = (_cursor_pos + page_size * step) % _candidates.size;
7b21e5c
+                int pos = (start + (int) page_size * step) % total;
7b21e5c
                 if (pos < 0)
7b21e5c
-                    pos += _candidates.size;
7b21e5c
-                pos = get_page_start_cursor_pos (pos);
7b21e5c
-                if (pos != _cursor_pos) {
7b21e5c
-                    _cursor_pos = (int) pos;
7b21e5c
-                    notify_property ("cursor-pos");
7b21e5c
-                    return true;
7b21e5c
+                    pos += total;
7b21e5c
+                if (pos + (int) page_start < _candidates.size) {
7b21e5c
+                    var new_pos = get_page_start_cursor_pos (pos + page_start);
7b21e5c
+                    if (update_cursor_pos (new_pos))
7b21e5c
+                        return true;
7b21e5c
                 }
7b21e5c
             } else {
7b21e5c
-                var pos = _cursor_pos + page_size * step;
7b21e5c
-                if (0 <= pos && pos < _candidates.size) {
7b21e5c
-                    pos = get_page_start_cursor_pos (pos);
7b21e5c
-                    if (pos != _cursor_pos) {
7b21e5c
-                        _cursor_pos = (int) pos;
7b21e5c
-                        notify_property ("cursor-pos");
7b21e5c
+                var pos = start + (int) page_size * step;
7b21e5c
+                if (0 <= pos && pos < total) {
7b21e5c
+                    var new_pos = get_page_start_cursor_pos (pos + page_start);
7b21e5c
+                    if (update_cursor_pos (new_pos))
7b21e5c
                         return true;
7b21e5c
-                    }
7b21e5c
                 }
7b21e5c
             }
7b21e5c
             return false;
7b21e5c
diff --git a/libkkc/context.vala b/libkkc/context.vala
7b21e5c
index e328c34..d94a248 100644
7b21e5c
--- a/libkkc/context.vala
7b21e5c
+++ b/libkkc/context.vala
7b21e5c
@@ -326,6 +326,35 @@ namespace Kkc {
7b21e5c
             }
7b21e5c
         }
7b21e5c
 
7b21e5c
+        /**
7b21e5c
+         * Process an explicit command in the context.
7b21e5c
+         *
7b21e5c
+         * This function is rarely used in programs but called from
7b21e5c
+         * D-Bus service.
7b21e5c
+         *
7b21e5c
+         * @param command a command
7b21e5c
+         *
7b21e5c
+         * @return `true` if the command is handled, `false` otherwise
7b21e5c
+         */
7b21e5c
+        internal bool process_command_event (string command) {
7b21e5c
+            var key = new KeyEvent (Keysyms.VoidSymbol, 0, 0);
7b21e5c
+            while (true) {
7b21e5c
+                var handler_type = state.handler_type;
7b21e5c
+                var handler = handlers.get (handler_type);
7b21e5c
+                state.this_command_key = key;
7b21e5c
+                if (handler.dispatch_command (command, state, key)) {
7b21e5c
+                    notify_property ("input");
7b21e5c
+                    return true;
7b21e5c
+                }
7b21e5c
+                // state.handler_type may change if handler cannot
7b21e5c
+                // handle the event.  In that case retry with the new
7b21e5c
+                // handler.  Otherwise exit the loop.
7b21e5c
+                if (handler_type == state.handler_type) {
7b21e5c
+                    return false;
7b21e5c
+                }
7b21e5c
+            }
7b21e5c
+        }
7b21e5c
+
7b21e5c
         /**
7b21e5c
          * Reset the context.
7b21e5c
          */
7b21e5c
diff --git a/libkkc/convert-segment-state-handler.vala b/libkkc/convert-segment-state-handler.vala
7b21e5c
index 3a4e05d..10a48b2 100644
7b21e5c
--- a/libkkc/convert-segment-state-handler.vala
7b21e5c
+++ b/libkkc/convert-segment-state-handler.vala
7b21e5c
@@ -36,6 +36,8 @@ namespace Kkc {
7b21e5c
                                        do_select_unhandled);
7b21e5c
             register_command_callback ("last-segment",
7b21e5c
                                        do_select_unhandled);
7b21e5c
+            register_command_callback ("commit",
7b21e5c
+                                       do_select_unhandled);
7b21e5c
             register_command_callback ("delete",
7b21e5c
                                        do_clear_unhandled);
7b21e5c
             register_command_callback ("original-candidate",
7b21e5c
@@ -49,21 +51,19 @@ namespace Kkc {
7b21e5c
                         "convert-" + enum_value.value_nick,
7b21e5c
                         do_clear_unhandled);
7b21e5c
             }
7b21e5c
-
7b21e5c
-            register_command_callback (null, do_select_unhandled);
7b21e5c
         }
7b21e5c
 
7b21e5c
-        bool do_next_candidate (string? command, State state, KeyEvent key) {
7b21e5c
+        bool do_next_candidate (string command, State state, KeyEvent key) {
7b21e5c
             state.candidates.cursor_down ();
7b21e5c
             return true;
7b21e5c
         }
7b21e5c
 
7b21e5c
-        bool do_previous_candidate (string? command, State state, KeyEvent key) {
7b21e5c
+        bool do_previous_candidate (string command, State state, KeyEvent key) {
7b21e5c
             state.candidates.cursor_up ();
7b21e5c
             return true;
7b21e5c
         }
7b21e5c
 
7b21e5c
-        bool do_purge_candidate (string? command, State state, KeyEvent key) {
7b21e5c
+        bool do_purge_candidate (string command, State state, KeyEvent key) {
7b21e5c
             if (state.candidates.cursor_pos >= 0) {
7b21e5c
                 var candidate = state.candidates.get ();
7b21e5c
                 state.purge_candidate (candidate);
7b21e5c
@@ -72,21 +72,29 @@ namespace Kkc {
7b21e5c
             return true;
7b21e5c
         }
7b21e5c
 
7b21e5c
-        bool do_select_unhandled (string? command, State state, KeyEvent key) {
7b21e5c
+        bool do_select_unhandled (string command, State state, KeyEvent key) {
7b21e5c
             if (state.candidates.cursor_pos >= 0)
7b21e5c
                 state.candidates.select ();
7b21e5c
             state.handler_type = typeof (ConvertSentenceStateHandler);
7b21e5c
             return false;
7b21e5c
         }
7b21e5c
 
7b21e5c
-        bool do_clear_unhandled (string? command, State state, KeyEvent key) {
7b21e5c
+        bool do_clear_unhandled (string command, State state, KeyEvent key) {
7b21e5c
             state.candidates.clear ();
7b21e5c
             state.handler_type = typeof (ConvertSentenceStateHandler);
7b21e5c
             return false;
7b21e5c
         }
7b21e5c
-                    
7b21e5c
+
7b21e5c
+        public override bool default_command_callback (string? command,
7b21e5c
+                                                       State state,
7b21e5c
+                                                       KeyEvent key)
7b21e5c
+        {
7b21e5c
+            return do_select_unhandled (command ?? "", state, key);
7b21e5c
+        }
7b21e5c
+
7b21e5c
         public override bool process_key_event (State state, KeyEvent key) {
7b21e5c
-            return dispatch_command (state, key);
7b21e5c
+            var command = state.lookup_key (key);
7b21e5c
+            return dispatch_command (command, state, key);
7b21e5c
         }
7b21e5c
     }
7b21e5c
 }
7b21e5c
diff --git a/libkkc/convert-sentence-state-handler.vala b/libkkc/convert-sentence-state-handler.vala
7b21e5c
index 476c8ae..ae97e68 100644
7b21e5c
--- a/libkkc/convert-sentence-state-handler.vala
7b21e5c
+++ b/libkkc/convert-sentence-state-handler.vala
7b21e5c
@@ -25,7 +25,7 @@ namespace Kkc {
7b21e5c
                 this.mode = mode;
7b21e5c
             }
7b21e5c
 
7b21e5c
-            public bool call (string? command, State state, KeyEvent key) {
7b21e5c
+            public bool call (string command, State state, KeyEvent key) {
7b21e5c
                 state.convert_segment_by_kana_mode (mode);
7b21e5c
                 return true;
7b21e5c
             }
7b21e5c
@@ -57,6 +57,7 @@ namespace Kkc {
7b21e5c
 
7b21e5c
             register_command_callback ("abort", do_clear_unhandled);
7b21e5c
             register_command_callback ("delete", do_clear_unhandled);
7b21e5c
+            register_command_callback ("commit", do_commit);
7b21e5c
 
7b21e5c
             var enum_class = (EnumClass) typeof (KanaMode).class_ref ();
7b21e5c
             for (int i = enum_class.minimum; i <= enum_class.maximum; i++) {
7b21e5c
@@ -67,62 +68,70 @@ namespace Kkc {
7b21e5c
                         new ConvertCommandHandler (
7b21e5c
                             (KanaMode) enum_value.value));
7b21e5c
             }
7b21e5c
-
7b21e5c
-            register_command_callback (null, do_);
7b21e5c
         }
7b21e5c
 
7b21e5c
-        bool do_original_candidate (string? command, State state, KeyEvent key) {
7b21e5c
+        bool do_original_candidate (string command, State state, KeyEvent key) {
7b21e5c
             var segment = state.segments[state.segments.cursor_pos];
7b21e5c
             segment.output = segment.input;
7b21e5c
             return true;
7b21e5c
         }
7b21e5c
 
7b21e5c
-        bool do_expand_segment (string? command, State state, KeyEvent key) {
7b21e5c
+        bool do_expand_segment (string command, State state, KeyEvent key) {
7b21e5c
             if (state.segments.cursor_pos < state.segments.size - 1)
7b21e5c
                 state.resize_segment (1);
7b21e5c
             return true;
7b21e5c
         }
7b21e5c
 
7b21e5c
-        bool do_shrink_segment (string? command, State state, KeyEvent key) {
7b21e5c
+        bool do_shrink_segment (string command, State state, KeyEvent key) {
7b21e5c
             if (state.segments[state.segments.cursor_pos].input.char_count () > 1)
7b21e5c
                 state.resize_segment (-1);
7b21e5c
             return true;
7b21e5c
         }
7b21e5c
 
7b21e5c
-        bool do_next_segment (string? command, State state, KeyEvent key) {
7b21e5c
+        bool do_next_segment (string command, State state, KeyEvent key) {
7b21e5c
             state.segments.next_segment ();
7b21e5c
             return true;
7b21e5c
         }
7b21e5c
 
7b21e5c
-        bool do_previous_segment (string? command, State state, KeyEvent key) {
7b21e5c
+        bool do_previous_segment (string command, State state, KeyEvent key) {
7b21e5c
             state.segments.previous_segment ();
7b21e5c
             return true;
7b21e5c
         }
7b21e5c
 
7b21e5c
-        bool do_first_segment (string? command, State state, KeyEvent key) {
7b21e5c
+        bool do_first_segment (string command, State state, KeyEvent key) {
7b21e5c
             state.segments.first_segment ();
7b21e5c
             return true;
7b21e5c
         }
7b21e5c
 
7b21e5c
-        bool do_last_segment (string? command, State state, KeyEvent key) {
7b21e5c
+        bool do_last_segment (string command, State state, KeyEvent key) {
7b21e5c
             state.segments.last_segment ();
7b21e5c
             return true;
7b21e5c
         }
7b21e5c
 
7b21e5c
-        bool do_start_segment_conversion (string? command, State state, KeyEvent key) {
7b21e5c
+        bool do_start_segment_conversion (string command, State state, KeyEvent key) {
7b21e5c
             state.lookup (state.segments[state.segments.cursor_pos]);
7b21e5c
             state.candidates.first ();
7b21e5c
             state.handler_type = typeof (ConvertSegmentStateHandler);
7b21e5c
             return false;
7b21e5c
         }
7b21e5c
 
7b21e5c
-        bool do_clear_unhandled (string? command, State state, KeyEvent key) {
7b21e5c
+        bool do_clear_unhandled (string command, State state, KeyEvent key) {
7b21e5c
             state.segments.clear ();
7b21e5c
             state.handler_type = typeof (InitialStateHandler);
7b21e5c
             return true;
7b21e5c
         }
7b21e5c
 
7b21e5c
-        bool do_ (string? command, State state, KeyEvent key) {
7b21e5c
+        bool do_commit (string command, State state, KeyEvent key) {
7b21e5c
+            state.output.append (state.segments.get_output ());
7b21e5c
+            state.select_sentence ();
7b21e5c
+            state.reset ();
7b21e5c
+            return true;
7b21e5c
+        }
7b21e5c
+
7b21e5c
+        public override bool default_command_callback (string? command,
7b21e5c
+                                                       State state,
7b21e5c
+                                                       KeyEvent key)
7b21e5c
+        {
7b21e5c
             state.output.append (state.segments.get_output ());
7b21e5c
             state.select_sentence ();
7b21e5c
             state.reset ();
7b21e5c
@@ -142,7 +151,8 @@ namespace Kkc {
7b21e5c
         }
7b21e5c
 
7b21e5c
         public override bool process_key_event (State state, KeyEvent key) {
7b21e5c
-            return dispatch_command (state, key);
7b21e5c
+            var command = state.lookup_key (key);
7b21e5c
+            return dispatch_command (command, state, key);
7b21e5c
         }
7b21e5c
     }
7b21e5c
 }
7b21e5c
diff --git a/libkkc/encoding.vala b/libkkc/encoding.vala
7b21e5c
index fe9ced1..af64ef7 100644
7b21e5c
--- a/libkkc/encoding.vala
7b21e5c
+++ b/libkkc/encoding.vala
7b21e5c
@@ -19,15 +19,15 @@ namespace Kkc {
7b21e5c
     // XXX: we use Vala string to represent byte array, assuming that
7b21e5c
     // it does not contain null element
7b21e5c
     class EncodingConverter : Object, Initable {
7b21e5c
-        static const int BUFSIZ = 4096;
7b21e5c
-        static const string INTERNAL_ENCODING = "UTF-8";
7b21e5c
+        const int BUFSIZ = 4096;
7b21e5c
+        const string INTERNAL_ENCODING = "UTF-8";
7b21e5c
 
7b21e5c
         struct EncodingCodingSystemEntry {
7b21e5c
             string key;
7b21e5c
             string value;
7b21e5c
         }
7b21e5c
 
7b21e5c
-        static const EncodingCodingSystemEntry ENCODING_TO_CODING_SYSTEM_RULE[] = {
7b21e5c
+        const EncodingCodingSystemEntry ENCODING_TO_CODING_SYSTEM_RULE[] = {
7b21e5c
             { "UTF-8", "utf-8" },
7b21e5c
             { "EUC-JP", "euc-jp" },
7b21e5c
             { "Shift_JIS", "shift_jis" },
7b21e5c
diff --git a/libkkc/initial-state-handler.vala b/libkkc/initial-state-handler.vala
7b21e5c
index 3679b60..927560f 100644
7b21e5c
--- a/libkkc/initial-state-handler.vala
7b21e5c
+++ b/libkkc/initial-state-handler.vala
7b21e5c
@@ -25,7 +25,7 @@ namespace Kkc {
7b21e5c
                 this.mode = mode;
7b21e5c
             }
7b21e5c
 
7b21e5c
-            public bool call (string? command, State state, KeyEvent key) {
7b21e5c
+            public bool call (string command, State state, KeyEvent key) {
7b21e5c
                 state.finish_input_editing ();
7b21e5c
                 if (state.input_characters.size > 0) {
7b21e5c
                     state.selection.erase ();
7b21e5c
@@ -61,21 +61,20 @@ namespace Kkc {
7b21e5c
             register_command_callback ("last-segment", do_last_character);
7b21e5c
             register_command_callback ("quote", do_quote);
7b21e5c
             register_command_callback ("register", do_register);
7b21e5c
-
7b21e5c
-            register_command_callback (null, do_);
7b21e5c
+            register_command_callback ("commit", do_commit);
7b21e5c
         }
7b21e5c
 
7b21e5c
-        bool do_quote (string? command, State state, KeyEvent key) {
7b21e5c
+        bool do_quote (string command, State state, KeyEvent key) {
7b21e5c
             state.quoted = true;
7b21e5c
             return true;
7b21e5c
         }
7b21e5c
 
7b21e5c
-        bool do_register (string? command, State state, KeyEvent key) {
7b21e5c
+        bool do_register (string command, State state, KeyEvent key) {
7b21e5c
             state.request_selection_text ();
7b21e5c
             return true;
7b21e5c
         }
7b21e5c
 
7b21e5c
-        bool do_abort (string? command, State state, KeyEvent key) {
7b21e5c
+        bool do_abort (string command, State state, KeyEvent key) {
7b21e5c
             if (state.overriding_input != null) {
7b21e5c
                 state.overriding_input = null;
7b21e5c
                 return true;
7b21e5c
@@ -90,7 +89,7 @@ namespace Kkc {
7b21e5c
             return false;
7b21e5c
         }
7b21e5c
 
7b21e5c
-        bool do_delete (string? command, State state, KeyEvent key) {
7b21e5c
+        bool do_delete (string command, State state, KeyEvent key) {
7b21e5c
             if (state.overriding_input != null) {
7b21e5c
                 state.overriding_input = null;
7b21e5c
                 return true;
7b21e5c
@@ -115,7 +114,7 @@ namespace Kkc {
7b21e5c
             return false;
7b21e5c
         }