Merge "Allow logging while replaying"
diff --git a/java/res/values-af/strings.xml b/java/res/values-af/strings.xml
index f0b47be..90f321b 100644
--- a/java/res/values-af/strings.xml
+++ b/java/res/values-af/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Android-sleutelbord (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Android-sleutelbord-instellings (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Android-speltoetser (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Android-speltoetserinstellings (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Invoeropsies"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Navorsing-loglêerbevele"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Soek kontakname op"</string>
diff --git a/java/res/values-am/strings.xml b/java/res/values-am/strings.xml
index f6ccec7..7a6dd1d 100644
--- a/java/res/values-am/strings.xml
+++ b/java/res/values-am/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"የAndroid ቁልፍ ሰሌዳ (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"የAndroid ቁልፍ ሰሌዳ ቅንብሮች (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Android ፊደል አራሚ (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"የAndroid ፊደል አራሚ ቅንብሮች (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"ግቤት አማራጮች"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"የጥናት የምዝግብ ማስታወሻ ትዕዛዞች"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"የእውቅያ ስሞችን ተመልከት"</string>
diff --git a/java/res/values-ar/strings.xml b/java/res/values-ar/strings.xml
index f9b01d3..e9f9237 100644
--- a/java/res/values-ar/strings.xml
+++ b/java/res/values-ar/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"لوحة مفاتيح Android ‏(AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"إعدادات لوحة مفاتيح Android‏ (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"المدقق الإملائي في Android‏ (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"إعدادات المدقق الإملائي في Android‏ (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"خيارات الإرسال"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"أوامر سجلات البحث"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"بحث في أسماء جهات الاتصال"</string>
diff --git a/java/res/values-be/strings.xml b/java/res/values-be/strings.xml
index 6c95cce..c32a56d 100644
--- a/java/res/values-be/strings.xml
+++ b/java/res/values-be/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Клавіятура Android (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Налады клавіятуры Android (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Iнструмент праверкi правапiсу для Android (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Налады інструмента праверкі правапісу для Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Параметры ўводу"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Каманды гiсторыя даследаванняў"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Шукаць імёны кантактаў"</string>
diff --git a/java/res/values-bg/strings.xml b/java/res/values-bg/strings.xml
index 58a6a70..5feac03 100644
--- a/java/res/values-bg/strings.xml
+++ b/java/res/values-bg/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Клавиатура на Android (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Настройки на клавиатурата на Android (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Програма за правописна проверка за Android (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Настройки на програмата за правописна проверка за Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Опции за въвеждане"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Команди за рег. файл за проучвания"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Търсене на имена"</string>
diff --git a/java/res/values-ca/strings.xml b/java/res/values-ca/strings.xml
index 5539fee..3ebf639 100644
--- a/java/res/values-ca/strings.xml
+++ b/java/res/values-ca/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Teclat d\'Android (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Configuració del teclat d\'Android (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Corrector ortogràfic d\'Android (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Configuració del corrector ortogràfic d\'Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Opcions d\'entrada"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Recerca d\'ordres de reg."</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Cerca noms de contactes"</string>
diff --git a/java/res/values-cs/strings.xml b/java/res/values-cs/strings.xml
index c7261ba..050ae64 100644
--- a/java/res/values-cs/strings.xml
+++ b/java/res/values-cs/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Klávesnice Android (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Nastavení klávesnice Android (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Kontrola pravopisu Android (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Nastavení kontroly pravopisu Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Možnosti zadávání textu a dat"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Příkazy vývoj. protokolu"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Vyhledat kontakty"</string>
diff --git a/java/res/values-da/strings.xml b/java/res/values-da/strings.xml
index 1ed9983..034c98f 100644
--- a/java/res/values-da/strings.xml
+++ b/java/res/values-da/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Android-tastatur (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Indstillinger for Android-tastatur (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Android-stavekontrol (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Indstillinger for Android-stavekontrol (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Indstillinger for input"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Forskningslogkommandoer"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Slå kontaktnavne op"</string>
diff --git a/java/res/values-de/strings.xml b/java/res/values-de/strings.xml
index 9816621..39444ea 100644
--- a/java/res/values-de/strings.xml
+++ b/java/res/values-de/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Android-Tastatur (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Android-Tastatureinstellungen (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Android-Rechtschreibprüfung (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Einstellungen für die Android-Rechtschreibprüfung (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Eingabeoptionen"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Forschungsprotokollbefehle"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Kontaktnamen prüfen"</string>
diff --git a/java/res/values-el/strings.xml b/java/res/values-el/strings.xml
index f3b84c6..93de977 100644
--- a/java/res/values-el/strings.xml
+++ b/java/res/values-el/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Πληκτρολόγιο Android (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Ρυθμίσεις πληκτρολογίου Android (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Ορθογραφικός έλεγχος Android (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Ρυθμίσεις ορθογραφικού ελέγχου Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Επιλογές εισόδου"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Έρευνα εντολών καταγραφής"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Αναζήτηση ονομάτων επαφών"</string>
diff --git a/java/res/values-es-rUS/strings.xml b/java/res/values-es-rUS/strings.xml
index c7f20aa..a544f77 100644
--- a/java/res/values-es-rUS/strings.xml
+++ b/java/res/values-es-rUS/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Teclado de Android (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Configuración del teclado de Android (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Corrector ortográfico de Android (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Configuración del corrector ortográfico de Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Opciones de entrada"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Comandos registro invest."</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Buscar nombres contactos"</string>
diff --git a/java/res/values-es/strings.xml b/java/res/values-es/strings.xml
index 8168258..afc04f5 100644
--- a/java/res/values-es/strings.xml
+++ b/java/res/values-es/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Teclado Android (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Ajustes del teclado de Android (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Corrector de Android (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Ajustes del corrector de Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Opciones entrada texto"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Comandos registro investigación"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Nombres de contactos"</string>
diff --git a/java/res/values-et/strings.xml b/java/res/values-et/strings.xml
index 8968bf4..acfa957 100644
--- a/java/res/values-et/strings.xml
+++ b/java/res/values-et/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Androidi klaviatuur (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Androidi klaviatuuri seaded (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Androidi õigekirjakontroll (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Androidi õigekirjakontrolli seaded (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Sisestusvalikud"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Uuringulogi käsud"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Kontakti nimede kontroll."</string>
diff --git a/java/res/values-fa/strings.xml b/java/res/values-fa/strings.xml
index 723ecd6..52d5b83 100644
--- a/java/res/values-fa/strings.xml
+++ b/java/res/values-fa/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"صفحه کلید Android ‏(AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"تنظیمات صفحه کلید Android ‏(AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"غلط‌گیر Android ‏(AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"تنظیمات غلط‌گیر Android ‏(AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"گزینه‌های ورودی"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"فرمان‌های گزارش‌گیری پژوهش"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"جستجوی نام مخاطبین"</string>
diff --git a/java/res/values-fi/strings.xml b/java/res/values-fi/strings.xml
index 2e6e4d1..8f36524 100644
--- a/java/res/values-fi/strings.xml
+++ b/java/res/values-fi/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Android-näppäimistö (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Android-näppäimistön asetukset (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Android-oikoluku (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Android-oikoluvun asetukset (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Syöttövalinnat"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Tutkimuslokin komennot"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Hae kontaktien nimiä"</string>
diff --git a/java/res/values-fr/strings.xml b/java/res/values-fr/strings.xml
index 2b8e3cb..4daa0f6 100644
--- a/java/res/values-fr/strings.xml
+++ b/java/res/values-fr/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Clavier Android (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Paramètres du clavier Android (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Correcteur orthographique Android (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Paramètres du correcteur orthographique Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Options de saisie"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Commandes journaux rech."</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Rechercher noms contacts"</string>
diff --git a/java/res/values-hi/strings.xml b/java/res/values-hi/strings.xml
index 28c4879..c4238e8 100644
--- a/java/res/values-hi/strings.xml
+++ b/java/res/values-hi/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Android कीबोर्ड (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Android कीबोर्ड सेटिंग (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Android वर्तनी परीक्षक (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Android वर्तनी परीक्षक सेटिंग (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"इनपुट विकल्‍प"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"लॉग आदेशों का शोध करें"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"संपर्क नामों को खोजें"</string>
diff --git a/java/res/values-hr/strings.xml b/java/res/values-hr/strings.xml
index 4732876..a5515cf 100644
--- a/java/res/values-hr/strings.xml
+++ b/java/res/values-hr/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Androidova tipkovnica (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Postavke Androidove tipkovnice (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Androidova provjera pravopisa (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Postavke Androidove provjere pravopisa (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Opcije ulaza"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Istraživanje naredbi dnevnika"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Potražite imena kontakata"</string>
diff --git a/java/res/values-hu/strings.xml b/java/res/values-hu/strings.xml
index 68ca03e..d6075f7 100644
--- a/java/res/values-hu/strings.xml
+++ b/java/res/values-hu/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Android-billentyűzet (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Android-billentyűzet beállításai (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Androidos helyesírás-ellenőrző (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Androidos helyesírás-ellenőrző beállításai (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Beviteli beállítások"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Naplózási parancsok"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Névjegyek keresése"</string>
diff --git a/java/res/values-in/strings.xml b/java/res/values-in/strings.xml
index e361e69..953e091 100644
--- a/java/res/values-in/strings.xml
+++ b/java/res/values-in/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Keyboard Android (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Setelan Keyboard Android (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Pemeriksa Ejaan Android (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Setelan Pemeriksa Ejaan Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Opsi masukan"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Riset Perintah Log"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Cari nama kontak"</string>
diff --git a/java/res/values-it/strings.xml b/java/res/values-it/strings.xml
index 52c2bfa..7ce349a 100644
--- a/java/res/values-it/strings.xml
+++ b/java/res/values-it/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Tastiera Android (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Impostazioni tastiera Android (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Controllo ortografico Android (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Impostazioni controllo ortografico Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Opzioni inserimento"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Ricerca comandi di log"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Cerca in nomi contatti"</string>
diff --git a/java/res/values-iw/strings.xml b/java/res/values-iw/strings.xml
index a235937..e3d3064 100644
--- a/java/res/values-iw/strings.xml
+++ b/java/res/values-iw/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"מקלדת Android‏ (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"הגדרות מקלדת Android‏ (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"בודק האיות של Android‏ (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"הגדרות בודק האיות של Android‏ (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"אפשרויות קלט"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"פקודות יומן מחקר"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"חפש שמות של אנשי קשר"</string>
diff --git a/java/res/values-ja/strings.xml b/java/res/values-ja/strings.xml
index a3da752..6d6259d 100644
--- a/java/res/values-ja/strings.xml
+++ b/java/res/values-ja/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Androidキーボード(AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Androidキーボードの設定(AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Androidスペルチェッカー(AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Androidスペルチェッカーの設定(AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"入力オプション"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"ログコマンドの検索"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"連絡先名の検索"</string>
diff --git a/java/res/values-ko/strings.xml b/java/res/values-ko/strings.xml
index 1f4596b..c58613d 100644
--- a/java/res/values-ko/strings.xml
+++ b/java/res/values-ko/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Android 키보드(AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Android 키보드 설정(AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Android 맞춤법 검사기(AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Android 맞춤법 검사기 설정(AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"입력 옵션"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"로그 명령 탐색"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"연락처 이름 조회"</string>
diff --git a/java/res/values-lt/strings.xml b/java/res/values-lt/strings.xml
index 307b5f2..57cf7bd 100644
--- a/java/res/values-lt/strings.xml
+++ b/java/res/values-lt/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"„Android“ klaviatūra (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"„Android“ klaviatūros nustatymai (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"„Android“ rašybos tikrinimo programa (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"„Android“ rašybos tikrinimo programos nustatymai (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Įvesties parinktys"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Tyrinėti žurnalo komandas"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Kontaktų vardų paieška"</string>
diff --git a/java/res/values-lv/strings.xml b/java/res/values-lv/strings.xml
index 0241583..499a451 100644
--- a/java/res/values-lv/strings.xml
+++ b/java/res/values-lv/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Android tastatūra (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Android tastatūras iestatījumi (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Android pareizrakstības pārbaudītājs (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Android pareizrakstības pārbaudītāja iestatījumi (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Ievades opcijas"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Izpētes žurnāla komandas"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Meklēt kontaktp. vārdus"</string>
diff --git a/java/res/values-ms/strings.xml b/java/res/values-ms/strings.xml
index afb036a..faf9164 100644
--- a/java/res/values-ms/strings.xml
+++ b/java/res/values-ms/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Papan kekunci Android (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Tetapan Papan Kekunci Android (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Penyemak Ejaan Android (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Tetapan Penyemak Ejaan Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Pilihan input"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Arahan Log Penyelidikan"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Cari nama kenalan"</string>
diff --git a/java/res/values-nb/strings.xml b/java/res/values-nb/strings.xml
index 5db141c..cd2606d 100644
--- a/java/res/values-nb/strings.xml
+++ b/java/res/values-nb/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Android-tastatur (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Instillinger for Android-tastatur (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Android-stavekontroll (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Innstillinger for Android-stavekontroll (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Inndataalternativer"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Kommandoer for undersøkelseslogging"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Slå opp kontaktnavn"</string>
diff --git a/java/res/values-nl/strings.xml b/java/res/values-nl/strings.xml
index 176f828..fb2a82e 100644
--- a/java/res/values-nl/strings.xml
+++ b/java/res/values-nl/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Android-toetsenbord (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Instellingen voor het Android-toetsenbord (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Spellingcontrole van Android (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Instellingen voor spellingcontrole van Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Invoeropties"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Opdrachten in onderzoekslogbestand"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Contactnamen opzoeken"</string>
diff --git a/java/res/values-pl/strings.xml b/java/res/values-pl/strings.xml
index 34a03f6..f703a74 100644
--- a/java/res/values-pl/strings.xml
+++ b/java/res/values-pl/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Klawiatura Android (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Ustawienia klawiatury Android (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Sprawdzanie pisowni na Androidzie (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Ustawienia sprawdzania pisowni na Androidzie (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Opcje wprowadzania"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Polecenia dziennika badań"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Przeszukaj kontakty"</string>
diff --git a/java/res/values-pt-rPT/strings.xml b/java/res/values-pt-rPT/strings.xml
index 81e1485..9c52fd7 100644
--- a/java/res/values-pt-rPT/strings.xml
+++ b/java/res/values-pt-rPT/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Teclado Android (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Definições do Teclado Android (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Verificador Ortográfico Android (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Definições do Verificador Ortográfico Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Opções de introdução"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Comandos de Reg. Invest."</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Procurar nomes de contac."</string>
diff --git a/java/res/values-pt/strings.xml b/java/res/values-pt/strings.xml
index 880aca6..dd1fcfd 100644
--- a/java/res/values-pt/strings.xml
+++ b/java/res/values-pt/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Teclado Android (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Configurações de teclado Android (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Corretor ortográfico do Android (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Configurações de corretor ortográfico do Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Opções de entrada"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Pesq. comandos de reg."</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Buscar nomes de contatos"</string>
diff --git a/java/res/values-ro/strings.xml b/java/res/values-ro/strings.xml
index a62bfd2..b0732c8 100644
--- a/java/res/values-ro/strings.xml
+++ b/java/res/values-ro/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Tastatură Android (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Setări tastatură Android (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Verificator ortografic Android (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Setări verificator ortografic Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Opţiuni de introducere text"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Comenzi jurnal cercetare"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Verificare nume în agendă"</string>
diff --git a/java/res/values-ru/strings.xml b/java/res/values-ru/strings.xml
index dab7387..b3d7706 100644
--- a/java/res/values-ru/strings.xml
+++ b/java/res/values-ru/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Клавиатура Android (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Настройки клавиатуры Android (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Проверка правописания Android (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Настройки проверки правописания Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Настройки"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Все команды"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Поиск контактов"</string>
diff --git a/java/res/values-sk/strings.xml b/java/res/values-sk/strings.xml
index d6d8068..77d9409 100644
--- a/java/res/values-sk/strings.xml
+++ b/java/res/values-sk/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Klávesnica Android (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Nastavenia klávesnice Android (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Kontrola pravopisu (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Nastavenia kontroly pravopisu Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Možnosti zadávania textu a údajov"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Príkazy denníka výskumu"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Vyhľadať kontakty"</string>
diff --git a/java/res/values-sl/strings.xml b/java/res/values-sl/strings.xml
index 37540c3..87961ef 100644
--- a/java/res/values-sl/strings.xml
+++ b/java/res/values-sl/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Tipkovnica za Android (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Nastavitve tipkovnice za Android (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Črkovalnik za Android (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Nastavitve črkovalnika za Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Možnosti vnosa"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Ukazi za dnevnik raziskav"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Iskanje imen stikov"</string>
diff --git a/java/res/values-sr/strings.xml b/java/res/values-sr/strings.xml
index 8b8720f..d8b3dbf 100644
--- a/java/res/values-sr/strings.xml
+++ b/java/res/values-sr/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Android тастатура (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Подешавања Android тастатуре (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Android провера правописа (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Подешавања Android провере правописа (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Опције уноса"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Команде евиденције истраживања"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Потражи имена контаката"</string>
diff --git a/java/res/values-sv/strings.xml b/java/res/values-sv/strings.xml
index 6ceed10..d178933 100644
--- a/java/res/values-sv/strings.xml
+++ b/java/res/values-sv/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Androids tangentbord (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Inställningar för Androids tangentbord (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Stavningskontroll i Android (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Inställningar för Androids stavningskontroll (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Inmatningsalternativ"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Loggkommandon"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Sök namn på kontakter"</string>
diff --git a/java/res/values-sw/strings.xml b/java/res/values-sw/strings.xml
index 5b56601..2909526 100644
--- a/java/res/values-sw/strings.xml
+++ b/java/res/values-sw/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Kibodi ya Android (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Mipangilio ya Kibodi ya Android (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Kikagua-tahajia cha Android (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Mipangilio ya Kikagua-tahajia cha Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Chaguo za uingizaji"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Amri za Kumbukumbu za Utafiti"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Angalia majina ya unaowasiliana nao"</string>
diff --git a/java/res/values-th/strings.xml b/java/res/values-th/strings.xml
index 2858076..142903c 100644
--- a/java/res/values-th/strings.xml
+++ b/java/res/values-th/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"แป้นพิมพ์แอนดรอยด์ (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"การตั้งค่าแป้นพิมพ์แอนดรอยด์ (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"เครื่องตรวจตัวสะกดแอนดรอยด์ (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"การตั้งค่าเครื่องตรวจตัวสะกดแอนดรอยด์ (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"ตัวเลือกการป้อนข้อมูล"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"คำสั่งบันทึกการวิจัย"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"ค้นหารายชื่อติดต่อ"</string>
diff --git a/java/res/values-tl/strings.xml b/java/res/values-tl/strings.xml
index 04127ab..fea07b4 100644
--- a/java/res/values-tl/strings.xml
+++ b/java/res/values-tl/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Android Keyboard (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Mga Setting ng Android Keyboard (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Spell Checker ng Android (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Mga Setting ng Spell Checker ng Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Mga pagpipilian sa input"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Cmmnd sa Log ng Pnnliksik"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Maghanap pangalan contact"</string>
diff --git a/java/res/values-tr/strings.xml b/java/res/values-tr/strings.xml
index f238f20..b976932 100644
--- a/java/res/values-tr/strings.xml
+++ b/java/res/values-tr/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Android klavye (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Android Klavye Ayarları (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Android Yazım Denetleyici (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Android Yazım Denetleyici Ayarları (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Giriş seçenekleri"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Araştırma Günlüğü Komutları"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Kişi adlarını denetle"</string>
diff --git a/java/res/values-uk/strings.xml b/java/res/values-uk/strings.xml
index 42a0b9f..f81ace6 100644
--- a/java/res/values-uk/strings.xml
+++ b/java/res/values-uk/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Клавіатура Android (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Налаштування клавіатури Android (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Перевірка орфографії Android (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Налаштування перевірки орфографії Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Парам. введення"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Команди журналу дослідж."</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Шукати імена контактів"</string>
diff --git a/java/res/values-vi/strings.xml b/java/res/values-vi/strings.xml
index 245e685..fac412a 100644
--- a/java/res/values-vi/strings.xml
+++ b/java/res/values-vi/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Bàn phím Android (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Cài đặt bàn phím Android (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Trình kiểm tra chính tả Android (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Cài đặt trình kiểm tra chính tả Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Tùy chọn nhập"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Lệnh ghi nhật ký cho nghiên cứu"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Tra cứu tên liên hệ"</string>
diff --git a/java/res/values-zh-rCN/strings.xml b/java/res/values-zh-rCN/strings.xml
index cba3322..693a674 100644
--- a/java/res/values-zh-rCN/strings.xml
+++ b/java/res/values-zh-rCN/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Android 键盘 (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Android 键盘设置 (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Android 拼写检查工具 (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Android 拼写检查工具设置 (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"输入选项"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"研究记录命令"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"查找联系人姓名"</string>
diff --git a/java/res/values-zh-rTW/strings.xml b/java/res/values-zh-rTW/strings.xml
index 9a48969..1fb204f 100644
--- a/java/res/values-zh-rTW/strings.xml
+++ b/java/res/values-zh-rTW/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Android 鍵盤 (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Android 鍵盤設定 (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Android 拼字檢查 (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Android 拼字檢查設定 (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"輸入選項"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"研究紀錄指令"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"查詢聯絡人姓名"</string>
diff --git a/java/res/values-zu/strings.xml b/java/res/values-zu/strings.xml
index e360750..bcec13d 100644
--- a/java/res/values-zu/strings.xml
+++ b/java/res/values-zu/strings.xml
@@ -20,14 +20,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for aosp_android_keyboard_ime_name (8250992613616792321) -->
-    <skip />
-    <!-- no translation found for aosp_android_keyboard_ime_settings (423615877174850267) -->
-    <skip />
-    <!-- no translation found for aosp_spell_checker_service_name (511950477199948048) -->
-    <skip />
-    <!-- no translation found for aosp_android_spell_checker_service_settings (2970535894327288421) -->
-    <skip />
+    <string name="aosp_android_keyboard_ime_name" msgid="8250992613616792321">"Ikhibhodi ye-Android (AOSP)"</string>
+    <string name="aosp_android_keyboard_ime_settings" msgid="423615877174850267">"Izilungiselelo zekhibhodi ye-Android (AOSP)"</string>
+    <string name="aosp_spell_checker_service_name" msgid="511950477199948048">"Isihloli sokupela se-Android (AOSP)"</string>
+    <string name="aosp_android_spell_checker_service_settings" msgid="2970535894327288421">"Izilungiselelo zesihloli sokupela se-Android (AOSP)"</string>
     <string name="english_ime_input_options" msgid="3909945612939668554">"Okukhethwa kukho kokungenayo"</string>
     <string name="english_ime_research_log" msgid="8492602295696577851">"Imiyalo yefayela lokungena lokucwaninga"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Bheka amagama woxhumana nabo"</string>
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index ebcd3d9..201fc70 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -350,10 +350,10 @@
     <!-- Inform the user that a particular language has an available dictionary -->
     <string name="has_dictionary">Dictionary available</string>
 
-    <!-- Preferences item for enabling to send user statistics to Google -->
+    <!-- Preferences item for enabling to send user statistics for development only diagnostics -->
     <string name="prefs_enable_log">Enable user feedback</string>
-    <!-- Description for enabling to send user statistics to Google -->
-    <string name="prefs_description_log">Help improve this input method editor by automatically sending usage statistics and crash reports to Google.</string>
+    <!-- Description for enabling to send user statistics for development only diagnostics -->
+    <string name="prefs_description_log">Help improve this input method editor by automatically sending usage statistics and crash reports</string>
 
     <!-- Title of the item to change the keyboard theme [CHAR LIMIT=20]-->
     <string name="keyboard_layout">Keyboard theme</string>
@@ -422,12 +422,12 @@
 
     <!-- Title of an option for usability study mode -->
     <string name="prefs_usability_study_mode">Usability study mode</string>
-    <!-- Title of the settings for key long press delay -->
-    <string name="prefs_key_longpress_timeout_settings">Key long press delay settings</string>
-    <!-- Title of the settings for keypress vibration duration -->
-    <string name="prefs_keypress_vibration_duration_settings">Keypress vibration duration settings</string>
-    <!-- Title of the settings for keypress sound volume -->
-    <string name="prefs_keypress_sound_volume_settings">Keypress sound volume settings</string>
+    <!-- Title of the settings for key long press delay [CHAR LIMIT=30] -->
+    <string name="prefs_key_longpress_timeout_settings">Key long press delay</string>
+    <!-- Title of the settings for keypress vibration duration [CHAR LIMIT=30] -->
+    <string name="prefs_keypress_vibration_duration_settings">Keypress vibration duration</string>
+    <!-- Title of the settings for keypress sound volume [CHAR LIMIT=30] -->
+    <string name="prefs_keypress_sound_volume_settings">Keypress sound volume</string>
     <!-- Title of the settings for reading an external dictionary file -->
     <string name="prefs_read_external_dictionary">Read external dictionary file</string>
     <!-- Message to show when there are no files to install as an external dictionary [CHAR LIMIT=100] -->
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java b/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java
index b047fe0..e3e6d39 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java
@@ -44,6 +44,7 @@
     // The wall time of the zero value in {@link #mEventTimes}
     private long mCurrentTimeBase;
     private int mTrailStartIndex;
+    private int mLastInterpolatedDrawIndex;
 
     static final class Params {
         public final int mTrailColor;
@@ -96,6 +97,17 @@
         }
         final int[] eventTimes = mEventTimes.getPrimitiveArray();
         final int strokeId = stroke.getGestureStrokeId();
+        // Because interpolation algorithm in {@link GestureStrokeWithPreviewPoints} can't determine
+        // the interpolated points in the last segment of gesture stroke, it may need recalculation
+        // of interpolation when new segments are added to the stroke.
+        // {@link #mLastInterpolatedDrawIndex} holds the start index of the last segment. It may
+        // be updated by the interpolation
+        // {@link GestureStrokeWithPreviewPoints#interpolatePreviewStroke}
+        // or by animation {@link #drawGestureTrail(Canvas,Paint,Rect,Params)} below.
+        final int lastInterpolatedIndex = (strokeId == mCurrentStrokeId)
+                ? mLastInterpolatedDrawIndex : trailSize;
+        mLastInterpolatedDrawIndex = stroke.interpolateStrokeAndReturnStartIndexOfLastSegment(
+                lastInterpolatedIndex, mEventTimes, mXCoordinates, mYCoordinates);
         if (strokeId != mCurrentStrokeId) {
             final int elapsedTime = (int)(downTime - mCurrentTimeBase);
             for (int i = mTrailStartIndex; i < trailSize; i++) {
@@ -216,6 +228,10 @@
                 System.arraycopy(eventTimes, startIndex, eventTimes, 0, newSize);
                 System.arraycopy(xCoords, startIndex, xCoords, 0, newSize);
                 System.arraycopy(yCoords, startIndex, yCoords, 0, newSize);
+                // The start index of the last segment of the stroke
+                // {@link mLastInterpolatedDrawIndex} should also be updated because all array
+                // elements have just been shifted for compaction.
+                mLastInterpolatedDrawIndex = Math.max(mLastInterpolatedDrawIndex - startIndex, 0);
             }
             mEventTimes.setLength(newSize);
             mXCoordinates.setLength(newSize);
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewPoints.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewPoints.java
index fc81410..3315954 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewPoints.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewPoints.java
@@ -21,19 +21,32 @@
 public final class GestureStrokeWithPreviewPoints extends GestureStroke {
     public static final int PREVIEW_CAPACITY = 256;
 
+    private static final boolean ENABLE_INTERPOLATION = true;
+
     private final ResizableIntArray mPreviewEventTimes = new ResizableIntArray(PREVIEW_CAPACITY);
     private final ResizableIntArray mPreviewXCoordinates = new ResizableIntArray(PREVIEW_CAPACITY);
     private final ResizableIntArray mPreviewYCoordinates = new ResizableIntArray(PREVIEW_CAPACITY);
 
     private int mStrokeId;
     private int mLastPreviewSize;
+    private final HermiteInterpolator mInterpolator = new HermiteInterpolator();
+    private int mLastInterpolatedPreviewIndex;
 
-    private int mMinPreviewSampleLengthSquare;
+    private int mMinPreviewSamplingDistanceSquared;
     private int mLastX;
     private int mLastY;
+    private double mMinPreviewSamplingDistance;
+    private double mDistanceFromLastSample;
 
-    // TODO: Move this to resource.
-    private static final float MIN_PREVIEW_SAMPLE_LENGTH_RATIO_TO_KEY_WIDTH = 0.1f;
+    // TODO: Move these constants to resource.
+    // The minimum linear distance between sample points for preview in keyWidth unit.
+    private static final float MIN_PREVIEW_SAMPLING_RATIO_TO_KEY_WIDTH = 0.1f;
+    // The minimum trail distance between sample points for preview in keyWidth unit when using
+    // interpolation.
+    private static final float MIN_PREVIEW_SAMPLING_RATIO_TO_KEY_WIDTH_WITH_INTERPOLATION = 0.2f;
+    // The angular threshold to use interpolation in radian. PI/12 is 15 degree.
+    private static final double INTERPOLATION_ANGULAR_THRESHOLD = Math.PI / 12.0d;
+    private static final int MAX_INTERPOLATION_PARTITION = 4;
 
     public GestureStrokeWithPreviewPoints(final int pointerId, final GestureStrokeParams params) {
         super(pointerId, params);
@@ -44,6 +57,7 @@
         super.reset();
         mStrokeId++;
         mLastPreviewSize = 0;
+        mLastInterpolatedPreviewIndex = 0;
         mPreviewEventTimes.setLength(0);
         mPreviewXCoordinates.setLength(0);
         mPreviewYCoordinates.setLength(0);
@@ -53,35 +67,49 @@
         return mStrokeId;
     }
 
-    public int getGestureStrokePreviewSize() {
-        return mPreviewEventTimes.getLength();
-    }
-
     @Override
     public void setKeyboardGeometry(final int keyWidth, final int keyboardHeight) {
         super.setKeyboardGeometry(keyWidth, keyboardHeight);
-        final float sampleLength = keyWidth * MIN_PREVIEW_SAMPLE_LENGTH_RATIO_TO_KEY_WIDTH;
-        mMinPreviewSampleLengthSquare = (int)(sampleLength * sampleLength);
+        final float samplingRatioToKeyWidth = ENABLE_INTERPOLATION
+                ? MIN_PREVIEW_SAMPLING_RATIO_TO_KEY_WIDTH_WITH_INTERPOLATION
+                : MIN_PREVIEW_SAMPLING_RATIO_TO_KEY_WIDTH;
+        mMinPreviewSamplingDistance = keyWidth * samplingRatioToKeyWidth;
+        mMinPreviewSamplingDistanceSquared = (int)(
+                mMinPreviewSamplingDistance * mMinPreviewSamplingDistance);
     }
 
-    private boolean needsSampling(final int x, final int y) {
+    private boolean needsSampling(final int x, final int y, final boolean isMajorEvent) {
+        if (ENABLE_INTERPOLATION) {
+            mDistanceFromLastSample += Math.hypot(x - mLastX, y - mLastY);
+            mLastX = x;
+            mLastY = y;
+            if (mDistanceFromLastSample >= mMinPreviewSamplingDistance) {
+                mDistanceFromLastSample = 0.0d;
+                return true;
+            }
+            return false;
+        }
+
         final int dx = x - mLastX;
         final int dy = y - mLastY;
-        return dx * dx + dy * dy >= mMinPreviewSampleLengthSquare;
+        if (isMajorEvent || dx * dx + dy * dy >= mMinPreviewSamplingDistanceSquared) {
+            mLastX = x;
+            mLastY = y;
+            return true;
+        }
+        return false;
     }
 
     @Override
     public boolean addPointOnKeyboard(final int x, final int y, final int time,
             final boolean isMajorEvent) {
-        final boolean onValidArea = super.addPointOnKeyboard(x, y, time, isMajorEvent);
-        if (isMajorEvent || needsSampling(x, y)) {
+        if (needsSampling(x, y, isMajorEvent)) {
             mPreviewEventTimes.add(time);
             mPreviewXCoordinates.add(x);
             mPreviewYCoordinates.add(y);
-            mLastX = x;
-            mLastY = y;
         }
-        return onValidArea;
+        return super.addPointOnKeyboard(x, y, time, isMajorEvent);
+
     }
 
     public void appendPreviewStroke(final ResizableIntArray eventTimes,
@@ -95,4 +123,82 @@
         yCoords.append(mPreviewYCoordinates, mLastPreviewSize, length);
         mLastPreviewSize = mPreviewEventTimes.getLength();
     }
+
+    /**
+     * Calculate interpolated points between the last interpolated point and the end of the trail.
+     * And return the start index of the last interpolated segment of input arrays because it
+     * may need to recalculate the interpolated points in the segment if further segments are
+     * added to this stroke.
+     *
+     * @param lastInterpolatedIndex the start index of the last interpolated segment of
+     *        <code>eventTimes</code>, <code>xCoords</code>, and <code>yCoords</code>.
+     * @param eventTimes the event time array of gesture preview trail to be drawn.
+     * @param xCoords the x-coordinates array of gesture preview trail to be drawn.
+     * @param yCoords the y-coordinates array of gesture preview trail to be drawn.
+     * @return the start index of the last interpolated segment of input arrays.
+     */
+    public int interpolateStrokeAndReturnStartIndexOfLastSegment(final int lastInterpolatedIndex,
+            final ResizableIntArray eventTimes, final ResizableIntArray xCoords,
+            final ResizableIntArray yCoords) {
+        if (!ENABLE_INTERPOLATION) {
+            return lastInterpolatedIndex;
+        }
+        final int size = mPreviewEventTimes.getLength();
+        final int[] pt = mPreviewEventTimes.getPrimitiveArray();
+        final int[] px = mPreviewXCoordinates.getPrimitiveArray();
+        final int[] py = mPreviewYCoordinates.getPrimitiveArray();
+        mInterpolator.reset(px, py, 0, size);
+        // The last segment of gesture stroke needs to be interpolated again because the slope of
+        // the tangent at the last point isn't determined.
+        int lastInterpolatedDrawIndex = lastInterpolatedIndex;
+        int d1 = lastInterpolatedIndex;
+        for (int p2 = mLastInterpolatedPreviewIndex + 1; p2 < size; p2++) {
+            final int p1 = p2 - 1;
+            final int p0 = p1 - 1;
+            final int p3 = p2 + 1;
+            mLastInterpolatedPreviewIndex = p1;
+            lastInterpolatedDrawIndex = d1;
+            mInterpolator.setInterval(p0, p1, p2, p3);
+            final double m1 = Math.atan2(mInterpolator.mSlope1Y, mInterpolator.mSlope1X);
+            final double m2 = Math.atan2(mInterpolator.mSlope2Y, mInterpolator.mSlope2X);
+            final double dm = Math.abs(angularDiff(m2, m1));
+            final int partition = Math.min((int)Math.ceil(dm / INTERPOLATION_ANGULAR_THRESHOLD),
+                    MAX_INTERPOLATION_PARTITION);
+            final int t1 = eventTimes.get(d1);
+            final int dt = pt[p2] - pt[p1];
+            d1++;
+            for (int i = 1; i < partition; i++) {
+                final float t = i / (float)partition;
+                mInterpolator.interpolate(t);
+                eventTimes.add(d1, (int)(dt * t) + t1);
+                xCoords.add(d1, (int)mInterpolator.mInterpolatedX);
+                yCoords.add(d1, (int)mInterpolator.mInterpolatedY);
+                d1++;
+            }
+            eventTimes.add(d1, pt[p2]);
+            xCoords.add(d1, px[p2]);
+            yCoords.add(d1, py[p2]);
+        }
+        return lastInterpolatedDrawIndex;
+    }
+
+    private static final double TWO_PI = Math.PI * 2.0d;
+
+    /**
+     * Calculate the angular of rotation from <code>a0</code> to <code>a1</code>.
+     *
+     * @param a1 the angular to which the rotation ends.
+     * @param a0 the angular from which the rotation starts.
+     * @return the angular rotation value from a0 to a1, normalized to [-PI, +PI].
+     */
+    private static double angularDiff(final double a1, final double a0) {
+        double deltaAngle = a1 - a0;
+        while (deltaAngle > Math.PI) {
+            deltaAngle -= TWO_PI;
+        }
+        while (deltaAngle < -Math.PI) {
+            deltaAngle += TWO_PI;
+        }
+        return deltaAngle;
+    }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/internal/HermiteInterpolator.java b/java/src/com/android/inputmethod/keyboard/internal/HermiteInterpolator.java
new file mode 100644
index 0000000..0ec8153
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/HermiteInterpolator.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.internal;
+
+import com.android.inputmethod.annotations.UsedForTesting;
+
+/**
+ * Interpolates XY-coordinates using Cubic Hermite Curve.
+ */
+public final class HermiteInterpolator {
+    private int[] mXCoords;
+    private int[] mYCoords;
+    private int mMinPos;
+    private int mMaxPos;
+
+    // Working variable to calculate interpolated value.
+    /** The coordinates of the start point of the interval. */
+    public int mP1X, mP1Y;
+    /** The coordinates of the end point of the interval. */
+    public int mP2X, mP2Y;
+    /** The slope of the tangent at the start point. */
+    public float mSlope1X, mSlope1Y;
+    /** The slope of the tangent at the end point. */
+    public float mSlope2X, mSlope2Y;
+    /** The interpolated coordinates.
+     * The return variables of {@link #interpolate(float)} to avoid instantiations.
+     */
+    public float mInterpolatedX, mInterpolatedY;
+
+    public HermiteInterpolator() {
+        // Nothing to do with here.
+    }
+
+    /**
+     * Reset this interpolator to point XY-coordinates data.
+     * @param xCoords the array of x-coordinates. Valid data are in left-open interval
+     *                <code>[minPos, maxPos)</code>.
+     * @param yCoords the array of y-coordinates. Valid data are in left-open interval
+     *                <code>[minPos, maxPos)</code>.
+     * @param minPos the minimum index of left-open interval of valid data.
+     * @param maxPos the maximum index of left-open interval of valid data.
+     */
+    @UsedForTesting
+    public void reset(final int[] xCoords, final int[] yCoords, final int minPos,
+            final int maxPos) {
+        mXCoords = xCoords;
+        mYCoords = yCoords;
+        mMinPos = minPos;
+        mMaxPos = maxPos;
+    }
+
+    /**
+     * Set interpolation interval.
+     * <p>
+     * The start and end coordinates of the interval will be set in {@link #mP1X}, {@link #mP1Y},
+     * {@link #mP2X}, and {@link #mP2Y}. The slope of the tangents at start and end points will be
+     * set in {@link #mSlope1X}, {@link #mSlope1Y}, {@link #mSlope2X}, and {@link #mSlope2Y}.
+     *
+     * @param p0 the index just before interpolation interval. If <code>p1</code> points the start
+     *           of valid points, <code>p0</code> must be less than <code>minPos</code> of
+     *           {@link #reset(int[],int[],int,int)}.
+     * @param p1 the start index of interpolation interval.
+     * @param p2 the end index of interpolation interval.
+     * @param p3 the index just after interpolation interval. If <code>p2</code> points the end of
+     *           valid points, <code>p3</code> must be equal or greater than <code>maxPos</code> of
+     *           {@link #reset(int[],int[],int,int)}.
+     */
+    @UsedForTesting
+    public void setInterval(final int p0, final int p1, final int p2, final int p3) {
+        mP1X = mXCoords[p1];
+        mP1Y = mYCoords[p1];
+        mP2X = mXCoords[p2];
+        mP2Y = mYCoords[p2];
+        // A(ax,ay) is the vector p1->p2.
+        final int ax = mP2X - mP1X;
+        final int ay = mP2Y - mP1Y;
+
+        // Calculate the slope of the tangent at p1.
+        if (p0 >= mMinPos) {
+            // p1 has previous valid point p0.
+            // The slope of the tangent is half of the vector p0->p2.
+            mSlope1X = (mP2X - mXCoords[p0]) / 2.0f;
+            mSlope1Y = (mP2Y - mYCoords[p0]) / 2.0f;
+        } else if (p3 < mMaxPos) {
+            // p1 has no previous valid point, but p2 has next valid point p3.
+            // B(bx,by) is the slope vector of the tangent at p2.
+            final float bx = (mXCoords[p3] - mP1X) / 2.0f;
+            final float by = (mYCoords[p3] - mP1Y) / 2.0f;
+            final float crossProdAB = ax * by - ay * bx;
+            final float dotProdAB = ax * bx + ay * by;
+            final float normASquare = ax * ax + ay * ay;
+            final float invHalfNormASquare = 1.0f / normASquare / 2.0f;
+            // The slope of the tangent is the mirror image of vector B to vector A.
+            mSlope1X = invHalfNormASquare * (dotProdAB * ax + crossProdAB * ay);
+            mSlope1Y = invHalfNormASquare * (dotProdAB * ay - crossProdAB * ax);
+        } else {
+            // p1 and p2 have no previous valid point. (Interval has only point p1 and p2)
+            mSlope1X = ax;
+            mSlope1Y = ay;
+        }
+
+        // Calculate the slope of the tangent at p2.
+        if (p3 < mMaxPos) {
+            // p2 has next valid point p3.
+            // The slope of the tangent is half of the vector p1->p3.
+            mSlope2X = (mXCoords[p3] - mP1X) / 2.0f;
+            mSlope2Y = (mYCoords[p3] - mP1Y) / 2.0f;
+        } else if (p0 >= mMinPos) {
+            // p2 has no next valid point, but p1 has previous valid point p0.
+            // B(bx,by) is the slope vector of the tangent at p1.
+            final float bx = (mP2X - mXCoords[p0]) / 2.0f;
+            final float by = (mP2Y - mYCoords[p0]) / 2.0f;
+            final float crossProdAB = ax * by - ay * bx;
+            final float dotProdAB = ax * bx + ay * by;
+            final float normASquare = ax * ax + ay * ay;
+            final float invHalfNormASquare = 1.0f / normASquare / 2.0f;
+            // The slope of the tangent is the mirror image of vector B to vector A.
+            mSlope2X = invHalfNormASquare * (dotProdAB * ax + crossProdAB * ay);
+            mSlope2Y = invHalfNormASquare * (dotProdAB * ay - crossProdAB * ax);
+        } else {
+            // p1 and p2 has no previous valid point. (Interval has only point p1 and p2)
+            mSlope2X = ax;
+            mSlope2Y = ay;
+        }
+    }
+
+    /**
+     * Calculate interpolation value at <code>t</code> in unit interval <code>[0,1]</code>.
+     * <p>
+     * On the unit interval [0,1], given a starting point p1 at t=0 and an ending point p2 at t=1
+     * with the slope of the tangent m1 at p1 and m2 at p2, the polynomial of cubic Hermite curve
+     * can be defined by
+     *   p(t) = (1+2t)(1-t)(1-t)*p1 + t(1-t)(1-t)*m1 + (3-2t)t^2*p2 + (t-1)t^2*m2
+     * where t is an element of [0,1].
+     * <p>
+     * The interpolated XY-coordinates will be set in {@link #mInterpolatedX} and
+     * {@link #mInterpolatedY}.
+     *
+     * @param t the interpolation parameter. The value must be in close interval <code>[0,1]</code>.
+     */
+    @UsedForTesting
+    public void interpolate(final float t) {
+        final float omt = 1.0f - t;
+        final float tm2 = 2.0f * t;
+        final float k1 = 1.0f + tm2;
+        final float k2 = 3.0f - tm2;
+        final float omt2 = omt * omt;
+        final float t2 = t * t;
+        mInterpolatedX = (k1 * mP1X + t * mSlope1X) * omt2 + (k2 * mP2X - omt * mSlope2X) * t2;
+        mInterpolatedY = (k1 * mP1Y + t * mSlope1Y) * omt2 + (k2 * mP2Y - omt * mSlope2Y) * t2;
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 92b68dc..3262a9a 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -1143,11 +1143,11 @@
         if (!mWordComposer.isComposingWord()) return;
         final String typedWord = mWordComposer.getTypedWord();
         if (typedWord.length() > 0) {
-            commitChosenWord(typedWord, LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD,
-                    separatorString);
             if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
                 ResearchLogger.getInstance().onWordFinished(typedWord, mWordComposer.isBatchMode());
             }
+            commitChosenWord(typedWord, LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD,
+                    separatorString);
         }
     }
 
@@ -1905,7 +1905,6 @@
     private boolean handleSeparator(final int primaryCode, final int x, final int y,
             final int spaceState) {
         if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
-            ResearchLogger.recordTimeForLogUnitSplit();
             ResearchLogger.latinIME_handleSeparator(primaryCode, mWordComposer.isComposingWord());
         }
         boolean didAutoCorrect = false;
diff --git a/java/src/com/android/inputmethod/latin/StringUtils.java b/java/src/com/android/inputmethod/latin/StringUtils.java
index 90c3fcd..dcb514a 100644
--- a/java/src/com/android/inputmethod/latin/StringUtils.java
+++ b/java/src/com/android/inputmethod/latin/StringUtils.java
@@ -22,6 +22,10 @@
 import java.util.Locale;
 
 public final class StringUtils {
+    public static final int CAPITALIZE_NONE = 0;  // No caps, or mixed case
+    public static final int CAPITALIZE_FIRST = 1; // First only
+    public static final int CAPITALIZE_ALL = 2;   // All caps
+
     private StringUtils() {
         // This utility class is not publicly instantiable.
     }
@@ -171,4 +175,31 @@
         }
         return list.toArray(new String[list.size()]);
     }
+
+    // This method assumes the text is not empty or null.
+    public static int getCapitalizationType(final String text) {
+        // If the first char is not uppercase, then the word is either all lower case or
+        // camel case, and in either case we return CAPITALIZE_NONE.
+        if (!Character.isUpperCase(text.codePointAt(0))) return CAPITALIZE_NONE;
+        final int len = text.length();
+        int capsCount = 1;
+        int letterCount = 1;
+        for (int i = 1; i < len; i = text.offsetByCodePoints(i, 1)) {
+            if (1 != capsCount && letterCount != capsCount) break;
+            final int codePoint = text.codePointAt(i);
+            if (Character.isUpperCase(codePoint)) {
+                ++capsCount;
+                ++letterCount;
+            } else if (Character.isLetter(codePoint)) {
+                // We need to discount non-letters since they may not be upper-case, but may
+                // still be part of a word (e.g. single quote or dash, as in "IT'S" or "FULL-TIME")
+                ++letterCount;
+            }
+        }
+        // We know the first char is upper case. So we want to test if either every letter other
+        // than the first is lower case, or if they are all upper case. If the string is exactly
+        // one char long, then we will arrive here with letterCount 1, and this is correct, too.
+        if (1 == capsCount) return CAPITALIZE_FIRST;
+        return (letterCount == capsCount ? CAPITALIZE_ALL : CAPITALIZE_NONE);
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/define/ProductionFlag.java b/java/src/com/android/inputmethod/latin/define/ProductionFlag.java
index 699e47b..dc937fb 100644
--- a/java/src/com/android/inputmethod/latin/define/ProductionFlag.java
+++ b/java/src/com/android/inputmethod/latin/define/ProductionFlag.java
@@ -28,5 +28,5 @@
     // USES_DEVELOPMENT_ONLY_DIAGNOSTICS must be false for any production build.
     public static final boolean USES_DEVELOPMENT_ONLY_DIAGNOSTICS_DEBUG = false;
 
-    public static final boolean IS_HARDWARE_KEYBOARD_SUPPORTED = true;
+    public static final boolean IS_HARDWARE_KEYBOARD_SUPPORTED = false;
 }
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
index 38a2648..8d3b062 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
@@ -58,10 +58,6 @@
 
     public static final String PREF_USE_CONTACTS_KEY = "pref_spellcheck_use_contacts";
 
-    public static final int CAPITALIZE_NONE = 0; // No caps, or mixed case
-    public static final int CAPITALIZE_FIRST = 1; // First only
-    public static final int CAPITALIZE_ALL = 2; // All caps
-
     private final static String[] EMPTY_STRING_ARRAY = new String[0];
     private Map<String, DictionaryPool> mDictionaryPools = CollectionUtils.newSynchronizedTreeMap();
     private Map<String, UserBinaryDictionary> mUserDictionaries =
@@ -325,13 +321,13 @@
                 }
                 Collections.reverse(mSuggestions);
                 StringUtils.removeDupes(mSuggestions);
-                if (CAPITALIZE_ALL == capitalizeType) {
+                if (StringUtils.CAPITALIZE_ALL == capitalizeType) {
                     for (int i = 0; i < mSuggestions.size(); ++i) {
                         // get(i) returns a CharSequence which is actually a String so .toString()
                         // should return the same object.
                         mSuggestions.set(i, mSuggestions.get(i).toString().toUpperCase(locale));
                     }
-                } else if (CAPITALIZE_FIRST == capitalizeType) {
+                } else if (StringUtils.CAPITALIZE_FIRST == capitalizeType) {
                     for (int i = 0; i < mSuggestions.size(); ++i) {
                         // Likewise
                         mSuggestions.set(i, StringUtils.toTitleCase(
@@ -438,31 +434,4 @@
         }
         return new DictAndProximity(dictionaryCollection, proximityInfo);
     }
-
-    // This method assumes the text is not empty or null.
-    public static int getCapitalizationType(String text) {
-        // If the first char is not uppercase, then the word is either all lower case,
-        // and in either case we return CAPITALIZE_NONE.
-        if (!Character.isUpperCase(text.codePointAt(0))) return CAPITALIZE_NONE;
-        final int len = text.length();
-        int capsCount = 1;
-        int letterCount = 1;
-        for (int i = 1; i < len; i = text.offsetByCodePoints(i, 1)) {
-            if (1 != capsCount && letterCount != capsCount) break;
-            final int codePoint = text.codePointAt(i);
-            if (Character.isUpperCase(codePoint)) {
-                ++capsCount;
-                ++letterCount;
-            } else if (Character.isLetter(codePoint)) {
-                // We need to discount non-letters since they may not be upper-case, but may
-                // still be part of a word (e.g. single quote or dash, as in "IT'S" or "FULL-TIME")
-                ++letterCount;
-            }
-        }
-        // We know the first char is upper case. So we want to test if either every letter other
-        // than the first is lower case, or if they are all upper case. If the string is exactly
-        // one char long, then we will arrive here with letterCount 1, and this is correct, too.
-        if (1 == capsCount) return CAPITALIZE_FIRST;
-        return (letterCount == capsCount ? CAPITALIZE_ALL : CAPITALIZE_NONE);
-    }
 }
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
index 4f86a31..b150632 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
@@ -150,7 +150,7 @@
             // Greek letters are either in the 370~3FF range (Greek & Coptic), or in the
             // 1F00~1FFF range (Greek extended). Our dictionary contains both sort of characters.
             // Our dictionary also contains a few words with 0xF2; it would be best to check
-            // if that's correct, but a Google search does return results for these words so
+            // if that's correct, but a web search does return results for these words so
             // they are probably okay.
             return (codePoint >= 0x370 && codePoint <= 0x3FF)
                     || (codePoint >= 0x1F00 && codePoint <= 0x1FFF)
@@ -214,14 +214,14 @@
         // If the word is in there as is, then it's in the dictionary. If not, we'll test lower
         // case versions, but only if the word is not already all-lower case or mixed case.
         if (dict.isValidWord(text)) return true;
-        if (AndroidSpellCheckerService.CAPITALIZE_NONE == capitalizeType) return false;
+        if (StringUtils.CAPITALIZE_NONE == capitalizeType) return false;
 
         // If we come here, we have a capitalized word (either First- or All-).
         // Downcase the word and look it up again. If the word is only capitalized, we
         // tested all possibilities, so if it's still negative we can return false.
         final String lowerCaseText = text.toLowerCase(mLocale);
         if (dict.isValidWord(lowerCaseText)) return true;
-        if (AndroidSpellCheckerService.CAPITALIZE_FIRST == capitalizeType) return false;
+        if (StringUtils.CAPITALIZE_FIRST == capitalizeType) return false;
 
         // If the lower case version is not in the dictionary, it's still possible
         // that we have an all-caps version of a word that needs to be capitalized
@@ -296,7 +296,7 @@
                 }
             }
 
-            final int capitalizeType = AndroidSpellCheckerService.getCapitalizationType(text);
+            final int capitalizeType = StringUtils.getCapitalizationType(text);
             boolean isInDict = true;
             DictAndProximity dictInfo = null;
             try {
diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java
index 6e486ce..fa124f3 100644
--- a/java/src/com/android/inputmethod/research/ResearchLogger.java
+++ b/java/src/com/android/inputmethod/research/ResearchLogger.java
@@ -1635,8 +1635,7 @@
         final String scrubbedAutoCorrection = scrubDigitsFromString(autoCorrection);
         final ResearchLogger researchLogger = getInstance();
         researchLogger.mCurrentLogUnit.initializeSuggestions(suggestedWords);
-        researchLogger.commitCurrentLogUnitAsWord(scrubbedAutoCorrection, Long.MAX_VALUE,
-                isBatchMode);
+        researchLogger.onWordFinished(scrubbedAutoCorrection, isBatchMode);
 
         // Add the autocorrection logStatement at the end of the logUnit for the committed word.
         // We have to do this after calling commitCurrentLogUnitAsWord, because it may split the
diff --git a/native/jni/Android.mk b/native/jni/Android.mk
index 423c24e..cbe9515 100644
--- a/native/jni/Android.mk
+++ b/native/jni/Android.mk
@@ -24,20 +24,14 @@
 include $(CLEAR_VARS)
 
 LATIN_IME_SRC_DIR := src
-LATIN_IME_SRC_FULLPATH_DIR := $(LOCAL_PATH)/$(LATIN_IME_SRC_DIR)
 
-LOCAL_C_INCLUDES += \
-    $(LATIN_IME_SRC_FULLPATH_DIR) \
-    $(LATIN_IME_SRC_FULLPATH_DIR)/suggest \
-    $(LATIN_IME_SRC_FULLPATH_DIR)/suggest/core/dicnode \
-    $(LATIN_IME_SRC_FULLPATH_DIR)/suggest/core/policy \
-    $(LATIN_IME_SRC_FULLPATH_DIR)/suggest/core/session
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/$(LATIN_IME_SRC_DIR)
 
 LOCAL_CFLAGS += -Werror -Wall -Wextra -Weffc++ -Wformat=2 -Wcast-qual -Wcast-align \
     -Wwrite-strings -Wfloat-equal -Wpointer-arith -Winit-self -Wredundant-decls -Wno-system-headers
 
 ifeq ($(TARGET_ARCH), arm)
-ifneq ($(TARGET_GCC_VERSION), 4.7)
+ifeq ($(TARGET_GCC_VERSION), 4.6)
 LOCAL_CFLAGS += -Winline
 endif # TARGET_GCC_VERSION
 endif # TARGET_ARCH
@@ -58,19 +52,27 @@
     correction.cpp \
     dictionary.cpp \
     dic_traverse_wrapper.cpp \
+    digraph_utils.cpp \
     proximity_info.cpp \
     proximity_info_params.cpp \
     proximity_info_state.cpp \
     proximity_info_state_utils.cpp \
     unigram_dictionary.cpp \
     words_priority_queue.cpp \
-    suggest/core/dicnode/dic_node.cpp \
-    suggest/core/dicnode/dic_nodes_cache.cpp \
-    suggest/core/dicnode/dic_node_utils.cpp \
+    suggest/core/suggest.cpp \
+    $(addprefix suggest/core/dicnode/, \
+        dic_node.cpp \
+        dic_node_utils.cpp \
+        dic_nodes_cache.cpp) \
     suggest/core/policy/weighting.cpp \
     suggest/core/session/dic_traverse_session.cpp \
-    suggest/gesture_suggest.cpp \
-    suggest/typing_suggest.cpp
+    suggest/policyimpl/gesture/gesture_suggest_policy_factory.cpp \
+    $(addprefix suggest/policyimpl/typing/, \
+        scoring_params.cpp \
+        typing_scoring.cpp \
+        typing_suggest_policy.cpp \
+        typing_traversal.cpp \
+        typing_weighting.cpp)
 
 LOCAL_SRC_FILES := \
     $(LATIN_IME_JNI_SRC_FILES) \
@@ -125,6 +127,4 @@
 #################### Clean up the tmp vars
 LATIN_IME_CORE_SRC_FILES :=
 LATIN_IME_JNI_SRC_FILES :=
-LATIN_IME_GESTURE_IMPL_SRC_FILES :=
 LATIN_IME_SRC_DIR :=
-LATIN_IME_SRC_FULLPATH_DIR :=
diff --git a/native/jni/jni_common.cpp b/native/jni/jni_common.cpp
index 1ea2041..8e5c508 100644
--- a/native/jni/jni_common.cpp
+++ b/native/jni/jni_common.cpp
@@ -16,12 +16,12 @@
 
 #define LOG_TAG "LatinIME: jni"
 
+#include "jni_common.h"
+
 #include "com_android_inputmethod_keyboard_ProximityInfo.h"
 #include "com_android_inputmethod_latin_BinaryDictionary.h"
 #include "com_android_inputmethod_latin_DicTraverseSession.h"
 #include "defines.h"
-#include "jni.h"
-#include "jni_common.h"
 
 /*
  * Returns the JNI version on success, -1 on failure.
diff --git a/native/jni/src/bigram_dictionary.cpp b/native/jni/src/bigram_dictionary.cpp
index 43e59a2..9289038 100644
--- a/native/jni/src/bigram_dictionary.cpp
+++ b/native/jni/src/bigram_dictionary.cpp
@@ -39,7 +39,7 @@
 void BigramDictionary::addWordBigram(int *word, int length, int probability, int *bigramProbability,
         int *bigramCodePoints, int *outputTypes) const {
     word[length] = 0;
-    if (DEBUG_DICT) {
+    if (DEBUG_DICT_FULL) {
 #ifdef FLAG_DBG
         char s[length + 1];
         for (int i = 0; i <= length; i++) s[i] = static_cast<char>(word[i]);
@@ -57,7 +57,7 @@
         }
         insertAt++;
     }
-    if (DEBUG_DICT) {
+    if (DEBUG_DICT_FULL) {
         AKLOGI("Bigram: InsertAt -> %d MAX_RESULTS: %d", insertAt, MAX_RESULTS);
     }
     if (insertAt >= MAX_RESULTS) {
@@ -76,7 +76,7 @@
         *dest++ = *word++;
     }
     *dest = 0; // NULL terminate
-    if (DEBUG_DICT) {
+    if (DEBUG_DICT_FULL) {
         AKLOGI("Bigram: Added word at %d", insertAt);
     }
 }
diff --git a/native/jni/src/correction.cpp b/native/jni/src/correction.cpp
index 671507e..76234f8 100644
--- a/native/jni/src/correction.cpp
+++ b/native/jni/src/correction.cpp
@@ -954,7 +954,13 @@
 
 
 // In dictionary.cpp, getSuggestion() method,
-// suggestion scores are computed using the below formula.
+// When USE_SUGGEST_INTERFACE_FOR_TYPING is true:
+//   SUGGEST_INTERFACE_OUTPUT_SCALE was multiplied to the original suggestion scores to convert
+//   them to integers.
+//     score = (int)((original score) * SUGGEST_INTERFACE_OUTPUT_SCALE)
+//   Undo the scaling here to recover the original score.
+//     normalizedScore = ((float)score) / SUGGEST_INTERFACE_OUTPUT_SCALE
+// Otherwise: suggestion scores are computed using the below formula.
 // original score
 //  := powf(mTypedLetterMultiplier (this is defined 2),
 //         (the number of matched characters between typed word and suggested word))
@@ -991,16 +997,20 @@
         return 0.0f;
     }
 
+    // add a weight based on edit distance.
+    // distance <= max(afterLength, beforeLength) == afterLength,
+    // so, 0 <= distance / afterLength <= 1
+    const float weight = 1.0f - static_cast<float>(distance) / static_cast<float>(afterLength);
+
+    if (USE_SUGGEST_INTERFACE_FOR_TYPING) {
+        return (static_cast<float>(score) / SUGGEST_INTERFACE_OUTPUT_SCALE) * weight;
+    }
     const float maxScore = score >= S_INT_MAX ? static_cast<float>(S_INT_MAX)
             : static_cast<float>(MAX_INITIAL_SCORE)
                     * powf(static_cast<float>(TYPED_LETTER_MULTIPLIER),
                             static_cast<float>(min(beforeLength, afterLength - spaceCount)))
                     * static_cast<float>(FULL_WORD_MULTIPLIER);
 
-    // add a weight based on edit distance.
-    // distance <= max(afterLength, beforeLength) == afterLength,
-    // so, 0 <= distance / afterLength <= 1
-    const float weight = 1.0f - static_cast<float>(distance) / static_cast<float>(afterLength);
     return (static_cast<float>(score) / maxScore) * weight;
 }
 } // namespace latinime
diff --git a/native/jni/src/correction.h b/native/jni/src/correction.h
index f0d6210..a9e9b48 100644
--- a/native/jni/src/correction.h
+++ b/native/jni/src/correction.h
@@ -307,7 +307,7 @@
     mNeedsToTraverseAllNodes = true;
 }
 
-inline bool Correction::isSingleQuote(const int c) {
+AK_FORCE_INLINE bool Correction::isSingleQuote(const int c) {
     const int userTypedChar = mProximityInfoState.getPrimaryCodePointAt(mInputIndex);
     return (c == KEYCODE_SINGLE_QUOTE && userTypedChar != KEYCODE_SINGLE_QUOTE);
 }
diff --git a/native/jni/src/defines.h b/native/jni/src/defines.h
index 6e09815..a456912 100644
--- a/native/jni/src/defines.h
+++ b/native/jni/src/defines.h
@@ -287,6 +287,7 @@
 
 #define CALIBRATE_SCORE_BY_TOUCH_COORDINATES true
 #define SUGGEST_MULTIPLE_WORDS true
+#define USE_SUGGEST_INTERFACE_FOR_TYPING true
 #define SUGGEST_INTERFACE_OUTPUT_SCALE 1000000.0f
 
 // The following "rate"s are used as a multiplier before dividing by 100, so they are in percent.
diff --git a/native/jni/src/dictionary.cpp b/native/jni/src/dictionary.cpp
index 6deab36..ed6ddb5 100644
--- a/native/jni/src/dictionary.cpp
+++ b/native/jni/src/dictionary.cpp
@@ -16,14 +16,18 @@
 
 #define LOG_TAG "LatinIME: dictionary.cpp"
 
+#include "dictionary.h"
+
+#include <map> // TODO: remove
 #include <stdint.h>
 
 #include "bigram_dictionary.h"
 #include "binary_format.h"
 #include "defines.h"
-#include "dictionary.h"
 #include "dic_traverse_wrapper.h"
-#include "gesture_suggest.h"
+#include "suggest/core/suggest.h"
+#include "suggest/policyimpl/gesture/gesture_suggest_policy_factory.h"
+#include "suggest/policyimpl/typing/typing_suggest_policy_factory.h"
 #include "unigram_dictionary.h"
 
 namespace latinime {
@@ -34,13 +38,15 @@
           mDictSize(dictSize), mMmapFd(mmapFd), mDictBufAdjust(dictBufAdjust),
           mUnigramDictionary(new UnigramDictionary(mOffsetDict, BinaryFormat::getFlags(mDict))),
           mBigramDictionary(new BigramDictionary(mOffsetDict)),
-          mGestureSuggest(new GestureSuggest()) {
+          mGestureSuggest(new Suggest(GestureSuggestPolicyFactory::getGestureSuggestPolicy())),
+          mTypingSuggest(new Suggest(TypingSuggestPolicyFactory::getTypingSuggestPolicy())) {
 }
 
 Dictionary::~Dictionary() {
     delete mUnigramDictionary;
     delete mBigramDictionary;
     delete mGestureSuggest;
+    delete mTypingSuggest;
 }
 
 int Dictionary::getSuggestions(ProximityInfo *proximityInfo, void *traverseSession,
@@ -60,14 +66,26 @@
         }
         return result;
     } else {
-        std::map<int, int> bigramMap;
-        uint8_t bigramFilter[BIGRAM_FILTER_BYTE_SIZE];
-        mBigramDictionary->fillBigramAddressToProbabilityMapAndFilter(prevWordCodePoints,
-                prevWordLength, &bigramMap, bigramFilter);
-        result = mUnigramDictionary->getSuggestions(proximityInfo, xcoordinates, ycoordinates,
-                inputCodePoints, inputSize, &bigramMap, bigramFilter, useFullEditDistance, outWords,
-                frequencies, outputTypes);
-        return result;
+        if (USE_SUGGEST_INTERFACE_FOR_TYPING) {
+            DicTraverseWrapper::initDicTraverseSession(
+                    traverseSession, this, prevWordCodePoints, prevWordLength);
+            result = mTypingSuggest->getSuggestions(proximityInfo, traverseSession, xcoordinates,
+                    ycoordinates, times, pointerIds, inputCodePoints, inputSize, commitPoint,
+                    outWords, frequencies, spaceIndices, outputTypes);
+            if (DEBUG_DICT) {
+                DUMP_RESULT(outWords, frequencies);
+            }
+            return result;
+        } else {
+            std::map<int, int> bigramMap;
+            uint8_t bigramFilter[BIGRAM_FILTER_BYTE_SIZE];
+            mBigramDictionary->fillBigramAddressToProbabilityMapAndFilter(prevWordCodePoints,
+                    prevWordLength, &bigramMap, bigramFilter);
+            result = mUnigramDictionary->getSuggestions(proximityInfo, xcoordinates, ycoordinates,
+                    inputCodePoints, inputSize, &bigramMap, bigramFilter, useFullEditDistance,
+                    outWords, frequencies, outputTypes);
+            return result;
+        }
     }
 }
 
diff --git a/native/jni/src/dictionary.h b/native/jni/src/dictionary.h
index 449b95a..8c6a7de 100644
--- a/native/jni/src/dictionary.h
+++ b/native/jni/src/dictionary.h
@@ -79,6 +79,7 @@
     const UnigramDictionary *mUnigramDictionary;
     const BigramDictionary *mBigramDictionary;
     SuggestInterface *mGestureSuggest;
+    SuggestInterface *mTypingSuggest;
 };
 } // namespace latinime
 #endif // LATINIME_DICTIONARY_H
diff --git a/native/jni/src/digraph_utils.cpp b/native/jni/src/digraph_utils.cpp
new file mode 100644
index 0000000..8781c50
--- /dev/null
+++ b/native/jni/src/digraph_utils.cpp
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "binary_format.h"
+#include "defines.h"
+#include "digraph_utils.h"
+
+namespace latinime {
+
+const DigraphUtils::digraph_t DigraphUtils::GERMAN_UMLAUT_DIGRAPHS[] =
+        { { 'a', 'e', 0x00E4 }, // U+00E4 : LATIN SMALL LETTER A WITH DIAERESIS
+        { 'o', 'e', 0x00F6 },   // U+00F6 : LATIN SMALL LETTER O WITH DIAERESIS
+        { 'u', 'e', 0x00FC } }; // U+00FC : LATIN SMALL LETTER U WITH DIAERESIS
+const DigraphUtils::digraph_t DigraphUtils::FRENCH_LIGATURES_DIGRAPHS[] =
+        { { 'a', 'e', 0x00E6 }, // U+00E6 : LATIN SMALL LETTER AE
+        { 'o', 'e', 0x0153 } }; // U+0153 : LATIN SMALL LIGATURE OE
+
+/* static */ bool DigraphUtils::hasDigraphForCodePoint(
+        const int dictFlags, const int compositeGlyphCodePoint) {
+    if (DigraphUtils::getDigraphForCodePoint(dictFlags, compositeGlyphCodePoint)) {
+        return true;
+    }
+    return false;
+}
+
+// Retrieves the set of all digraphs associated with the given dictionary.
+// Returns the size of the digraph array, or 0 if none exist.
+/* static */ int DigraphUtils::getAllDigraphsForDictionaryAndReturnSize(
+        const int dictFlags, const DigraphUtils::digraph_t **digraphs) {
+    if (BinaryFormat::REQUIRES_GERMAN_UMLAUT_PROCESSING & dictFlags) {
+        *digraphs = DigraphUtils::GERMAN_UMLAUT_DIGRAPHS;
+        return NELEMS(DigraphUtils::GERMAN_UMLAUT_DIGRAPHS);
+    }
+    if (BinaryFormat::REQUIRES_FRENCH_LIGATURES_PROCESSING & dictFlags) {
+        *digraphs = DigraphUtils::FRENCH_LIGATURES_DIGRAPHS;
+        return NELEMS(DigraphUtils::FRENCH_LIGATURES_DIGRAPHS);
+    }
+    return 0;
+}
+
+// Returns the digraph codepoint for the given composite glyph codepoint and digraph codepoint index
+// (which specifies the first or second codepoint in the digraph).
+/* static */ int DigraphUtils::getDigraphCodePointForIndex(const int dictFlags,
+        const int compositeGlyphCodePoint, const DigraphCodePointIndex digraphCodePointIndex) {
+    if (digraphCodePointIndex == NOT_A_DIGRAPH_INDEX) {
+        return NOT_A_CODE_POINT;
+    }
+    const DigraphUtils::digraph_t *digraph =
+            DigraphUtils::getDigraphForCodePoint(dictFlags, compositeGlyphCodePoint);
+    if (!digraph) {
+        return NOT_A_CODE_POINT;
+    }
+    if (digraphCodePointIndex == FIRST_DIGRAPH_CODEPOINT) {
+        return digraph->first;
+    } else if (digraphCodePointIndex == SECOND_DIGRAPH_CODEPOINT) {
+        return digraph->second;
+    }
+    ASSERT(false);
+    return NOT_A_CODE_POINT;
+}
+
+/**
+ * Returns the digraph for the input composite glyph codepoint, or 0 if none exists.
+ * dictFlags: the dictionary flags needed to determine which digraphs are supported.
+ * compositeGlyphCodePoint: the method returns the digraph corresponding to this codepoint.
+ */
+/* static */ const DigraphUtils::digraph_t *DigraphUtils::getDigraphForCodePoint(
+        const int dictFlags, const int compositeGlyphCodePoint) {
+    const DigraphUtils::digraph_t *digraphs = 0;
+    const int digraphsSize =
+            DigraphUtils::getAllDigraphsForDictionaryAndReturnSize(dictFlags, &digraphs);
+    for (int i = 0; i < digraphsSize; i++) {
+        if (digraphs[i].compositeGlyph == compositeGlyphCodePoint) {
+            return &digraphs[i];
+        }
+    }
+    return 0;
+}
+
+} // namespace latinime
diff --git a/native/jni/src/digraph_utils.h b/native/jni/src/digraph_utils.h
new file mode 100644
index 0000000..6e364b6
--- /dev/null
+++ b/native/jni/src/digraph_utils.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DIGRAPH_UTILS_H
+#define DIGRAPH_UTILS_H
+
+namespace latinime {
+
+class DigraphUtils {
+ public:
+    typedef enum {
+        NOT_A_DIGRAPH_INDEX,
+        FIRST_DIGRAPH_CODEPOINT,
+        SECOND_DIGRAPH_CODEPOINT
+    } DigraphCodePointIndex;
+
+    typedef struct { int first; int second; int compositeGlyph; } digraph_t;
+
+    static bool hasDigraphForCodePoint(const int dictFlags, const int compositeGlyphCodePoint);
+    static int getAllDigraphsForDictionaryAndReturnSize(
+            const int dictFlags, const digraph_t **digraphs);
+    static int getDigraphCodePointForIndex(const int dictFlags, const int compositeGlyphCodePoint,
+            const DigraphCodePointIndex digraphCodePointIndex);
+
+ private:
+    DISALLOW_IMPLICIT_CONSTRUCTORS(DigraphUtils);
+    static const digraph_t *getDigraphForCodePoint(
+            const int dictFlags, const int compositeGlyphCodePoint);
+
+    static const digraph_t GERMAN_UMLAUT_DIGRAPHS[];
+    static const digraph_t FRENCH_LIGATURES_DIGRAPHS[];
+};
+} // namespace latinime
+#endif // DIGRAPH_UTILS_H
diff --git a/native/jni/src/suggest/core/dictionary/shortcut_utils.h b/native/jni/src/suggest/core/dictionary/shortcut_utils.h
new file mode 100644
index 0000000..c411408
--- /dev/null
+++ b/native/jni/src/suggest/core/dictionary/shortcut_utils.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LATINIME_SHORTCUT_UTILS
+#define LATINIME_SHORTCUT_UTILS
+
+#include "defines.h"
+#include "suggest/core/dicnode/dic_node_utils.h"
+#include "terminal_attributes.h"
+
+namespace latinime {
+
+class ShortcutUtils {
+ public:
+    static int outputShortcuts(const TerminalAttributes *const terminalAttributes,
+            int outputWordIndex, const int finalScore, int *const outputCodePoints,
+            int *const frequencies, int *const outputTypes, const bool sameAsTyped) {
+        TerminalAttributes::ShortcutIterator iterator = terminalAttributes->getShortcutIterator();
+        while (iterator.hasNextShortcutTarget() && outputWordIndex < MAX_RESULTS) {
+            int shortcutTarget[MAX_WORD_LENGTH];
+            int shortcutProbability;
+            const int shortcutTargetStringLength = iterator.getNextShortcutTarget(
+                    MAX_WORD_LENGTH, shortcutTarget, &shortcutProbability);
+            int shortcutScore;
+            int kind;
+            if (shortcutProbability == BinaryFormat::WHITELIST_SHORTCUT_PROBABILITY
+                    && sameAsTyped) {
+                shortcutScore = S_INT_MAX;
+                kind = Dictionary::KIND_WHITELIST;
+            } else {
+                // shortcut entry's score == its base entry's score - 1
+                shortcutScore = finalScore;
+                // Protection against int underflow
+                shortcutScore = max(S_INT_MIN + 1, shortcutScore) - 1;
+                kind = Dictionary::KIND_CORRECTION;
+            }
+            outputTypes[outputWordIndex] = kind;
+            frequencies[outputWordIndex] = shortcutScore;
+            frequencies[outputWordIndex] = max(S_INT_MIN + 1, shortcutScore) - 1;
+            const int startIndex2 = outputWordIndex * MAX_WORD_LENGTH;
+            DicNodeUtils::appendTwoWords(0, 0, shortcutTarget, shortcutTargetStringLength,
+                    &outputCodePoints[startIndex2]);
+            ++outputWordIndex;
+        }
+        return outputWordIndex;
+    }
+
+ private:
+    DISALLOW_IMPLICIT_CONSTRUCTORS(ShortcutUtils);
+};
+} // namespace latinime
+#endif // LATINIME_SHORTCUT_UTILS
diff --git a/native/jni/src/suggest/core/policy/traversal.h b/native/jni/src/suggest/core/policy/traversal.h
index 1d5082f..02c358a 100644
--- a/native/jni/src/suggest/core/policy/traversal.h
+++ b/native/jni/src/suggest/core/policy/traversal.h
@@ -20,6 +20,9 @@
 #include "defines.h"
 
 namespace latinime {
+
+class DicTraverseSession;
+
 class Traversal {
  public:
     virtual int getMaxPointerCount() const = 0;
diff --git a/native/jni/src/suggest/core/policy/weighting.cpp b/native/jni/src/suggest/core/policy/weighting.cpp
index 4d08fa0..e62b704 100644
--- a/native/jni/src/suggest/core/policy/weighting.cpp
+++ b/native/jni/src/suggest/core/policy/weighting.cpp
@@ -14,14 +14,15 @@
  * limitations under the License.
  */
 
+#include "suggest/core/policy/weighting.h"
+
 #include "char_utils.h"
 #include "defines.h"
-#include "dic_node.h"
-#include "dic_node_profiler.h"
-#include "dic_node_utils.h"
-#include "dic_traverse_session.h"
 #include "hash_map_compat.h"
-#include "weighting.h"
+#include "suggest/core/dicnode/dic_node.h"
+#include "suggest/core/dicnode/dic_node_profiler.h"
+#include "suggest/core/dicnode/dic_node_utils.h"
+#include "suggest/core/session/dic_traverse_session.h"
 
 namespace latinime {
 
diff --git a/native/jni/src/suggest/core/policy/weighting.h b/native/jni/src/suggest/core/policy/weighting.h
index 83a0f4b..b92dbe2 100644
--- a/native/jni/src/suggest/core/policy/weighting.h
+++ b/native/jni/src/suggest/core/policy/weighting.h
@@ -18,6 +18,7 @@
 #define LATINIME_WEIGHTING_H
 
 #include "defines.h"
+#include "hash_map_compat.h"
 
 namespace latinime {
 
diff --git a/native/jni/src/suggest/core/session/dic_traverse_session.cpp b/native/jni/src/suggest/core/session/dic_traverse_session.cpp
index 1f781dd..ef6616e 100644
--- a/native/jni/src/suggest/core/session/dic_traverse_session.cpp
+++ b/native/jni/src/suggest/core/session/dic_traverse_session.cpp
@@ -14,12 +14,13 @@
  * limitations under the License.
  */
 
+#include "suggest/core/session/dic_traverse_session.h"
+
 #include "defines.h"
 #include "dictionary.h"
-#include "dic_node_utils.h"
-#include "dic_traverse_session.h"
 #include "dic_traverse_wrapper.h"
 #include "jni.h"
+#include "suggest/core/dicnode/dic_node_utils.h"
 
 namespace latinime {
 
diff --git a/native/jni/src/suggest/core/session/dic_traverse_session.h b/native/jni/src/suggest/core/session/dic_traverse_session.h
index af036f8..62e1d1a 100644
--- a/native/jni/src/suggest/core/session/dic_traverse_session.h
+++ b/native/jni/src/suggest/core/session/dic_traverse_session.h
@@ -21,10 +21,10 @@
 #include <vector>
 
 #include "defines.h"
-#include "dic_nodes_cache.h"
 #include "hash_map_compat.h"
 #include "jni.h"
 #include "proximity_info_state.h"
+#include "suggest/core/dicnode/dic_nodes_cache.h"
 
 namespace latinime {
 
diff --git a/native/jni/src/suggest/core/suggest.cpp b/native/jni/src/suggest/core/suggest.cpp
new file mode 100644
index 0000000..1e97a91
--- /dev/null
+++ b/native/jni/src/suggest/core/suggest.cpp
@@ -0,0 +1,520 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "suggest/core/suggest.h"
+
+#include "char_utils.h"
+#include "dictionary.h"
+#include "proximity_info.h"
+#include "suggest/core/dicnode/dic_node.h"
+#include "suggest/core/dicnode/dic_node_priority_queue.h"
+#include "suggest/core/dicnode/dic_node_vector.h"
+#include "suggest/core/dictionary/shortcut_utils.h"
+#include "suggest/core/policy/scoring.h"
+#include "suggest/core/policy/traversal.h"
+#include "suggest/core/policy/weighting.h"
+#include "suggest/core/session/dic_traverse_session.h"
+#include "terminal_attributes.h"
+
+namespace latinime {
+
+// Initialization of class constants.
+const int Suggest::LOOKAHEAD_DIC_NODES_CACHE_SIZE = 25;
+const int Suggest::MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT = 16;
+const int Suggest::MIN_CONTINUOUS_SUGGESTION_INPUT_SIZE = 2;
+const float Suggest::AUTOCORRECT_CLASSIFICATION_THRESHOLD = 0.33f;
+const float Suggest::AUTOCORRECT_LANGUAGE_FEATURE_THRESHOLD = 0.6f;
+
+const bool Suggest::CORRECT_SPACE_OMISSION = true;
+const bool Suggest::CORRECT_TRANSPOSITION = true;
+const bool Suggest::CORRECT_INSERTION = true;
+const bool Suggest::CORRECT_OMISSION_G = true;
+
+/**
+ * Returns a set of suggestions for the given input touch points. The commitPoint argument indicates
+ * whether to prematurely commit the suggested words up to the given point for sentence-level
+ * suggestion.
+ *
+ * Note: Currently does not support concurrent calls across threads. Continuous suggestion is
+ * automatically activated for sequential calls that share the same starting input.
+ * TODO: Stop detecting continuous suggestion. Start using traverseSession instead.
+ */
+int Suggest::getSuggestions(ProximityInfo *pInfo, void *traverseSession,
+        int *inputXs, int *inputYs, int *times, int *pointerIds, int *inputCodePoints,
+        int inputSize, int commitPoint, int *outWords, int *frequencies, int *outputIndices,
+        int *outputTypes) const {
+    PROF_OPEN;
+    PROF_START(0);
+    const float maxSpatialDistance = TRAVERSAL->getMaxSpatialDistance();
+    DicTraverseSession *tSession = static_cast<DicTraverseSession *>(traverseSession);
+    tSession->setupForGetSuggestions(pInfo, inputCodePoints, inputSize, inputXs, inputYs, times,
+            pointerIds, maxSpatialDistance, TRAVERSAL->getMaxPointerCount());
+    // TODO: Add the way to evaluate cache
+
+    initializeSearch(tSession, commitPoint);
+    PROF_END(0);
+    PROF_START(1);
+
+    // keep expanding search dicNodes until all have terminated.
+    while (tSession->getDicTraverseCache()->activeSize() > 0) {
+        expandCurrentDicNodes(tSession);
+        tSession->getDicTraverseCache()->advanceActiveDicNodes();
+        tSession->getDicTraverseCache()->advanceInputIndex(inputSize);
+    }
+    PROF_END(1);
+    PROF_START(2);
+    const int size = outputSuggestions(tSession, frequencies, outWords, outputIndices, outputTypes);
+    PROF_END(2);
+    PROF_CLOSE;
+    return size;
+}
+
+/**
+ * Initializes the search at the root of the lexicon trie. Note that when possible the search will
+ * continue suggestion from where it left off during the last call.
+ */
+void Suggest::initializeSearch(DicTraverseSession *traverseSession, int commitPoint) const {
+    if (!traverseSession->getProximityInfoState(0)->isUsed()) {
+        return;
+    }
+    if (TRAVERSAL->allowPartialCommit()) {
+        commitPoint = 0;
+    }
+
+    if (traverseSession->getInputSize() > MIN_CONTINUOUS_SUGGESTION_INPUT_SIZE
+            && traverseSession->isContinuousSuggestionPossible()) {
+        if (commitPoint == 0) {
+            // Continue suggestion
+            traverseSession->getDicTraverseCache()->continueSearch();
+        } else {
+            // Continue suggestion after partial commit.
+            DicNode *topDicNode =
+                    traverseSession->getDicTraverseCache()->setCommitPoint(commitPoint);
+            traverseSession->setPrevWordPos(topDicNode->getPrevWordNodePos());
+            traverseSession->getDicTraverseCache()->continueSearch();
+            traverseSession->setPartiallyCommited();
+        }
+    } else {
+        // Restart recognition at the root.
+        traverseSession->resetCache(TRAVERSAL->getMaxCacheSize(), MAX_RESULTS);
+        // Create a new dic node here
+        DicNode rootNode;
+        DicNodeUtils::initAsRoot(traverseSession->getDicRootPos(),
+                traverseSession->getOffsetDict(), traverseSession->getPrevWordPos(), &rootNode);
+        traverseSession->getDicTraverseCache()->copyPushActive(&rootNode);
+    }
+}
+
+/**
+ * Outputs the final list of suggestions (i.e., terminal nodes).
+ */
+int Suggest::outputSuggestions(DicTraverseSession *traverseSession, int *frequencies,
+        int *outputCodePoints, int *spaceIndices, int *outputTypes) const {
+    const int terminalSize = min(MAX_RESULTS,
+            static_cast<int>(traverseSession->getDicTraverseCache()->terminalSize()));
+    DicNode terminals[MAX_RESULTS]; // Avoiding non-POD variable length array
+
+    for (int index = terminalSize - 1; index >= 0; --index) {
+        traverseSession->getDicTraverseCache()->popTerminal(&terminals[index]);
+    }
+
+    const float languageWeight = SCORING->getAdjustedLanguageWeight(
+            traverseSession, terminals, terminalSize);
+
+    int outputWordIndex = 0;
+    // Insert most probable word at index == 0 as long as there is one terminal at least
+    const bool hasMostProbableString =
+            SCORING->getMostProbableString(traverseSession, terminalSize, languageWeight,
+                    &outputCodePoints[0], &outputTypes[0], &frequencies[0]);
+    if (hasMostProbableString) {
+        ++outputWordIndex;
+    }
+
+    // Initial value of the loop index for terminal nodes (words)
+    int doubleLetterTerminalIndex = -1;
+    DoubleLetterLevel doubleLetterLevel = NOT_A_DOUBLE_LETTER;
+    SCORING->searchWordWithDoubleLetter(terminals, terminalSize,
+            &doubleLetterTerminalIndex, &doubleLetterLevel);
+
+    int maxScore = S_INT_MIN;
+    // Output suggestion results here
+    for (int terminalIndex = 0; terminalIndex < terminalSize && outputWordIndex < MAX_RESULTS;
+            ++terminalIndex) {
+        DicNode *terminalDicNode = &terminals[terminalIndex];
+        if (DEBUG_GEO_FULL) {
+            terminalDicNode->dump("OUT:");
+        }
+        const float doubleLetterCost = SCORING->getDoubleLetterDemotionDistanceCost(
+                terminalIndex, doubleLetterTerminalIndex, doubleLetterLevel);
+        const float compoundDistance = terminalDicNode->getCompoundDistance(languageWeight)
+                + doubleLetterCost;
+        const TerminalAttributes terminalAttributes(traverseSession->getOffsetDict(),
+                terminalDicNode->getFlags(), terminalDicNode->getAttributesPos());
+        const int originalTerminalProbability = terminalDicNode->getProbability();
+
+        // Do not suggest words with a 0 probability, or entries that are blacklisted or do not
+        // represent a word. However, we should still submit their shortcuts if any.
+        const bool isValidWord =
+                originalTerminalProbability > 0 && !terminalAttributes.isBlacklistedOrNotAWord();
+        // Increase output score of top typing suggestion to ensure autocorrection.
+        // TODO: Better integration with java side autocorrection logic.
+        // Force autocorrection for obvious long multi-word suggestions.
+        const bool isForceCommitMultiWords = TRAVERSAL->allowPartialCommit()
+                && (traverseSession->isPartiallyCommited()
+                        || (traverseSession->getInputSize() >= MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT
+                                && terminalDicNode->hasMultipleWords()));
+
+        const int finalScore = SCORING->calculateFinalScore(
+                compoundDistance, traverseSession->getInputSize(),
+                isForceCommitMultiWords || (isValidWord && SCORING->doesAutoCorrectValidWord()));
+
+        maxScore = max(maxScore, finalScore);
+
+        if (TRAVERSAL->allowPartialCommit()) {
+            // Index for top typing suggestion should be 0.
+            if (isValidWord && outputWordIndex == 0) {
+                terminalDicNode->outputSpacePositionsResult(spaceIndices);
+            }
+        }
+
+        // Do not suggest words with a 0 probability, or entries that are blacklisted or do not
+        // represent a word. However, we should still submit their shortcuts if any.
+        if (isValidWord) {
+            outputTypes[outputWordIndex] = Dictionary::KIND_CORRECTION;
+            frequencies[outputWordIndex] = finalScore;
+            // Populate the outputChars array with the suggested word.
+            const int startIndex = outputWordIndex * MAX_WORD_LENGTH;
+            terminalDicNode->outputResult(&outputCodePoints[startIndex]);
+            ++outputWordIndex;
+        }
+
+        const bool sameAsTyped = TRAVERSAL->sameAsTyped(traverseSession, terminalDicNode);
+        outputWordIndex = ShortcutUtils::outputShortcuts(&terminalAttributes, outputWordIndex,
+                finalScore, outputCodePoints, frequencies, outputTypes, sameAsTyped);
+        DicNode::managedDelete(terminalDicNode);
+    }
+
+    if (hasMostProbableString) {
+        SCORING->safetyNetForMostProbableString(terminalSize, maxScore,
+                &outputCodePoints[0], &frequencies[0]);
+    }
+    return outputWordIndex;
+}
+
+/**
+ * Expands the dicNodes in the current search priority queue by advancing to the possible child
+ * nodes based on the next touch point(s) (or no touch points for lookahead)
+ */
+void Suggest::expandCurrentDicNodes(DicTraverseSession *traverseSession) const {
+    const int inputSize = traverseSession->getInputSize();
+    DicNodeVector childDicNodes(TRAVERSAL->getDefaultExpandDicNodeSize());
+    DicNode omissionDicNode;
+
+    // TODO: Find more efficient caching
+    const bool shouldDepthLevelCache = TRAVERSAL->shouldDepthLevelCache(traverseSession);
+    if (shouldDepthLevelCache) {
+        traverseSession->getDicTraverseCache()->updateLastCachedInputIndex();
+    }
+    if (DEBUG_CACHE) {
+        AKLOGI("expandCurrentDicNodes depth level cache = %d, inputSize = %d",
+                shouldDepthLevelCache, inputSize);
+    }
+    while (traverseSession->getDicTraverseCache()->activeSize() > 0) {
+        DicNode dicNode;
+        traverseSession->getDicTraverseCache()->popActive(&dicNode);
+        if (dicNode.isTotalInputSizeExceedingLimit()) {
+            return;
+        }
+        childDicNodes.clear();
+        const int point0Index = dicNode.getInputIndex(0);
+        const bool canDoLookAheadCorrection =
+                TRAVERSAL->canDoLookAheadCorrection(traverseSession, &dicNode);
+        const bool isLookAheadCorrection = canDoLookAheadCorrection
+                && traverseSession->getDicTraverseCache()->
+                        isLookAheadCorrectionInputIndex(static_cast<int>(point0Index));
+        const bool isCompletion = dicNode.isCompletion(inputSize);
+
+        const bool shouldNodeLevelCache =
+                TRAVERSAL->shouldNodeLevelCache(traverseSession, &dicNode);
+        if (shouldDepthLevelCache || shouldNodeLevelCache) {
+            if (DEBUG_CACHE) {
+                dicNode.dump("PUSH_CACHE");
+            }
+            traverseSession->getDicTraverseCache()->copyPushContinue(&dicNode);
+            dicNode.setCached();
+        }
+
+        if (isLookAheadCorrection) {
+            // The algorithm maintains a small set of "deferred" nodes that have not consumed the
+            // latest touch point yet. These are needed to apply look-ahead correction operations
+            // that require special handling of the latest touch point. For example, with insertions
+            // (e.g., "thiis" -> "this") the latest touch point should not be consumed at all.
+            if (CORRECT_TRANSPOSITION) {
+                processDicNodeAsTransposition(traverseSession, &dicNode);
+            }
+            if (CORRECT_INSERTION) {
+                processDicNodeAsInsertion(traverseSession, &dicNode);
+            }
+        } else { // !isLookAheadCorrection
+            // Only consider typing error corrections if the normalized compound distance is
+            // below a spatial distance threshold.
+            // NOTE: the threshold may need to be updated if scoring model changes.
+            // TODO: Remove. Do not prune node here.
+            const bool allowsErrorCorrections = TRAVERSAL->allowsErrorCorrections(&dicNode);
+            // Process for handling space substitution (e.g., hevis => he is)
+            if (allowsErrorCorrections
+                    && TRAVERSAL->isSpaceSubstitutionTerminal(traverseSession, &dicNode)) {
+                createNextWordDicNode(traverseSession, &dicNode, true /* spaceSubstitution */);
+            }
+
+            DicNodeUtils::getAllChildDicNodes(
+                    &dicNode, traverseSession->getOffsetDict(), &childDicNodes);
+
+            const int childDicNodesSize = childDicNodes.getSizeAndLock();
+            for (int i = 0; i < childDicNodesSize; ++i) {
+                DicNode *const childDicNode = childDicNodes[i];
+                if (isCompletion) {
+                    // Handle forward lookahead when the lexicon letter exceeds the input size.
+                    processDicNodeAsMatch(traverseSession, childDicNode);
+                    continue;
+                }
+                if (allowsErrorCorrections
+                        && TRAVERSAL->isOmission(traverseSession, &dicNode, childDicNode)) {
+                    // TODO: (Gesture) Change weight between omission and substitution errors
+                    // TODO: (Gesture) Terminal node should not be handled as omission
+                    omissionDicNode.initByCopy(childDicNode);
+                    processDicNodeAsOmission(traverseSession, &omissionDicNode);
+                }
+                const ProximityType proximityType = TRAVERSAL->getProximityType(
+                        traverseSession, &dicNode, childDicNode);
+                switch (proximityType) {
+                    // TODO: Consider the difference of proximityType here
+                    case MATCH_CHAR:
+                    case PROXIMITY_CHAR:
+                        processDicNodeAsMatch(traverseSession, childDicNode);
+                        break;
+                    case ADDITIONAL_PROXIMITY_CHAR:
+                        if (allowsErrorCorrections) {
+                            processDicNodeAsAdditionalProximityChar(traverseSession, &dicNode,
+                                    childDicNode);
+                        }
+                        break;
+                    case SUBSTITUTION_CHAR:
+                        if (allowsErrorCorrections) {
+                            processDicNodeAsSubstitution(traverseSession, &dicNode, childDicNode);
+                        }
+                        break;
+                    case UNRELATED_CHAR:
+                        // Just drop this node and do nothing.
+                        break;
+                    default:
+                        // Just drop this node and do nothing.
+                        break;
+                }
+            }
+
+            // Push the node for look-ahead correction
+            if (allowsErrorCorrections && canDoLookAheadCorrection) {
+                traverseSession->getDicTraverseCache()->copyPushNextActive(&dicNode);
+            }
+        }
+    }
+}
+
+void Suggest::processTerminalDicNode(
+        DicTraverseSession *traverseSession, DicNode *dicNode) const {
+    if (dicNode->getCompoundDistance() >= static_cast<float>(MAX_VALUE_FOR_WEIGHTING)) {
+        return;
+    }
+    if (!dicNode->isTerminalWordNode()) {
+        return;
+    }
+    if (TRAVERSAL->needsToTraverseAllUserInput()
+            && dicNode->getInputIndex(0) < traverseSession->getInputSize()) {
+        return;
+    }
+
+    if (dicNode->shouldBeFilterdBySafetyNetForBigram()) {
+        return;
+    }
+    // Create a non-cached node here.
+    DicNode terminalDicNode;
+    DicNodeUtils::initByCopy(dicNode, &terminalDicNode);
+    Weighting::addCostAndForwardInputIndex(WEIGHTING, CT_TERMINAL, traverseSession, 0,
+            &terminalDicNode, traverseSession->getBigramCacheMap());
+    traverseSession->getDicTraverseCache()->copyPushTerminal(&terminalDicNode);
+}
+
+/**
+ * Adds the expanded dicNode to the next search priority queue. Also creates an additional next word
+ * (by the space omission error correction) search path if input dicNode is on a terminal node.
+ */
+void Suggest::processExpandedDicNode(
+        DicTraverseSession *traverseSession, DicNode *dicNode) const {
+    processTerminalDicNode(traverseSession, dicNode);
+    if (dicNode->getCompoundDistance() < static_cast<float>(MAX_VALUE_FOR_WEIGHTING)) {
+        if (TRAVERSAL->isSpaceOmissionTerminal(traverseSession, dicNode)) {
+            createNextWordDicNode(traverseSession, dicNode, false /* spaceSubstitution */);
+        }
+        const int allowsLookAhead = !(dicNode->hasMultipleWords()
+                && dicNode->isCompletion(traverseSession->getInputSize()));
+        if (dicNode->hasChildren() && allowsLookAhead) {
+            traverseSession->getDicTraverseCache()->copyPushNextActive(dicNode);
+        }
+    }
+    DicNode::managedDelete(dicNode);
+}
+
+void Suggest::processDicNodeAsMatch(DicTraverseSession *traverseSession,
+        DicNode *childDicNode) const {
+    weightChildNode(traverseSession, childDicNode);
+    processExpandedDicNode(traverseSession, childDicNode);
+}
+
+void Suggest::processDicNodeAsAdditionalProximityChar(DicTraverseSession *traverseSession,
+        DicNode *dicNode, DicNode *childDicNode) const {
+    Weighting::addCostAndForwardInputIndex(WEIGHTING, CT_ADDITIONAL_PROXIMITY,
+            traverseSession, dicNode, childDicNode, 0 /* bigramCacheMap */);
+    weightChildNode(traverseSession, childDicNode);
+    processExpandedDicNode(traverseSession, childDicNode);
+}
+
+void Suggest::processDicNodeAsSubstitution(DicTraverseSession *traverseSession,
+        DicNode *dicNode, DicNode *childDicNode) const {
+    Weighting::addCostAndForwardInputIndex(WEIGHTING, CT_SUBSTITUTION, traverseSession,
+            dicNode, childDicNode, 0 /* bigramCacheMap */);
+    weightChildNode(traverseSession, childDicNode);
+    processExpandedDicNode(traverseSession, childDicNode);
+}
+
+/**
+ * Handle the dicNode as an omission error (e.g., ths => this). Skip the current letter and consider
+ * matches for all possible next letters. Note that just skipping the current letter without any
+ * other conditions tends to flood the search dic nodes cache with omission nodes. Instead, check
+ * the possible *next* letters after the omission to better limit search to plausible omissions.
+ * Note that apostrophes are handled as omissions.
+ */
+void Suggest::processDicNodeAsOmission(
+        DicTraverseSession *traverseSession, DicNode *dicNode) const {
+    // If the omission is surely intentional that it should incur zero cost.
+    const bool isZeroCostOmission = dicNode->isZeroCostOmission();
+    DicNodeVector childDicNodes;
+
+    DicNodeUtils::getAllChildDicNodes(dicNode, traverseSession->getOffsetDict(), &childDicNodes);
+
+    const int size = childDicNodes.getSizeAndLock();
+    for (int i = 0; i < size; i++) {
+        DicNode *const childDicNode = childDicNodes[i];
+        if (!isZeroCostOmission) {
+            // Treat this word as omission
+            Weighting::addCostAndForwardInputIndex(WEIGHTING, CT_OMISSION, traverseSession,
+                    dicNode, childDicNode, 0 /* bigramCacheMap */);
+        }
+        weightChildNode(traverseSession, childDicNode);
+
+        if (!TRAVERSAL->isPossibleOmissionChildNode(traverseSession, dicNode, childDicNode)) {
+            DicNode::managedDelete(childDicNode);
+            continue;
+        }
+        processExpandedDicNode(traverseSession, childDicNode);
+    }
+}
+
+/**
+ * Handle the dicNode as an insertion error (e.g., thiis => this). Skip the current touch point and
+ * consider matches for the next touch point.
+ */
+void Suggest::processDicNodeAsInsertion(DicTraverseSession *traverseSession,
+        DicNode *dicNode) const {
+    const int16_t pointIndex = dicNode->getInputIndex(0);
+    DicNodeVector childDicNodes;
+    DicNodeUtils::getProximityChildDicNodes(dicNode, traverseSession->getOffsetDict(),
+            traverseSession->getProximityInfoState(0), pointIndex + 1, true, &childDicNodes);
+    const int size = childDicNodes.getSizeAndLock();
+    for (int i = 0; i < size; i++) {
+        DicNode *const childDicNode = childDicNodes[i];
+        Weighting::addCostAndForwardInputIndex(WEIGHTING, CT_INSERTION, traverseSession,
+                dicNode, childDicNode, 0 /* bigramCacheMap */);
+        processExpandedDicNode(traverseSession, childDicNode);
+    }
+}
+
+/**
+ * Handle the dicNode as a transposition error (e.g., thsi => this). Swap the next two touch points.
+ */
+void Suggest::processDicNodeAsTransposition(DicTraverseSession *traverseSession,
+        DicNode *dicNode) const {
+    const int16_t pointIndex = dicNode->getInputIndex(0);
+    DicNodeVector childDicNodes1;
+    DicNodeUtils::getProximityChildDicNodes(dicNode, traverseSession->getOffsetDict(),
+            traverseSession->getProximityInfoState(0), pointIndex + 1, false, &childDicNodes1);
+    const int childSize1 = childDicNodes1.getSizeAndLock();
+    for (int i = 0; i < childSize1; i++) {
+        if (childDicNodes1[i]->hasChildren()) {
+            DicNodeVector childDicNodes2;
+            DicNodeUtils::getProximityChildDicNodes(
+                    childDicNodes1[i], traverseSession->getOffsetDict(),
+                    traverseSession->getProximityInfoState(0), pointIndex, false, &childDicNodes2);
+            const int childSize2 = childDicNodes2.getSizeAndLock();
+            for (int j = 0; j < childSize2; j++) {
+                DicNode *const childDicNode2 = childDicNodes2[j];
+                Weighting::addCostAndForwardInputIndex(WEIGHTING, CT_TRANSPOSITION,
+                        traverseSession, childDicNodes1[i], childDicNode2, 0 /* bigramCacheMap */);
+                processExpandedDicNode(traverseSession, childDicNode2);
+            }
+        }
+        DicNode::managedDelete(childDicNodes1[i]);
+    }
+}
+
+/**
+ * Weight child node by aligning it to the key
+ */
+void Suggest::weightChildNode(DicTraverseSession *traverseSession, DicNode *dicNode) const {
+    const int inputSize = traverseSession->getInputSize();
+    if (dicNode->isCompletion(inputSize)) {
+        Weighting::addCostAndForwardInputIndex(WEIGHTING, CT_COMPLETION, traverseSession,
+                0 /* parentDicNode */, dicNode, 0 /* bigramCacheMap */);
+    } else { // completion
+        Weighting::addCostAndForwardInputIndex(WEIGHTING, CT_MATCH, traverseSession,
+                0 /* parentDicNode */, dicNode, 0 /* bigramCacheMap */);
+    }
+}
+
+/**
+ * Creates a new dicNode that represents a space insertion at the end of the input dicNode. Also
+ * incorporates the unigram / bigram score for the ending word into the new dicNode.
+ */
+void Suggest::createNextWordDicNode(DicTraverseSession *traverseSession, DicNode *dicNode,
+        const bool spaceSubstitution) const {
+    if (!TRAVERSAL->isGoodToTraverseNextWord(dicNode)) {
+        return;
+    }
+
+    // Create a non-cached node here.
+    DicNode newDicNode;
+    DicNodeUtils::initAsRootWithPreviousWord(traverseSession->getDicRootPos(),
+            traverseSession->getOffsetDict(), dicNode, &newDicNode);
+    Weighting::addCostAndForwardInputIndex(WEIGHTING, CT_NEW_WORD, traverseSession, dicNode,
+            &newDicNode, traverseSession->getBigramCacheMap());
+    if (spaceSubstitution) {
+        // Merge this with CT_NEW_WORD
+        Weighting::addCostAndForwardInputIndex(WEIGHTING, CT_SPACE_SUBSTITUTION,
+                traverseSession, 0, &newDicNode, 0 /* bigramCacheMap */);
+    }
+    traverseSession->getDicTraverseCache()->copyPushNextActive(&newDicNode);
+}
+} // namespace latinime
diff --git a/native/jni/src/suggest/core/suggest.h b/native/jni/src/suggest/core/suggest.h
new file mode 100644
index 0000000..a1e7e7a
--- /dev/null
+++ b/native/jni/src/suggest/core/suggest.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LATINIME_SUGGEST_IMPL_H
+#define LATINIME_SUGGEST_IMPL_H
+
+#include "defines.h"
+#include "suggest/core/suggest_interface.h"
+#include "suggest/core/policy/suggest_policy.h"
+
+namespace latinime {
+
+class DicNode;
+class DicTraverseSession;
+class ProximityInfo;
+class Scoring;
+class Traversal;
+class Weighting;
+
+class Suggest : public SuggestInterface {
+ public:
+    AK_FORCE_INLINE Suggest(const SuggestPolicy *const suggestPolicy)
+            : TRAVERSAL(suggestPolicy->getTraversal()),
+              SCORING(suggestPolicy->getScoring()), WEIGHTING(suggestPolicy->getWeighting()) {}
+    AK_FORCE_INLINE virtual ~Suggest() {}
+    int getSuggestions(ProximityInfo *pInfo, void *traverseSession, int *inputXs, int *inputYs,
+            int *times, int *pointerIds, int *inputCodePoints, int inputSize, int commitPoint,
+            int *outWords, int *frequencies, int *outputIndices, int *outputTypes) const;
+
+ private:
+    DISALLOW_IMPLICIT_CONSTRUCTORS(Suggest);
+    void createNextWordDicNode(DicTraverseSession *traverseSession, DicNode *dicNode,
+            const bool spaceSubstitution) const;
+    int outputSuggestions(DicTraverseSession *traverseSession, int *frequencies,
+            int *outputCodePoints, int *outputIndices, int *outputTypes) const;
+    void initializeSearch(DicTraverseSession *traverseSession, int commitPoint) const;
+    void expandCurrentDicNodes(DicTraverseSession *traverseSession) const;
+    void processTerminalDicNode(DicTraverseSession *traverseSession, DicNode *dicNode) const;
+    void processExpandedDicNode(DicTraverseSession *traverseSession, DicNode *dicNode) const;
+    void weightChildNode(DicTraverseSession *traverseSession, DicNode *dicNode) const;
+    float getAutocorrectScore(DicTraverseSession *traverseSession, DicNode *dicNode) const;
+    void generateFeatures(
+            DicTraverseSession *traverseSession, DicNode *dicNode, float *features) const;
+    void processDicNodeAsOmission(DicTraverseSession *traverseSession, DicNode *dicNode) const;
+    void processDicNodeAsTransposition(DicTraverseSession *traverseSession,
+            DicNode *dicNode) const;
+    void processDicNodeAsInsertion(DicTraverseSession *traverseSession, DicNode *dicNode) const;
+    void processDicNodeAsAdditionalProximityChar(DicTraverseSession *traverseSession,
+            DicNode *dicNode, DicNode *childDicNode) const;
+    void processDicNodeAsSubstitution(DicTraverseSession *traverseSession, DicNode *dicNode,
+            DicNode *childDicNode) const;
+    void processDicNodeAsMatch(DicTraverseSession *traverseSession,
+            DicNode *childDicNode) const;
+
+    // Dic nodes cache size for lookahead (autocompletion)
+    static const int LOOKAHEAD_DIC_NODES_CACHE_SIZE;
+    // Max characters to lookahead
+    static const int MAX_LOOKAHEAD;
+    // Inputs longer than this will autocorrect if the suggestion is multi-word
+    static const int MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT;
+    static const int MIN_CONTINUOUS_SUGGESTION_INPUT_SIZE;
+    // Base value for converting costs into scores (low so will not autocorrect without classifier)
+    static const float BASE_OUTPUT_SCORE;
+
+    // Threshold for autocorrection classifier
+    static const float AUTOCORRECT_CLASSIFICATION_THRESHOLD;
+    // Threshold for computing the language model feature for autocorrect classification
+    static const float AUTOCORRECT_LANGUAGE_FEATURE_THRESHOLD;
+
+    // Typing error correction settings
+    static const bool CORRECT_SPACE_OMISSION;
+    static const bool CORRECT_TRANSPOSITION;
+    static const bool CORRECT_INSERTION;
+
+    const Traversal *const TRAVERSAL;
+    const Scoring *const SCORING;
+    const Weighting *const WEIGHTING;
+
+    static const bool CORRECT_OMISSION_G;
+};
+} // namespace latinime
+#endif // LATINIME_SUGGEST_IMPL_H
diff --git a/native/jni/src/suggest/suggest_interface.h b/native/jni/src/suggest/core/suggest_interface.h
similarity index 100%
rename from native/jni/src/suggest/suggest_interface.h
rename to native/jni/src/suggest/core/suggest_interface.h
diff --git a/native/jni/src/suggest/gesture_suggest.cpp b/native/jni/src/suggest/gesture_suggest.cpp
deleted file mode 100644
index fce5621..0000000
--- a/native/jni/src/suggest/gesture_suggest.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "gesture_suggest.h"
-
-namespace latinime {
-    SuggestInterface *(*GestureSuggest::sGestureSuggestFactoryMethod)() = 0;
-
-    GestureSuggest::~GestureSuggest() {
-        delete mSuggestInterface;
-    }
-} // namespace latinime
diff --git a/native/jni/src/suggest/gesture_suggest.h b/native/jni/src/suggest/gesture_suggest.h
deleted file mode 100644
index 82c3a69..0000000
--- a/native/jni/src/suggest/gesture_suggest.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef LATINIME_GESTURE_SUGGEST_H
-#define LATINIME_GESTURE_SUGGEST_H
-
-#include "defines.h"
-#include "suggest_interface.h"
-
-namespace latinime {
-
-class ProximityInfo;
-
-class GestureSuggest : public SuggestInterface {
- public:
-    GestureSuggest() : mSuggestInterface(getGestureSuggestInstance()) {}
-
-    virtual ~GestureSuggest();
-
-    int getSuggestions(ProximityInfo *pInfo, void *traverseSession, int *inputXs, int *inputYs,
-            int *times, int *pointerIds, int *inputCodePoints, int inputSize, int commitPoint,
-            int *outWords, int *frequencies, int *outputIndices, int *outputTypes) const {
-        if (!mSuggestInterface) {
-            return 0;
-        }
-        return mSuggestInterface->getSuggestions(pInfo, traverseSession, inputXs, inputYs, times,
-                pointerIds, inputCodePoints, inputSize, commitPoint, outWords, frequencies,
-                outputIndices, outputTypes);
-    }
-
-    static void setGestureSuggestFactoryMethod(SuggestInterface *(*factoryMethod)()) {
-        sGestureSuggestFactoryMethod = factoryMethod;
-    }
-
- private:
-    DISALLOW_COPY_AND_ASSIGN(GestureSuggest);
-    static SuggestInterface *getGestureSuggestInstance() {
-        if (!sGestureSuggestFactoryMethod) {
-            return 0;
-        }
-        return sGestureSuggestFactoryMethod();
-    }
-
-    static SuggestInterface *(*sGestureSuggestFactoryMethod)();
-    SuggestInterface *mSuggestInterface;
-};
-} // namespace latinime
-#endif // LATINIME_GESTURE_SUGGEST_H
diff --git a/native/jni/src/suggest/typing_suggest.cpp b/native/jni/src/suggest/policyimpl/gesture/gesture_suggest_policy_factory.cpp
similarity index 77%
rename from native/jni/src/suggest/typing_suggest.cpp
rename to native/jni/src/suggest/policyimpl/gesture/gesture_suggest_policy_factory.cpp
index 56bd5b6..6d31739 100644
--- a/native/jni/src/suggest/typing_suggest.cpp
+++ b/native/jni/src/suggest/policyimpl/gesture/gesture_suggest_policy_factory.cpp
@@ -14,12 +14,8 @@
  * limitations under the License.
  */
 
-#include "typing_suggest.h"
+#include "gesture_suggest_policy_factory.h"
 
 namespace latinime {
-    SuggestInterface *(*TypingSuggest::sTypingSuggestFactoryMethod)() = 0;
-
-    TypingSuggest::~TypingSuggest() {
-        delete mSuggestInterface;
-    }
+    const SuggestPolicy *(*GestureSuggestPolicyFactory::sGestureSuggestFactoryMethod)() = 0;
 } // namespace latinime
diff --git a/native/jni/src/suggest/policyimpl/gesture/gesture_suggest_policy_factory.h b/native/jni/src/suggest/policyimpl/gesture/gesture_suggest_policy_factory.h
new file mode 100644
index 0000000..509b01f
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/gesture/gesture_suggest_policy_factory.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LATINIME_GESTURE_SUGGEST_POLICY_FACTORY_H
+#define LATINIME_GESTURE_SUGGEST_POLICY_FACTORY_H
+
+#include "defines.h"
+
+namespace latinime {
+
+class SuggestPolicy;
+
+class GestureSuggestPolicyFactory {
+ public:
+    static void setGestureSuggestPolicyFactoryMethod(const SuggestPolicy *(*factoryMethod)()) {
+        sGestureSuggestFactoryMethod = factoryMethod;
+    }
+
+    static const SuggestPolicy *getGestureSuggestPolicy() {
+        if (!sGestureSuggestFactoryMethod) {
+            return 0;
+        }
+        return sGestureSuggestFactoryMethod();
+    }
+
+ private:
+    DISALLOW_COPY_AND_ASSIGN(GestureSuggestPolicyFactory);
+    static const SuggestPolicy *(*sGestureSuggestFactoryMethod)();
+};
+} // namespace latinime
+#endif // LATINIME_GESTURE_SUGGEST_POLICY_FACTORY_H
diff --git a/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp b/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp
new file mode 100644
index 0000000..0fa684f
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "suggest/policyimpl/typing/scoring_params.h"
+
+namespace latinime {
+// TODO: RENAME all
+const float ScoringParams::MAX_SPATIAL_DISTANCE = 1.0f;
+const int ScoringParams::THRESHOLD_NEXT_WORD_PROBABILITY = 40;
+const int ScoringParams::THRESHOLD_NEXT_WORD_PROBABILITY_FOR_CAPPED = 120;
+const float ScoringParams::AUTOCORRECT_OUTPUT_THRESHOLD = 1.0f;
+const int ScoringParams::MAX_CACHE_DIC_NODE_SIZE = 125;
+const int ScoringParams::THRESHOLD_SHORT_WORD_LENGTH = 4;
+
+const float ScoringParams::DISTANCE_WEIGHT_LENGTH = 0.132f;
+const float ScoringParams::PROXIMITY_COST = 0.086f;
+const float ScoringParams::FIRST_PROXIMITY_COST = 0.104f;
+const float ScoringParams::OMISSION_COST = 0.388f;
+const float ScoringParams::OMISSION_COST_SAME_CHAR = 0.431f;
+const float ScoringParams::OMISSION_COST_FIRST_CHAR = 0.532f;
+const float ScoringParams::INSERTION_COST = 0.670f;
+const float ScoringParams::INSERTION_COST_SAME_CHAR = 0.526f;
+const float ScoringParams::INSERTION_COST_FIRST_CHAR = 0.563f;
+const float ScoringParams::TRANSPOSITION_COST = 0.494f;
+const float ScoringParams::SPACE_SUBSTITUTION_COST = 0.239f;
+const float ScoringParams::ADDITIONAL_PROXIMITY_COST = 0.380f;
+const float ScoringParams::SUBSTITUTION_COST = 0.363f;
+const float ScoringParams::COST_NEW_WORD = 0.054f;
+const float ScoringParams::COST_NEW_WORD_CAPITALIZED = 0.174f;
+const float ScoringParams::DISTANCE_WEIGHT_LANGUAGE = 1.123f;
+const float ScoringParams::COST_FIRST_LOOKAHEAD = 0.462f;
+const float ScoringParams::COST_LOOKAHEAD = 0.092f;
+const float ScoringParams::HAS_PROXIMITY_TERMINAL_COST = 0.126f;
+const float ScoringParams::HAS_EDIT_CORRECTION_TERMINAL_COST = 0.056f;
+const float ScoringParams::HAS_MULTI_WORD_TERMINAL_COST = 0.136f;
+const float ScoringParams::TYPING_BASE_OUTPUT_SCORE = 1.0f;
+const float ScoringParams::TYPING_MAX_OUTPUT_SCORE_PER_INPUT = 0.1f;
+const float ScoringParams::MAX_NORM_DISTANCE_FOR_EDIT = 0.1f;
+} // namespace latinime
diff --git a/native/jni/src/suggest/policyimpl/typing/scoring_params.h b/native/jni/src/suggest/policyimpl/typing/scoring_params.h
new file mode 100644
index 0000000..8f104b3
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/typing/scoring_params.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LATINIME_SCORING_PARAMS_H
+#define LATINIME_SCORING_PARAMS_H
+
+#include "defines.h"
+
+namespace latinime {
+
+class ScoringParams {
+ public:
+    // Fixed model parameters
+    static const float MAX_SPATIAL_DISTANCE;
+    static const int THRESHOLD_NEXT_WORD_PROBABILITY;
+    static const int THRESHOLD_NEXT_WORD_PROBABILITY_FOR_CAPPED;
+    static const float AUTOCORRECT_OUTPUT_THRESHOLD;
+    static const int MAX_CACHE_DIC_NODE_SIZE;
+    static const int THRESHOLD_SHORT_WORD_LENGTH;
+
+    // Numerically optimized parameters (currently for tap typing only).
+    // TODO: add ability to modify these constants programmatically.
+    // TODO: explore optimization of gesture parameters.
+    static const float DISTANCE_WEIGHT_LENGTH;
+    static const float PROXIMITY_COST;
+    static const float FIRST_PROXIMITY_COST;
+    static const float OMISSION_COST;
+    static const float OMISSION_COST_SAME_CHAR;
+    static const float OMISSION_COST_FIRST_CHAR;
+    static const float INSERTION_COST;
+    static const float INSERTION_COST_SAME_CHAR;
+    static const float INSERTION_COST_FIRST_CHAR;
+    static const float TRANSPOSITION_COST;
+    static const float SPACE_SUBSTITUTION_COST;
+    static const float ADDITIONAL_PROXIMITY_COST;
+    static const float SUBSTITUTION_COST;
+    static const float COST_NEW_WORD;
+    static const float COST_NEW_WORD_CAPITALIZED;
+    static const float DISTANCE_WEIGHT_LANGUAGE;
+    static const float COST_FIRST_LOOKAHEAD;
+    static const float COST_LOOKAHEAD;
+    static const float HAS_PROXIMITY_TERMINAL_COST;
+    static const float HAS_EDIT_CORRECTION_TERMINAL_COST;
+    static const float HAS_MULTI_WORD_TERMINAL_COST;
+    static const float TYPING_BASE_OUTPUT_SCORE;
+    static const float TYPING_MAX_OUTPUT_SCORE_PER_INPUT;
+    static const float MAX_NORM_DISTANCE_FOR_EDIT;
+
+ private:
+    DISALLOW_IMPLICIT_CONSTRUCTORS(ScoringParams);
+};
+} // namespace latinime
+#endif // LATINIME_SCORING_PARAMS_H
diff --git a/native/jni/src/suggest/typing_suggest.cpp b/native/jni/src/suggest/policyimpl/typing/typing_scoring.cpp
similarity index 68%
copy from native/jni/src/suggest/typing_suggest.cpp
copy to native/jni/src/suggest/policyimpl/typing/typing_scoring.cpp
index 56bd5b6..d8c6175 100644
--- a/native/jni/src/suggest/typing_suggest.cpp
+++ b/native/jni/src/suggest/policyimpl/typing/typing_scoring.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2013 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,12 +14,8 @@
  * limitations under the License.
  */
 
-#include "typing_suggest.h"
+#include "suggest/policyimpl/typing/typing_scoring.h"
 
 namespace latinime {
-    SuggestInterface *(*TypingSuggest::sTypingSuggestFactoryMethod)() = 0;
-
-    TypingSuggest::~TypingSuggest() {
-        delete mSuggestInterface;
-    }
-} // namespace latinime
+const TypingScoring TypingScoring::sInstance;
+}  // namespace latinime
diff --git a/native/jni/src/suggest/policyimpl/typing/typing_scoring.h b/native/jni/src/suggest/policyimpl/typing/typing_scoring.h
new file mode 100644
index 0000000..90e2133
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/typing/typing_scoring.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LATINIME_TYPING_SCORING_H
+#define LATINIME_TYPING_SCORING_H
+
+#include "defines.h"
+#include "suggest/core/policy/scoring.h"
+#include "suggest/policyimpl/typing/scoring_params.h"
+
+namespace latinime {
+
+class DicNode;
+class DicTraverseSession;
+
+class TypingScoring : public Scoring {
+ public:
+    static const TypingScoring *getInstance() { return &sInstance; }
+
+    AK_FORCE_INLINE bool getMostProbableString(
+            const DicTraverseSession *const traverseSession, const int terminalSize,
+            const float languageWeight, int *const outputCodePoints, int *const type,
+            int *const freq) const {
+        return false;
+    }
+
+    AK_FORCE_INLINE void safetyNetForMostProbableString(const int terminalSize,
+            const int maxScore, int *const outputCodePoints, int *const frequencies) const {
+    }
+
+    AK_FORCE_INLINE void searchWordWithDoubleLetter(DicNode *terminals,
+            const int terminalSize, int *doubleLetterTerminalIndex,
+            DoubleLetterLevel *doubleLetterLevel) const {
+    }
+
+    AK_FORCE_INLINE float getAdjustedLanguageWeight(DicTraverseSession *const traverseSession,
+             DicNode *const terminals, const int size) const {
+        return 1.0f;
+    }
+
+    AK_FORCE_INLINE int calculateFinalScore(const float compoundDistance,
+            const int inputSize, const bool forceCommit) const {
+        const float maxDistance = ScoringParams::DISTANCE_WEIGHT_LANGUAGE
+                + static_cast<float>(inputSize) * ScoringParams::TYPING_MAX_OUTPUT_SCORE_PER_INPUT;
+        return static_cast<int>((ScoringParams::TYPING_BASE_OUTPUT_SCORE
+                - (compoundDistance / maxDistance)
+                + (forceCommit ? ScoringParams::AUTOCORRECT_OUTPUT_THRESHOLD : 0.0f))
+                        * SUGGEST_INTERFACE_OUTPUT_SCALE);
+    }
+
+    AK_FORCE_INLINE float getDoubleLetterDemotionDistanceCost(const int terminalIndex,
+            const int doubleLetterTerminalIndex,
+            const DoubleLetterLevel doubleLetterLevel) const {
+        return 0.0f;
+    }
+
+    AK_FORCE_INLINE bool doesAutoCorrectValidWord() const {
+        return false;
+    }
+
+ private:
+    DISALLOW_COPY_AND_ASSIGN(TypingScoring);
+    static const TypingScoring sInstance;
+
+    TypingScoring() {}
+    ~TypingScoring() {}
+};
+} // namespace latinime
+#endif // LATINIME_TYPING_SCORING_H
diff --git a/native/jni/src/suggest/typing_suggest.cpp b/native/jni/src/suggest/policyimpl/typing/typing_suggest_policy.cpp
similarity index 71%
copy from native/jni/src/suggest/typing_suggest.cpp
copy to native/jni/src/suggest/policyimpl/typing/typing_suggest_policy.cpp
index 56bd5b6..0c27639 100644
--- a/native/jni/src/suggest/typing_suggest.cpp
+++ b/native/jni/src/suggest/policyimpl/typing/typing_suggest_policy.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2013 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,12 +14,8 @@
  * limitations under the License.
  */
 
-#include "typing_suggest.h"
+#include "suggest/policyimpl/typing/typing_suggest_policy.h"
 
 namespace latinime {
-    SuggestInterface *(*TypingSuggest::sTypingSuggestFactoryMethod)() = 0;
-
-    TypingSuggest::~TypingSuggest() {
-        delete mSuggestInterface;
-    }
+const TypingSuggestPolicy TypingSuggestPolicy::sInstance;
 } // namespace latinime
diff --git a/native/jni/src/suggest/policyimpl/typing/typing_suggest_policy.h b/native/jni/src/suggest/policyimpl/typing/typing_suggest_policy.h
new file mode 100644
index 0000000..35f4809
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/typing/typing_suggest_policy.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LATINIME_TYPING_SUGGEST_POLICY_H
+#define LATINIME_TYPING_SUGGEST_POLICY_H
+
+#include "defines.h"
+#include "suggest/core/policy/suggest_policy.h"
+#include "suggest/policyimpl/typing/typing_scoring.h"
+#include "suggest/policyimpl/typing/typing_traversal.h"
+#include "suggest/policyimpl/typing/typing_weighting.h"
+
+namespace latinime {
+
+class Scoring;
+class Traversal;
+class Weighting;
+
+class TypingSuggestPolicy : public SuggestPolicy {
+ public:
+    static const TypingSuggestPolicy *getInstance() { return &sInstance; }
+
+    TypingSuggestPolicy() {}
+    virtual ~TypingSuggestPolicy() {}
+    AK_FORCE_INLINE const Traversal *getTraversal() const {
+        return TypingTraversal::getInstance();
+    }
+
+    AK_FORCE_INLINE const Scoring *getScoring() const {
+        return TypingScoring::getInstance();
+    }
+
+    AK_FORCE_INLINE const Weighting *getWeighting() const {
+        return TypingWeighting::getInstance();
+    }
+
+ private:
+    DISALLOW_COPY_AND_ASSIGN(TypingSuggestPolicy);
+    static const TypingSuggestPolicy sInstance;
+};
+} // namespace latinime
+#endif // LATINIME_TYPING_SUGGEST_POLICY_H
diff --git a/native/jni/src/suggest/policyimpl/typing/typing_suggest_policy_factory.h b/native/jni/src/suggest/policyimpl/typing/typing_suggest_policy_factory.h
new file mode 100644
index 0000000..a67b45b
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/typing/typing_suggest_policy_factory.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LATINIME_TYPING_SUGGEST_POLICY_FACTORY_H
+#define LATINIME_TYPING_SUGGEST_POLICY_FACTORY_H
+
+#include "defines.h"
+#include "typing_suggest_policy.h"
+
+namespace latinime {
+
+class SuggestPolicy;
+
+class TypingSuggestPolicyFactory {
+ public:
+    static const SuggestPolicy *getTypingSuggestPolicy() {
+        return TypingSuggestPolicy::getInstance();
+    }
+
+ private:
+    DISALLOW_COPY_AND_ASSIGN(TypingSuggestPolicyFactory);
+};
+} // namespace latinime
+#endif // LATINIME_TYPING_SUGGEST_POLICY_FACTORY_H
diff --git a/native/jni/src/suggest/typing_suggest.cpp b/native/jni/src/suggest/policyimpl/typing/typing_traversal.cpp
similarity index 61%
copy from native/jni/src/suggest/typing_suggest.cpp
copy to native/jni/src/suggest/policyimpl/typing/typing_traversal.cpp
index 56bd5b6..66f8ba9 100644
--- a/native/jni/src/suggest/typing_suggest.cpp
+++ b/native/jni/src/suggest/policyimpl/typing/typing_traversal.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2013 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,12 +14,11 @@
  * limitations under the License.
  */
 
-#include "typing_suggest.h"
+#include "suggest/policyimpl/typing/typing_traversal.h"
 
 namespace latinime {
-    SuggestInterface *(*TypingSuggest::sTypingSuggestFactoryMethod)() = 0;
-
-    TypingSuggest::~TypingSuggest() {
-        delete mSuggestInterface;
-    }
-} // namespace latinime
+const bool TypingTraversal::CORRECT_OMISSION = true;
+const bool TypingTraversal::CORRECT_SPACE_SUBSTITUTION = true;
+const bool TypingTraversal::CORRECT_SPACE_OMISSION = true;
+const TypingTraversal TypingTraversal::sInstance;
+}  // namespace latinime
diff --git a/native/jni/src/suggest/policyimpl/typing/typing_traversal.h b/native/jni/src/suggest/policyimpl/typing/typing_traversal.h
new file mode 100644
index 0000000..f22029a
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/typing/typing_traversal.h
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LATINIME_TYPING_TRAVERSAL_H
+#define LATINIME_TYPING_TRAVERSAL_H
+
+#include <stdint.h>
+
+#include "char_utils.h"
+#include "defines.h"
+#include "proximity_info_state.h"
+#include "suggest/core/dicnode/dic_node.h"
+#include "suggest/core/dicnode/dic_node_vector.h"
+#include "suggest/core/policy/traversal.h"
+#include "suggest/core/session/dic_traverse_session.h"
+#include "suggest/policyimpl/typing/scoring_params.h"
+
+namespace latinime {
+class TypingTraversal : public Traversal {
+ public:
+    static const TypingTraversal *getInstance() { return &sInstance; }
+
+    AK_FORCE_INLINE int getMaxPointerCount() const {
+        return MAX_POINTER_COUNT;
+    }
+
+    AK_FORCE_INLINE bool allowsErrorCorrections(const DicNode *const dicNode) const {
+        return dicNode->getNormalizedSpatialDistance()
+                < ScoringParams::MAX_NORM_DISTANCE_FOR_EDIT;
+    }
+
+    AK_FORCE_INLINE bool isOmission(const DicTraverseSession *const traverseSession,
+            const DicNode *const dicNode, const DicNode *const childDicNode) const {
+        if (!CORRECT_OMISSION) {
+            return false;
+        }
+        const int inputSize = traverseSession->getInputSize();
+        // TODO: Don't refer to isCompletion?
+        if (dicNode->isCompletion(inputSize)) {
+            return false;
+        }
+        if (dicNode->canBeIntentionalOmission()) {
+            return true;
+        }
+        const int point0Index = dicNode->getInputIndex(0);
+        const int currentBaseLowerCodePoint =
+                toBaseLowerCase(childDicNode->getNodeCodePoint());
+        const int typedBaseLowerCodePoint =
+                toBaseLowerCase(traverseSession->getProximityInfoState(0)
+                        ->getPrimaryCodePointAt(point0Index));
+        return (currentBaseLowerCodePoint != typedBaseLowerCodePoint);
+    }
+
+    AK_FORCE_INLINE bool isSpaceSubstitutionTerminal(
+            const DicTraverseSession *const traverseSession, const DicNode *const dicNode) const {
+        if (!CORRECT_SPACE_SUBSTITUTION) {
+            return false;
+        }
+        if (!canDoLookAheadCorrection(traverseSession, dicNode)) {
+            return false;
+        }
+        const int point0Index = dicNode->getInputIndex(0);
+        return dicNode->isTerminalWordNode()
+                && traverseSession->getProximityInfoState(0)->
+                        hasSpaceProximity(point0Index);
+    }
+
+    AK_FORCE_INLINE bool isSpaceOmissionTerminal(
+            const DicTraverseSession *const traverseSession, const DicNode *const dicNode) const {
+        if (!CORRECT_SPACE_OMISSION) {
+            return false;
+        }
+        const int inputSize = traverseSession->getInputSize();
+        // TODO: Don't refer to isCompletion?
+        if (dicNode->isCompletion(inputSize)) {
+            return false;
+        }
+        if (!dicNode->isTerminalWordNode()) {
+            return false;
+        }
+        const int16_t pointIndex = dicNode->getInputIndex(0);
+        return pointIndex <= inputSize && !dicNode->isTotalInputSizeExceedingLimit()
+                && !dicNode->shouldBeFilterdBySafetyNetForBigram();
+    }
+
+    AK_FORCE_INLINE bool shouldDepthLevelCache(
+            const DicTraverseSession *const traverseSession) const {
+        const int inputSize = traverseSession->getInputSize();
+        return traverseSession->isCacheBorderForTyping(inputSize);
+    }
+
+    AK_FORCE_INLINE bool shouldNodeLevelCache(
+            const DicTraverseSession *const traverseSession, const DicNode *const dicNode) const {
+        return false;
+    }
+
+    AK_FORCE_INLINE bool canDoLookAheadCorrection(
+            const DicTraverseSession *const traverseSession, const DicNode *const dicNode) const {
+        const int inputSize = traverseSession->getInputSize();
+        return dicNode->canDoLookAheadCorrection(inputSize);
+    }
+
+    AK_FORCE_INLINE ProximityType getProximityType(
+            const DicTraverseSession *const traverseSession, const DicNode *const dicNode,
+            const DicNode *const childDicNode) const {
+        return traverseSession->getProximityInfoState(0)->getProximityType(
+                dicNode->getInputIndex(0), childDicNode->getNodeCodePoint(),
+                true /* checkProximityChars */);
+    }
+
+    AK_FORCE_INLINE bool needsToTraverseAllUserInput() const {
+        return true;
+    }
+
+    AK_FORCE_INLINE float getMaxSpatialDistance() const {
+        return ScoringParams::MAX_SPATIAL_DISTANCE;
+    }
+
+    AK_FORCE_INLINE bool allowPartialCommit() const {
+        return true;
+    }
+
+    AK_FORCE_INLINE int getDefaultExpandDicNodeSize() const {
+        return DicNodeVector::DEFAULT_NODES_SIZE_FOR_OPTIMIZATION;
+    }
+
+    AK_FORCE_INLINE bool sameAsTyped(
+            const DicTraverseSession *const traverseSession, const DicNode *const dicNode) const {
+        return traverseSession->getProximityInfoState(0)->sameAsTyped(
+                dicNode->getOutputWordBuf(), dicNode->getDepth());
+    }
+
+    AK_FORCE_INLINE int getMaxCacheSize() const {
+        return ScoringParams::MAX_CACHE_DIC_NODE_SIZE;
+    }
+
+    AK_FORCE_INLINE bool isPossibleOmissionChildNode(
+            const DicTraverseSession *const traverseSession, const DicNode *const parentDicNode,
+            const DicNode *const dicNode) const {
+        const ProximityType proximityType =
+                getProximityType(traverseSession, parentDicNode, dicNode);
+        if (!DicNodeUtils::isProximityChar(proximityType)) {
+            return false;
+        }
+        return true;
+    }
+
+    AK_FORCE_INLINE bool isGoodToTraverseNextWord(const DicNode *const dicNode) const {
+        const int probability = dicNode->getProbability();
+        if (probability < ScoringParams::THRESHOLD_NEXT_WORD_PROBABILITY) {
+            return false;
+        }
+        const int c = dicNode->getOutputWordBuf()[0];
+        const bool shortCappedWord = dicNode->getDepth()
+                < ScoringParams::THRESHOLD_SHORT_WORD_LENGTH && isAsciiUpper(c);
+        return !shortCappedWord
+                || probability >= ScoringParams::THRESHOLD_NEXT_WORD_PROBABILITY_FOR_CAPPED;
+    }
+
+ private:
+    DISALLOW_COPY_AND_ASSIGN(TypingTraversal);
+    static const bool CORRECT_OMISSION;
+    static const bool CORRECT_SPACE_SUBSTITUTION;
+    static const bool CORRECT_SPACE_OMISSION;
+    static const TypingTraversal sInstance;
+
+    TypingTraversal() {}
+    ~TypingTraversal() {}
+};
+} // namespace latinime
+#endif // LATINIME_TYPING_TRAVERSAL_H
diff --git a/native/jni/src/suggest/typing_suggest.cpp b/native/jni/src/suggest/policyimpl/typing/typing_weighting.cpp
similarity index 73%
copy from native/jni/src/suggest/typing_suggest.cpp
copy to native/jni/src/suggest/policyimpl/typing/typing_weighting.cpp
index 56bd5b6..1500341 100644
--- a/native/jni/src/suggest/typing_suggest.cpp
+++ b/native/jni/src/suggest/policyimpl/typing/typing_weighting.cpp
@@ -14,12 +14,11 @@
  * limitations under the License.
  */
 
-#include "typing_suggest.h"
+#include "suggest/policyimpl/typing/typing_weighting.h"
+
+#include "suggest/core/dicnode/dic_node.h"
+#include "suggest/policyimpl/typing/scoring_params.h"
 
 namespace latinime {
-    SuggestInterface *(*TypingSuggest::sTypingSuggestFactoryMethod)() = 0;
-
-    TypingSuggest::~TypingSuggest() {
-        delete mSuggestInterface;
-    }
-} // namespace latinime
+const TypingWeighting TypingWeighting::sInstance;
+}  // namespace latinime
diff --git a/native/jni/src/suggest/policyimpl/typing/typing_weighting.h b/native/jni/src/suggest/policyimpl/typing/typing_weighting.h
new file mode 100644
index 0000000..52d54eb
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/typing/typing_weighting.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LATINIME_TYPING_WEIGHTING_H
+#define LATINIME_TYPING_WEIGHTING_H
+
+#include "defines.h"
+#include "suggest/core/dicnode/dic_node_utils.h"
+#include "suggest/core/policy/weighting.h"
+#include "suggest/core/session/dic_traverse_session.h"
+#include "suggest/policyimpl/typing/scoring_params.h"
+
+namespace latinime {
+
+class DicNode;
+struct DicNode_InputStateG;
+
+class TypingWeighting : public Weighting {
+ public:
+    static const TypingWeighting *getInstance() { return &sInstance; }
+
+ protected:
+    float getTerminalSpatialCost(
+            const DicTraverseSession *const traverseSession, const DicNode *const dicNode) const {
+        float cost = 0.0f;
+        if (dicNode->hasMultipleWords()) {
+            cost += ScoringParams::HAS_MULTI_WORD_TERMINAL_COST;
+        }
+        if (dicNode->getProximityCorrectionCount() > 0) {
+            cost += ScoringParams::HAS_PROXIMITY_TERMINAL_COST;
+        }
+        if (dicNode->getEditCorrectionCount() > 0) {
+            cost += ScoringParams::HAS_EDIT_CORRECTION_TERMINAL_COST;
+        }
+        return cost;
+    }
+
+    float getOmissionCost(const DicNode *const parentDicNode, const DicNode *const dicNode) const {
+        bool sameCodePoint = false;
+        bool isFirstLetterOmission = false;
+        float cost = 0.0f;
+        sameCodePoint = dicNode->isSameNodeCodePoint(parentDicNode);
+        // If the traversal omitted the first letter then the dicNode should now be on the second.
+        isFirstLetterOmission = dicNode->getDepth() == 2;
+        if (isFirstLetterOmission) {
+            cost = ScoringParams::OMISSION_COST_FIRST_CHAR;
+        } else {
+            cost = sameCodePoint ? ScoringParams::OMISSION_COST_SAME_CHAR
+                    : ScoringParams::OMISSION_COST;
+        }
+        return cost;
+    }
+
+    float getMatchedCost(
+            const DicTraverseSession *const traverseSession, const DicNode *const dicNode,
+            DicNode_InputStateG *inputStateG) const {
+        const int pointIndex = dicNode->getInputIndex(0);
+        // Note: min() required since length can be MAX_POINT_TO_KEY_LENGTH for characters not on
+        // the keyboard (like accented letters)
+        const float length = min(ScoringParams::MAX_SPATIAL_DISTANCE,
+                traverseSession->getProximityInfoState(0)->getPointToKeyLength(
+                        pointIndex, dicNode->getNodeCodePoint()));
+        const float weightedDistance = length * ScoringParams::DISTANCE_WEIGHT_LENGTH;
+        const bool isFirstChar = pointIndex == 0;
+        const bool isProximity = isProximityDicNode(traverseSession, dicNode);
+        const float cost = isProximity ? (isFirstChar ? ScoringParams::FIRST_PROXIMITY_COST
+                : ScoringParams::PROXIMITY_COST) : 0.0f;
+        return weightedDistance + cost;
+    }
+
+    bool isProximityDicNode(
+            const DicTraverseSession *const traverseSession, const DicNode *const dicNode) const {
+        const int pointIndex = dicNode->getInputIndex(0);
+        const int primaryCodePoint = toBaseLowerCase(
+                traverseSession->getProximityInfoState(0)->getPrimaryCodePointAt(pointIndex));
+        const int dicNodeChar = toBaseLowerCase(dicNode->getNodeCodePoint());
+        return primaryCodePoint != dicNodeChar;
+    }
+
+    float getTranspositionCost(
+            const DicTraverseSession *const traverseSession, const DicNode *const parentDicNode,
+            const DicNode *const dicNode) const {
+        const int16_t parentPointIndex = parentDicNode->getInputIndex(0);
+        const int prevCodePoint = parentDicNode->getNodeCodePoint();
+        const float distance1 = traverseSession->getProximityInfoState(0)->getPointToKeyLength(
+                parentPointIndex + 1, prevCodePoint);
+        const int codePoint = dicNode->getNodeCodePoint();
+        const float distance2 = traverseSession->getProximityInfoState(0)->getPointToKeyLength(
+                parentPointIndex, codePoint);
+        const float distance = distance1 + distance2;
+        const float weightedLengthDistance =
+                distance * ScoringParams::DISTANCE_WEIGHT_LENGTH;
+        return ScoringParams::TRANSPOSITION_COST + weightedLengthDistance;
+    }
+
+    float getInsertionCost(
+            const DicTraverseSession *const traverseSession,
+            const DicNode *const parentDicNode, const DicNode *const dicNode) const {
+        const int16_t parentPointIndex = parentDicNode->getInputIndex(0);
+        const int prevCodePoint =
+                traverseSession->getProximityInfoState(0)->getPrimaryCodePointAt(parentPointIndex);
+
+        const int currentCodePoint = dicNode->getNodeCodePoint();
+        const bool sameCodePoint = prevCodePoint == currentCodePoint;
+        const float dist = traverseSession->getProximityInfoState(0)->getPointToKeyLength(
+                parentPointIndex + 1, currentCodePoint);
+        const float weightedDistance = dist * ScoringParams::DISTANCE_WEIGHT_LENGTH;
+        const bool singleChar = dicNode->getDepth() == 1;
+        const float cost = (singleChar ? ScoringParams::INSERTION_COST_FIRST_CHAR : 0.0f)
+                + (sameCodePoint ? ScoringParams::INSERTION_COST_SAME_CHAR
+                        : ScoringParams::INSERTION_COST);
+        return cost + weightedDistance;
+    }
+
+    float getNewWordCost(const DicNode *const dicNode) const {
+        const bool isCapitalized = dicNode->isCapitalized();
+        return isCapitalized ?
+                ScoringParams::COST_NEW_WORD_CAPITALIZED : ScoringParams::COST_NEW_WORD;
+    }
+
+    float getNewWordBigramCost(
+            const DicTraverseSession *const traverseSession, const DicNode *const dicNode,
+            hash_map_compat<int, int16_t> *const bigramCacheMap) const {
+        return DicNodeUtils::getBigramNodeImprobability(traverseSession->getOffsetDict(),
+                dicNode, bigramCacheMap);
+    }
+
+    float getCompletionCost(const DicTraverseSession *const traverseSession,
+            const DicNode *const dicNode) const {
+        // The auto completion starts when the input index is same as the input size
+        const bool firstCompletion = dicNode->getInputIndex(0)
+                == traverseSession->getInputSize();
+        // TODO: Change the cost for the first completion for the gesture?
+        const float cost = firstCompletion ? ScoringParams::COST_FIRST_LOOKAHEAD
+                : ScoringParams::COST_LOOKAHEAD;
+        return cost;
+    }
+
+    float getTerminalLanguageCost(const DicTraverseSession *const traverseSession,
+            const DicNode *const dicNode, const float dicNodeLanguageImprobability) const {
+        const bool hasEditCount = dicNode->getEditCorrectionCount() > 0;
+        const bool isSameLength = dicNode->getDepth() == traverseSession->getInputSize();
+        const bool hasMultipleWords = dicNode->hasMultipleWords();
+        const bool hasProximityErrors = dicNode->getProximityCorrectionCount() > 0;
+        // Gesture input is always assumed to have proximity errors
+        // because the input word shouldn't be treated as perfect
+        const bool isExactMatch = !hasEditCount && !hasMultipleWords
+                && !hasProximityErrors && isSameLength;
+
+        const float totalPrevWordsLanguageCost = dicNode->getTotalPrevWordsLanguageCost();
+        const float languageImprobability = isExactMatch ? 0.0f : dicNodeLanguageImprobability;
+        const float languageWeight = ScoringParams::DISTANCE_WEIGHT_LANGUAGE;
+        // TODO: Caveat: The following equation should be:
+        // totalPrevWordsLanguageCost + (languageImprobability * languageWeight);
+        return (totalPrevWordsLanguageCost + languageImprobability) * languageWeight;
+    }
+
+    AK_FORCE_INLINE bool needsToNormalizeCompoundDistance() const {
+        return false;
+    }
+
+    AK_FORCE_INLINE float getAdditionalProximityCost() const {
+        return ScoringParams::ADDITIONAL_PROXIMITY_COST;
+    }
+
+    AK_FORCE_INLINE float getSubstitutionCost() const {
+        return ScoringParams::SUBSTITUTION_COST;
+    }
+
+    AK_FORCE_INLINE float getSpaceSubstitutionCost() const {
+        return ScoringParams::SPACE_SUBSTITUTION_COST;
+    }
+
+ private:
+    DISALLOW_COPY_AND_ASSIGN(TypingWeighting);
+    static const TypingWeighting sInstance;
+
+    TypingWeighting() {}
+    ~TypingWeighting() {}
+};
+} // namespace latinime
+#endif // LATINIME_TYPING_WEIGHTING_H
diff --git a/native/jni/src/suggest/typing_suggest.h b/native/jni/src/suggest/typing_suggest.h
deleted file mode 100644
index 678037a..0000000
--- a/native/jni/src/suggest/typing_suggest.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef LATINIME_TYPING_SUGGEST_H
-#define LATINIME_TYPING_SUGGEST_H
-
-#include "defines.h"
-#include "suggest_interface.h"
-
-namespace latinime {
-
-class ProximityInfo;
-
-class TypingSuggest : public SuggestInterface {
- public:
-    TypingSuggest() : mSuggestInterface(getTypingSuggestInstance()) {}
-
-    virtual ~TypingSuggest();
-
-    int getSuggestions(ProximityInfo *pInfo, void *traverseSession, int *inputXs, int *inputYs,
-            int *times, int *pointerIds, int *inputCodePoints, int inputSize, int commitPoint,
-            int *outWords, int *frequencies, int *outputIndices, int *outputTypes) const {
-        if (!mSuggestInterface) {
-            return 0;
-        }
-        return mSuggestInterface->getSuggestions(pInfo, traverseSession, inputXs, inputYs, times,
-                pointerIds, inputCodePoints, inputSize, commitPoint, outWords, frequencies,
-                outputIndices, outputTypes);
-    }
-
-    static void setTypingSuggestFactoryMethod(SuggestInterface *(*factoryMethod)()) {
-        sTypingSuggestFactoryMethod = factoryMethod;
-    }
-
- private:
-    DISALLOW_COPY_AND_ASSIGN(TypingSuggest);
-    static SuggestInterface *getTypingSuggestInstance() {
-        if (!sTypingSuggestFactoryMethod) {
-            return 0;
-        }
-        return sTypingSuggestFactoryMethod();
-    }
-
-    static SuggestInterface *(*sTypingSuggestFactoryMethod)();
-    SuggestInterface *mSuggestInterface;
-};
-} // namespace latinime
-#endif // LATINIME_TYPING_SUGGEST_H
diff --git a/native/jni/src/unigram_dictionary.cpp b/native/jni/src/unigram_dictionary.cpp
index 80ba412..33795ca 100644
--- a/native/jni/src/unigram_dictionary.cpp
+++ b/native/jni/src/unigram_dictionary.cpp
@@ -22,6 +22,7 @@
 #include "char_utils.h"
 #include "defines.h"
 #include "dictionary.h"
+#include "digraph_utils.h"
 #include "proximity_info.h"
 #include "terminal_attributes.h"
 #include "unigram_dictionary.h"
@@ -30,15 +31,6 @@
 
 namespace latinime {
 
-const UnigramDictionary::digraph_t UnigramDictionary::GERMAN_UMLAUT_DIGRAPHS[] =
-        { { 'a', 'e', 0x00E4 }, // U+00E4 : LATIN SMALL LETTER A WITH DIAERESIS
-        { 'o', 'e', 0x00F6 }, // U+00F6 : LATIN SMALL LETTER O WITH DIAERESIS
-        { 'u', 'e', 0x00FC } }; // U+00FC : LATIN SMALL LETTER U WITH DIAERESIS
-
-const UnigramDictionary::digraph_t UnigramDictionary::FRENCH_LIGATURES_DIGRAPHS[] =
-        { { 'a', 'e', 0x00E6 }, // U+00E6 : LATIN SMALL LETTER AE
-        { 'o', 'e', 0x0153 } }; // U+0153 : LATIN SMALL LIGATURE OE
-
 // TODO: check the header
 UnigramDictionary::UnigramDictionary(const uint8_t *const streamStart, const unsigned int flags)
         : DICT_ROOT(streamStart), ROOT_POS(0),
@@ -58,7 +50,7 @@
 
 // Return the replacement code point for a digraph, or 0 if none.
 int UnigramDictionary::getDigraphReplacement(const int *codes, const int i, const int inputSize,
-        const digraph_t *const digraphs, const unsigned int digraphsSize) const {
+        const DigraphUtils::digraph_t *const digraphs, const unsigned int digraphsSize) const {
 
     // There can't be a digraph if we don't have at least 2 characters to examine
     if (i + 2 > inputSize) return false;
@@ -74,7 +66,7 @@
 
     // It's an interesting digraph if the second char matches too.
     if (digraphs[lastDigraphIndex].second == codes[i + 1]) {
-        return digraphs[lastDigraphIndex].replacement;
+        return digraphs[lastDigraphIndex].compositeGlyph;
     } else {
         return 0;
     }
@@ -93,7 +85,7 @@
         const bool useFullEditDistance, const int *codesSrc,
         const int codesRemain, const int currentDepth, int *codesDest, Correction *correction,
         WordsPriorityQueuePool *queuePool,
-        const digraph_t *const digraphs, const unsigned int digraphsSize) const {
+        const DigraphUtils::digraph_t *const digraphs, const unsigned int digraphsSize) const {
     ASSERT(sizeof(codesDest[0]) == sizeof(codesSrc[0]));
     ASSERT(sizeof(xCoordinatesBuffer[0]) == sizeof(xcoordinates[0]));
     ASSERT(sizeof(yCoordinatesBuffer[0]) == sizeof(ycoordinates[0]));
@@ -169,7 +161,10 @@
     queuePool.clearAll();
     Correction masterCorrection;
     masterCorrection.resetCorrection();
-    if (BinaryFormat::REQUIRES_GERMAN_UMLAUT_PROCESSING & FLAGS)
+    const DigraphUtils::digraph_t *digraphs = 0;
+    const int digraphsSize =
+            DigraphUtils::getAllDigraphsForDictionaryAndReturnSize(FLAGS, &digraphs);
+    if (digraphsSize > 0)
     { // Incrementally tune the word and try all possibilities
         int codesBuffer[sizeof(*inputCodePoints) * inputSize];
         int xCoordinatesBuffer[inputSize];
@@ -177,15 +172,7 @@
         getWordWithDigraphSuggestionsRec(proximityInfo, xcoordinates, ycoordinates, codesBuffer,
                 xCoordinatesBuffer, yCoordinatesBuffer, inputSize, bigramMap, bigramFilter,
                 useFullEditDistance, inputCodePoints, inputSize, 0, codesBuffer, &masterCorrection,
-                &queuePool, GERMAN_UMLAUT_DIGRAPHS, NELEMS(GERMAN_UMLAUT_DIGRAPHS));
-    } else if (BinaryFormat::REQUIRES_FRENCH_LIGATURES_PROCESSING & FLAGS) {
-        int codesBuffer[sizeof(*inputCodePoints) * inputSize];
-        int xCoordinatesBuffer[inputSize];
-        int yCoordinatesBuffer[inputSize];
-        getWordWithDigraphSuggestionsRec(proximityInfo, xcoordinates, ycoordinates, codesBuffer,
-                xCoordinatesBuffer, yCoordinatesBuffer, inputSize, bigramMap, bigramFilter,
-                useFullEditDistance, inputCodePoints, inputSize, 0, codesBuffer, &masterCorrection,
-                &queuePool, FRENCH_LIGATURES_DIGRAPHS, NELEMS(FRENCH_LIGATURES_DIGRAPHS));
+                &queuePool, digraphs, digraphsSize);
     } else { // Normal processing
         getWordSuggestions(proximityInfo, xcoordinates, ycoordinates, inputCodePoints, inputSize,
                 bigramMap, bigramFilter, useFullEditDistance, &masterCorrection, &queuePool);
diff --git a/native/jni/src/unigram_dictionary.h b/native/jni/src/unigram_dictionary.h
index c1955e8..1a01758 100644
--- a/native/jni/src/unigram_dictionary.h
+++ b/native/jni/src/unigram_dictionary.h
@@ -20,6 +20,7 @@
 #include <map>
 #include <stdint.h>
 #include "defines.h"
+#include "digraph_utils.h"
 
 namespace latinime {
 
@@ -29,8 +30,6 @@
 class WordsPriorityQueuePool;
 
 class UnigramDictionary {
-    typedef struct { int first; int second; int replacement; } digraph_t;
-
  public:
     // Error tolerances
     static const int DEFAULT_MAX_ERRORS = 2;
@@ -57,13 +56,13 @@
             const bool useFullEditDistance, Correction *correction,
             WordsPriorityQueuePool *queuePool) const;
     int getDigraphReplacement(const int *codes, const int i, const int inputSize,
-            const digraph_t *const digraphs, const unsigned int digraphsSize) const;
+            const DigraphUtils::digraph_t *const digraphs, const unsigned int digraphsSize) const;
     void getWordWithDigraphSuggestionsRec(ProximityInfo *proximityInfo, const int *xcoordinates,
             const int *ycoordinates, const int *codesBuffer, int *xCoordinatesBuffer,
             int *yCoordinatesBuffer, const int codesBufferSize, const std::map<int, int> *bigramMap,
             const uint8_t *bigramFilter, const bool useFullEditDistance, const int *codesSrc,
             const int codesRemain, const int currentDepth, int *codesDest, Correction *correction,
-            WordsPriorityQueuePool *queuePool, const digraph_t *const digraphs,
+            WordsPriorityQueuePool *queuePool, const DigraphUtils::digraph_t *const digraphs,
             const unsigned int digraphsSize) const;
     void initSuggestions(ProximityInfo *proximityInfo, const int *xcoordinates,
             const int *ycoordinates, const int *codes, const int inputSize,
@@ -111,9 +110,6 @@
     const int ROOT_POS;
     const int MAX_DIGRAPH_SEARCH_DEPTH;
     const int FLAGS;
-
-    static const digraph_t GERMAN_UMLAUT_DIGRAPHS[];
-    static const digraph_t FRENCH_LIGATURES_DIGRAPHS[];
 };
 } // namespace latinime
 #endif // LATINIME_UNIGRAM_DICTIONARY_H
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/HermiteInterpolatorTests.java b/tests/src/com/android/inputmethod/keyboard/internal/HermiteInterpolatorTests.java
new file mode 100644
index 0000000..3ff5aa4
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/internal/HermiteInterpolatorTests.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.internal;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+@SmallTest
+public class HermiteInterpolatorTests extends AndroidTestCase {
+    private final HermiteInterpolator mInterpolator = new HermiteInterpolator();
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+    }
+
+    private static final float EPSLION = 0.0000005f;
+
+    private static void assertFloatEquals(final String message, float expected, float actual) {
+        if (Math.abs(expected - actual) >= EPSLION) {
+            fail(String.format("%s expected:<%s> but was:<%s>", message, expected, actual));
+        }
+    }
+
+    // t=0 p0=(0,1)
+    // t=1 p1=(1,0)
+    // t=2 p2=(3,2)
+    // t=3 p3=(2,3)
+    //   y
+    //   |
+    // 3 +       o p3
+    //   |
+    // 2 +           o p2
+    //   |
+    // 1 o p0
+    //   |    p1
+    // 0 +---o---+---+-- x
+    //   0   1   2   3
+    private final int[] mXCoords = { 0, 1, 3, 2 };
+    private final int[] mYCoords = { 1, 0, 2, 3 };
+    private static final int p0 = 0;
+    private static final int p1 = 1;
+    private static final int p2 = 2;
+    private static final int p3 = 3;
+
+    public void testP0P1() {
+        // [(p0 p1) p2 p3]
+        mInterpolator.reset(mXCoords, mYCoords, p0, p3 + 1);
+        mInterpolator.setInterval(p0 - 1, p0, p1, p1 + 1);
+        assertEquals("p0x", mXCoords[p0], mInterpolator.mP1X);
+        assertEquals("p0y", mYCoords[p0], mInterpolator.mP1Y);
+        assertEquals("p1x", mXCoords[p1], mInterpolator.mP2X);
+        assertEquals("p1y", mYCoords[p1], mInterpolator.mP2Y);
+        // XY-slope at p0=3.0 (-0.75/-0.25)
+        assertFloatEquals("slope x p0", -0.25f, mInterpolator.mSlope1X);
+        assertFloatEquals("slope y p0", -0.75f, mInterpolator.mSlope1Y);
+        // XY-slope at p1=1/3.0 (0.50/1.50)
+        assertFloatEquals("slope x p1",  1.50f, mInterpolator.mSlope2X);
+        assertFloatEquals("slope y p1",  0.50f, mInterpolator.mSlope2Y);
+        // t=0.0 (p0)
+        mInterpolator.interpolate(0.0f);
+        assertFloatEquals("t=0.0 x", 0.0f, mInterpolator.mInterpolatedX);
+        assertFloatEquals("t=0.0 y", 1.0f, mInterpolator.mInterpolatedY);
+        // t=0.2
+        mInterpolator.interpolate(0.2f);
+        assertFloatEquals("t=0.2 x", 0.02400f, mInterpolator.mInterpolatedX);
+        assertFloatEquals("t=0.2 y", 0.78400f, mInterpolator.mInterpolatedY);
+        // t=0.5
+        mInterpolator.interpolate(0.5f);
+        assertFloatEquals("t=0.5 x", 0.28125f, mInterpolator.mInterpolatedX);
+        assertFloatEquals("t=0.5 y", 0.34375f, mInterpolator.mInterpolatedY);
+        // t=0.8
+        mInterpolator.interpolate(0.8f);
+        assertFloatEquals("t=0.8 x", 0.69600f, mInterpolator.mInterpolatedX);
+        assertFloatEquals("t=0.8 y", 0.01600f, mInterpolator.mInterpolatedY);
+        // t=1.0 (p1)
+        mInterpolator.interpolate(1.0f);
+        assertFloatEquals("t=1.0 x", 1.0f, mInterpolator.mInterpolatedX);
+        assertFloatEquals("t=1.0 y", 0.0f, mInterpolator.mInterpolatedY);
+    }
+
+    public void testP1P2() {
+        // [p0 (p1 p2) p3]
+        mInterpolator.reset(mXCoords, mYCoords, p0, p3 + 1);
+        mInterpolator.setInterval(p1 - 1, p1, p2, p2 + 1);
+        assertEquals("p1x", mXCoords[p1], mInterpolator.mP1X);
+        assertEquals("p1y", mYCoords[p1], mInterpolator.mP1Y);
+        assertEquals("p2x", mXCoords[p2], mInterpolator.mP2X);
+        assertEquals("p2y", mYCoords[p2], mInterpolator.mP2Y);
+        // XY-slope at p1=1/3.0 (0.50/1.50)
+        assertFloatEquals("slope x p1",  1.50f, mInterpolator.mSlope1X);
+        assertFloatEquals("slope y p1",  0.50f, mInterpolator.mSlope1Y);
+        // XY-slope at p2=3.0 (1.50/0.50)
+        assertFloatEquals("slope x p2",  0.50f, mInterpolator.mSlope2X);
+        assertFloatEquals("slope y p2",  1.50f, mInterpolator.mSlope2Y);
+        // t=0.0 (p1)
+        mInterpolator.interpolate(0.0f);
+        assertFloatEquals("t=0.0 x", 1.0f, mInterpolator.mInterpolatedX);
+        assertFloatEquals("t=0.0 y", 0.0f, mInterpolator.mInterpolatedY);
+        // t=0.2
+        mInterpolator.interpolate(0.2f);
+        assertFloatEquals("t=0.2 x", 1.384f, mInterpolator.mInterpolatedX);
+        assertFloatEquals("t=0.2 y", 0.224f, mInterpolator.mInterpolatedY);
+        // t=0.5
+        mInterpolator.interpolate(0.5f);
+        assertFloatEquals("t=0.5 x", 2.125f, mInterpolator.mInterpolatedX);
+        assertFloatEquals("t=0.5 y", 0.875f, mInterpolator.mInterpolatedY);
+        // t=0.8
+        mInterpolator.interpolate(0.8f);
+        assertFloatEquals("t=0.8 x", 2.776f, mInterpolator.mInterpolatedX);
+        assertFloatEquals("t=0.8 y", 1.616f, mInterpolator.mInterpolatedY);
+        // t=1.0 (p2)
+        mInterpolator.interpolate(1.0f);
+        assertFloatEquals("t=1.0 x", 3.0f, mInterpolator.mInterpolatedX);
+        assertFloatEquals("t=1.0 y", 2.0f, mInterpolator.mInterpolatedY);
+    }
+
+    public void testP2P3() {
+        // [p0 p1 (p2 p3)]
+        mInterpolator.reset(mXCoords, mYCoords, p0, p3 + 1);
+        mInterpolator.setInterval(p2 - 1, p2, p3, p3 + 1);
+        assertEquals("p2x", mXCoords[p2], mInterpolator.mP1X);
+        assertEquals("p2y", mYCoords[p2], mInterpolator.mP1Y);
+        assertEquals("p3x", mXCoords[p3], mInterpolator.mP2X);
+        assertEquals("p3y", mYCoords[p3], mInterpolator.mP2Y);
+        // XY-slope at p2=3.0 (1.50/0.50)
+        assertFloatEquals("slope x p2",  0.50f, mInterpolator.mSlope1X);
+        assertFloatEquals("slope y p2",  1.50f, mInterpolator.mSlope1Y);
+        // XY-slope at p3=1/3.0 (-0.25/-0.75)
+        assertFloatEquals("slope x p3", -0.75f, mInterpolator.mSlope2X);
+        assertFloatEquals("slope y p3", -0.25f, mInterpolator.mSlope2Y);
+        // t=0.0 (p2)
+        mInterpolator.interpolate(0.0f);
+        assertFloatEquals("t=0.0 x", 3.0f, mInterpolator.mInterpolatedX);
+        assertFloatEquals("t=0.0 y", 2.0f, mInterpolator.mInterpolatedY);
+        // t=0.2
+        mInterpolator.interpolate(0.2f);
+        assertFloatEquals("t=0.2 x", 2.98400f, mInterpolator.mInterpolatedX);
+        assertFloatEquals("t=0.2 y", 2.30400f, mInterpolator.mInterpolatedY);
+        // t=0.5
+        mInterpolator.interpolate(0.5f);
+        assertFloatEquals("t=0.5 x", 2.65625f, mInterpolator.mInterpolatedX);
+        assertFloatEquals("t=0.5 y", 2.71875f, mInterpolator.mInterpolatedY);
+        // t=0.8
+        mInterpolator.interpolate(0.8f);
+        assertFloatEquals("t=0.8 x", 2.21600f, mInterpolator.mInterpolatedX);
+        assertFloatEquals("t=0.8 y", 2.97600f, mInterpolator.mInterpolatedY);
+        // t=1.0 (p3)
+        mInterpolator.interpolate(1.0f);
+        assertFloatEquals("t=1.0 x", 2.0f, mInterpolator.mInterpolatedX);
+        assertFloatEquals("t=1.0 y", 3.0f, mInterpolator.mInterpolatedY);
+    }
+
+    public void testJustP1P2() {
+        // [(p1 p2)]
+        mInterpolator.reset(mXCoords, mYCoords, p1, p2 + 1);
+        mInterpolator.setInterval(p1 - 1, p1, p2, p2 + 1);
+        assertEquals("p1x", mXCoords[p1], mInterpolator.mP1X);
+        assertEquals("p1y", mYCoords[p1], mInterpolator.mP1Y);
+        assertEquals("p2x", mXCoords[p2], mInterpolator.mP2X);
+        assertEquals("p2y", mYCoords[p2], mInterpolator.mP2Y);
+        // XY-slope at p1=1.0 (2.0/2.0)
+        assertFloatEquals("slope x p1", 2.00f, mInterpolator.mSlope1X);
+        assertFloatEquals("slope y p1", 2.00f, mInterpolator.mSlope1Y);
+        // XY-slope at p2=1.0 (2.0/2.0)
+        assertFloatEquals("slope x p2", 2.00f, mInterpolator.mSlope2X);
+        assertFloatEquals("slope y p2", 2.00f, mInterpolator.mSlope2Y);
+        // t=0.0 (p1)
+        mInterpolator.interpolate(0.0f);
+        assertFloatEquals("t=0.0 x", 1.0f, mInterpolator.mInterpolatedX);
+        assertFloatEquals("t=0.0 y", 0.0f, mInterpolator.mInterpolatedY);
+        // t=0.2
+        mInterpolator.interpolate(0.2f);
+        assertFloatEquals("t=0.2 x", 1.4f, mInterpolator.mInterpolatedX);
+        assertFloatEquals("t=0.2 y", 0.4f, mInterpolator.mInterpolatedY);
+        // t=0.5
+        mInterpolator.interpolate(0.5f);
+        assertFloatEquals("t=0.5 x", 2.0f, mInterpolator.mInterpolatedX);
+        assertFloatEquals("t=0.5 y", 1.0f, mInterpolator.mInterpolatedY);
+        // t=0.8
+        mInterpolator.interpolate(0.8f);
+        assertFloatEquals("t=0.8 x", 2.6f, mInterpolator.mInterpolatedX);
+        assertFloatEquals("t=0.8 y", 1.6f, mInterpolator.mInterpolatedY);
+        // t=1.0 (p2)
+        mInterpolator.interpolate(1.0f);
+        assertFloatEquals("t=1.0 x", 3.0f, mInterpolator.mInterpolatedX);
+        assertFloatEquals("t=1.0 y", 2.0f, mInterpolator.mInterpolatedY);
+    }
+}
diff --git a/tests/src/com/android/inputmethod/latin/InputTestsBase.java b/tests/src/com/android/inputmethod/latin/InputTestsBase.java
index 4ccbf48..04e1f93 100644
--- a/tests/src/com/android/inputmethod/latin/InputTestsBase.java
+++ b/tests/src/com/android/inputmethod/latin/InputTestsBase.java
@@ -130,7 +130,9 @@
     protected void setUp() throws Exception {
         super.setUp();
         mTextView = new MyTextView(getContext());
-        mTextView.setInputType(InputType.TYPE_CLASS_TEXT);
+        final int inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT
+                | InputType.TYPE_TEXT_FLAG_MULTI_LINE;
+        mTextView.setInputType(inputType);
         mTextView.setEnabled(true);
         setupService();
         mLatinIME = getService();
@@ -138,9 +140,7 @@
         mLatinIME.onCreate();
         setDebugMode(previousDebugSetting);
         final EditorInfo ei = new EditorInfo();
-        ei.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT;
         final InputConnection ic = mTextView.onCreateInputConnection(ei);
-        ei.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT;
         final LayoutInflater inflater =
                 (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
         final ViewGroup vg = new FrameLayout(getContext());
@@ -181,17 +181,21 @@
         // a message that calls it instead of calling it directly.
         Looper.loop();
 
-        // Once #quit() has been called, the message queue has an "mQuiting" field that prevents
-        // any subsequent post in this queue. However the queue itself is still fully functional!
-        // If we have a way of resetting "queue.mQuiting" then we can continue using it as normal,
-        // coming back to this method to run the messages.
+        // Once #quit() has been called, the looper is not functional any more (it used to be,
+        // but now it SIGSEGV's if it's used again).
+        // It won't accept creating a new looper for this thread and switching to it...
+        // ...unless we can trick it into throwing out the old looper and believing it hasn't
+        // been initialized before.
         MessageQueue queue = Looper.myQueue();
         try {
-            // However there is no way of doing it externally, and mQuiting is private.
+            // However there is no way of doing it externally, and the static ThreadLocal
+            // field into which it's stored is private.
             // So... get out the big guns.
-            java.lang.reflect.Field f = MessageQueue.class.getDeclaredField("mQuiting");
-            f.setAccessible(true); // What do you mean "private"?
-            f.setBoolean(queue, false);
+            java.lang.reflect.Field f = Looper.class.getDeclaredField("sThreadLocal");
+            f.setAccessible(true); // private lolwut
+            final ThreadLocal<Looper> a = (ThreadLocal<Looper>) f.get(looper);
+            a.set(null);
+            looper.prepare();
         } catch (NoSuchFieldException e) {
             throw new RuntimeException(e);
         } catch (IllegalAccessException e) {