Merge "Make gestures work correctly on keyboards with accented keys" into jb-mr1-dev
diff --git a/java/res/values-be/strings.xml b/java/res/values-be/strings.xml
index 976dccb..0d2de7a 100644
--- a/java/res/values-be/strings.xml
+++ b/java/res/values-be/strings.xml
@@ -36,10 +36,8 @@
     <string name="advanced_settings_summary" msgid="4487980456152830271">"Функцыi для спецыялістаў"</string>
     <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Перакл. да інш. спос. ув."</string>
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Кнопка пераключэння мовы звязана i з iншымi спосабамi ўводу"</string>
-    <!-- no translation found for show_language_switch_key (5915478828318774384) -->
-    <skip />
-    <!-- no translation found for show_language_switch_key_summary (7343403647474265713) -->
-    <skip />
+    <string name="show_language_switch_key" msgid="5915478828318774384">"Кнопка пераключэння мовы"</string>
+    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Паказваць, калі ўключана некалькі моў ўводу"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Затрым. скр. падк. клав."</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Няма затрымкі"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Па змаўчанні"</string>
diff --git a/java/res/values-ca/strings.xml b/java/res/values-ca/strings.xml
index 4e66af5..af4fae6 100644
--- a/java/res/values-ca/strings.xml
+++ b/java/res/values-ca/strings.xml
@@ -36,10 +36,8 @@
     <string name="advanced_settings_summary" msgid="4487980456152830271">"Opcions per a experts"</string>
     <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Canvia mètode d\'entrada"</string>
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"La tecla de canvi d\'idioma també cobreix altres mètodes d\'entrada"</string>
-    <!-- no translation found for show_language_switch_key (5915478828318774384) -->
-    <skip />
-    <!-- no translation found for show_language_switch_key_summary (7343403647474265713) -->
-    <skip />
+    <string name="show_language_switch_key" msgid="5915478828318774384">"Tecla de canvi d\'idioma"</string>
+    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Mostra-la quan hi hagi diversos idiomes d\'entrada activats"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Retard d\'om. em. de tecla"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Sense retard"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predeterminat"</string>
diff --git a/java/res/values-da/strings.xml b/java/res/values-da/strings.xml
index 968df5a..da974a4 100644
--- a/java/res/values-da/strings.xml
+++ b/java/res/values-da/strings.xml
@@ -36,10 +36,8 @@
     <string name="advanced_settings_summary" msgid="4487980456152830271">"Muligheder for eksperter"</string>
     <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Skift inputmetode"</string>
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Tasten til sprogskift gælder også for andre inputmetoder"</string>
-    <!-- no translation found for show_language_switch_key (5915478828318774384) -->
-    <skip />
-    <!-- no translation found for show_language_switch_key_summary (7343403647474265713) -->
-    <skip />
+    <string name="show_language_switch_key" msgid="5915478828318774384">"Tast til sprogskift"</string>
+    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Vis, når der er aktiveret flere inputsprog"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Forsink. afvis. af taste-pop op"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Ingen forsink."</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Standard"</string>
diff --git a/java/res/values-de/strings.xml b/java/res/values-de/strings.xml
index c948ce6..51a1c7e 100644
--- a/java/res/values-de/strings.xml
+++ b/java/res/values-de/strings.xml
@@ -25,7 +25,7 @@
     <string name="english_ime_research_log" msgid="8492602295696577851">"Forschungsprotokollbefehle"</string>
     <string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android-Rechtschreibprüfung (AOSP)"</string>
     <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Kontaktnamen prüfen"</string>
-    <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Rechtschreibprüfung verwendet Einträge aus Ihrer Kontaktliste."</string>
+    <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Rechtschreibprüfung kann Einträge aus meiner Kontaktliste verwenden"</string>
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"Bei Tastendruck vibrieren"</string>
     <string name="sound_on_keypress" msgid="6093592297198243644">"Ton bei Tastendruck"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Pop-up bei Tastendruck"</string>
@@ -36,16 +36,14 @@
     <string name="advanced_settings_summary" msgid="4487980456152830271">"Optionen für Experten"</string>
     <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Eingabemethoden wechseln"</string>
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Sprachwechseltaste umfasst auch andere Eingabemethoden."</string>
-    <!-- no translation found for show_language_switch_key (5915478828318774384) -->
-    <skip />
-    <!-- no translation found for show_language_switch_key_summary (7343403647474265713) -->
-    <skip />
+    <string name="show_language_switch_key" msgid="5915478828318774384">"Sprachwechsel"</string>
+    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Anzeigen, wenn mehrere Eingabesprachen aktiviert sind"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Tasten-Pop-up"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Keine Verzögerung"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Standard"</string>
     <string name="use_contacts_dict" msgid="4435317977804180815">"Kontakte vorschlagen"</string>
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Namen aus \"Kontakte\" als Vorschläge und Korrekturmöglichkeiten anzeigen"</string>
-    <string name="auto_cap" msgid="1719746674854628252">"Autom. Groß-/Kleinschr."</string>
+    <string name="auto_cap" msgid="1719746674854628252">"Autom. Groß-/Kleinschreibung"</string>
     <string name="configure_dictionaries_title" msgid="4238652338556902049">"Erweiterte Wörterbücher"</string>
     <string name="main_dictionary" msgid="4798763781818361168">"Allgemeines Wörterbuch"</string>
     <string name="prefs_show_suggestions" msgid="8026799663445531637">"Änderungsvorschläge"</string>
@@ -143,7 +141,7 @@
     <string name="enable" msgid="5031294444630523247">"Aktivieren"</string>
     <string name="not_now" msgid="6172462888202790482">"Später"</string>
     <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Der gleiche Eingabestil ist bereits vorhanden: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string>
-    <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Modus der Studie zur Benutzerfreundlichkeit"</string>
-    <string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Einstellungen für Vibrationsdauer bei Tastendruck"</string>
-    <string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Einstellungen für Tonlautstärke bei Tastendruck"</string>
+    <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Studie zur Benutzerfreundlichkeit"</string>
+    <string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Vibrationsdauer bei Tastendruck"</string>
+    <string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Tonlautstärke bei Tastendruck"</string>
 </resources>
diff --git a/java/res/values-en-rGB/strings.xml b/java/res/values-en-rGB/strings.xml
index 08b6bc0..801db82 100644
--- a/java/res/values-en-rGB/strings.xml
+++ b/java/res/values-en-rGB/strings.xml
@@ -36,10 +36,8 @@
     <string name="advanced_settings_summary" msgid="4487980456152830271">"Options for experts"</string>
     <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Switch to other input methods"</string>
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Language switch key also covers other input methods"</string>
-    <!-- no translation found for show_language_switch_key (5915478828318774384) -->
-    <skip />
-    <!-- no translation found for show_language_switch_key_summary (7343403647474265713) -->
-    <skip />
+    <string name="show_language_switch_key" msgid="5915478828318774384">"Language switch key"</string>
+    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Show when multiple input languages are enabled"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Key pop-up dismiss delay"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"No delay"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Default"</string>
diff --git a/java/res/values-es-rUS/strings.xml b/java/res/values-es-rUS/strings.xml
index cdd6592..8a5a6f6 100644
--- a/java/res/values-es-rUS/strings.xml
+++ b/java/res/values-es-rUS/strings.xml
@@ -36,10 +36,8 @@
     <string name="advanced_settings_summary" msgid="4487980456152830271">"Opciones para expertos"</string>
     <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Otros métodos de entrada"</string>
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"La tecla de cambio de idioma abarca otros métodos de entrada."</string>
-    <!-- no translation found for show_language_switch_key (5915478828318774384) -->
-    <skip />
-    <!-- no translation found for show_language_switch_key_summary (7343403647474265713) -->
-    <skip />
+    <string name="show_language_switch_key" msgid="5915478828318774384">"Tecla de selección de idioma"</string>
+    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Mostrar cuando se habiliten varios idiomas de entrada"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Retraso en rechazo de alerta de tecla"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Sin demora"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predeterminada"</string>
diff --git a/java/res/values-es/strings.xml b/java/res/values-es/strings.xml
index d441ad9..a61a57e 100644
--- a/java/res/values-es/strings.xml
+++ b/java/res/values-es/strings.xml
@@ -36,10 +36,8 @@
     <string name="advanced_settings_summary" msgid="4487980456152830271">"Opciones para expertos"</string>
     <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Otros métodos de entrada"</string>
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"La tecla de cambio de idioma sirve también para otros métodos"</string>
-    <!-- no translation found for show_language_switch_key (5915478828318774384) -->
-    <skip />
-    <!-- no translation found for show_language_switch_key_summary (7343403647474265713) -->
-    <skip />
+    <string name="show_language_switch_key" msgid="5915478828318774384">"Tecla para cambiar de idioma"</string>
+    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Mostrar cuando haya varios idiomas de entrada habilitados"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Retraso al ampliar tecla"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Sin retraso"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predeterminado"</string>
diff --git a/java/res/values-et/strings.xml b/java/res/values-et/strings.xml
index dfedf5e..9886f44 100644
--- a/java/res/values-et/strings.xml
+++ b/java/res/values-et/strings.xml
@@ -36,10 +36,8 @@
     <string name="advanced_settings_summary" msgid="4487980456152830271">"Valikud ekspertidele"</string>
     <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Vaheta sisestusmeetodit"</string>
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Keelevahetuse võti hõlmab ka muid sisestusmeetodeid"</string>
-    <!-- no translation found for show_language_switch_key (5915478828318774384) -->
-    <skip />
-    <!-- no translation found for show_language_switch_key_summary (7343403647474265713) -->
-    <skip />
+    <string name="show_language_switch_key" msgid="5915478828318774384">"Keelevahetuse nupp"</string>
+    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Kuva, kui lubatud on mitu sisendkeelt"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Hüpiku loobumisviivitus"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Viivituseta"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Vaikeseade"</string>
diff --git a/java/res/values-fa/strings.xml b/java/res/values-fa/strings.xml
index b8504f7..3e9526c 100644
--- a/java/res/values-fa/strings.xml
+++ b/java/res/values-fa/strings.xml
@@ -36,10 +36,8 @@
     <string name="advanced_settings_summary" msgid="4487980456152830271">"گزینه‌هایی برای حرفه‌ای‌ها"</string>
     <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"تغییر به دیگر روش‌های ورودی"</string>
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"کلید تغییر زبان، سایر ورودی‌های زبان را نیز پوشش می‌دهد"</string>
-    <!-- no translation found for show_language_switch_key (5915478828318774384) -->
-    <skip />
-    <!-- no translation found for show_language_switch_key_summary (7343403647474265713) -->
-    <skip />
+    <string name="show_language_switch_key" msgid="5915478828318774384">"کلید تغییر زبان"</string>
+    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"وقتی چند زبان ورودی فعال است نشان داده شود"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"تأخیر در رد کردن کلید نمایشی"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"بدون تأخیر"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"پیش‌فرض"</string>
diff --git a/java/res/values-fr/strings.xml b/java/res/values-fr/strings.xml
index b680c57..0a8c382 100644
--- a/java/res/values-fr/strings.xml
+++ b/java/res/values-fr/strings.xml
@@ -36,10 +36,8 @@
     <string name="advanced_settings_summary" msgid="4487980456152830271">"Options destinées aux experts"</string>
     <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Autres modes de saisie"</string>
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"La touche de sélection de langue couvre d\'autres modes de saisie."</string>
-    <!-- no translation found for show_language_switch_key (5915478828318774384) -->
-    <skip />
-    <!-- no translation found for show_language_switch_key_summary (7343403647474265713) -->
-    <skip />
+    <string name="show_language_switch_key" msgid="5915478828318774384">"Touche de sélection de langue"</string>
+    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Afficher lorsque plusieurs langues de saisie sont activées"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Masquer touche agrandie"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Sans délai"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Par défaut"</string>
diff --git a/java/res/values-hi/strings.xml b/java/res/values-hi/strings.xml
index 8a0f25d..2788baf 100644
--- a/java/res/values-hi/strings.xml
+++ b/java/res/values-hi/strings.xml
@@ -36,10 +36,8 @@
     <string name="advanced_settings_summary" msgid="4487980456152830271">"विशेषज्ञों के लिए विकल्‍प"</string>
     <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"अन्‍य इनपुट पद्धतियों पर जाएं"</string>
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"भाषा स्‍विच कुंजी में अन्‍य इनपुट पद्धतियां भी शामिल हैं"</string>
-    <!-- no translation found for show_language_switch_key (5915478828318774384) -->
-    <skip />
-    <!-- no translation found for show_language_switch_key_summary (7343403647474265713) -->
-    <skip />
+    <string name="show_language_switch_key" msgid="5915478828318774384">"भाषा स्विच कुंजी"</string>
+    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"एकाधिक इनपुट भाषाएं सक्षम होने पर दिखाएं"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"कुंजी पॉपअप खारिज़ विलंब"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"कोई विलंब नहीं"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"डिफ़ॉल्ट"</string>
diff --git a/java/res/values-hr/strings.xml b/java/res/values-hr/strings.xml
index f92d23e..017301b 100644
--- a/java/res/values-hr/strings.xml
+++ b/java/res/values-hr/strings.xml
@@ -36,10 +36,8 @@
     <string name="advanced_settings_summary" msgid="4487980456152830271">"Opcije za stručnjake"</string>
     <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Prebaci na druge unose"</string>
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Tipka za prebacivanje jezika pokriva i druge načine unosa"</string>
-    <!-- no translation found for show_language_switch_key (5915478828318774384) -->
-    <skip />
-    <!-- no translation found for show_language_switch_key_summary (7343403647474265713) -->
-    <skip />
+    <string name="show_language_switch_key" msgid="5915478828318774384">"Tipka za izmjenjivanje jezika"</string>
+    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Prikaži kada je omogućen unos na više jezika"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Odgoda prikaza tipki"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Bez odgode"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Zadano"</string>
diff --git a/java/res/values-hu/strings.xml b/java/res/values-hu/strings.xml
index 2073e79..c7efb69 100644
--- a/java/res/values-hu/strings.xml
+++ b/java/res/values-hu/strings.xml
@@ -36,10 +36,8 @@
     <string name="advanced_settings_summary" msgid="4487980456152830271">"Beállítások gyakorlott felhasználóknak"</string>
     <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Váltás más beviteli módra"</string>
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"A nyelvkapcsoló gomb egyéb beviteli módokat is tartalmaz"</string>
-    <!-- no translation found for show_language_switch_key (5915478828318774384) -->
-    <skip />
-    <!-- no translation found for show_language_switch_key_summary (7343403647474265713) -->
-    <skip />
+    <string name="show_language_switch_key" msgid="5915478828318774384">"A nyelvkapcsoló"</string>
+    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Megjelenítés, ha több beviteli nyelv engedélyezett"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Gombeltüntetés késése"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Nincs késés"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Alapbeállítás"</string>
diff --git a/java/res/values-in/strings.xml b/java/res/values-in/strings.xml
index 8b4cd6e..827cbed 100644
--- a/java/res/values-in/strings.xml
+++ b/java/res/values-in/strings.xml
@@ -36,10 +36,8 @@
     <string name="advanced_settings_summary" msgid="4487980456152830271">"Opsi untuk ahli"</string>
     <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Beralih ke metode masukan lain"</string>
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Tombol beralih bahasa juga mencakup metode masukan lain"</string>
-    <!-- no translation found for show_language_switch_key (5915478828318774384) -->
-    <skip />
-    <!-- no translation found for show_language_switch_key_summary (7343403647474265713) -->
-    <skip />
+    <string name="show_language_switch_key" msgid="5915478828318774384">"Tombol pengalih bahasa"</string>
+    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Tampilkan saat beberapa bahasa masukan diaktifkan"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Tundaan singkir munculan kunci"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Tanpa penundaan"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Default"</string>
diff --git a/java/res/values-ko/strings.xml b/java/res/values-ko/strings.xml
index 4ef392d..d6d46dd 100644
--- a/java/res/values-ko/strings.xml
+++ b/java/res/values-ko/strings.xml
@@ -36,10 +36,8 @@
     <string name="advanced_settings_summary" msgid="4487980456152830271">"전문가용 옵션"</string>
     <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"다른 입력 방법으로 전환"</string>
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"언어 전환 키가 제공하는 기타 입력 방법"</string>
-    <!-- no translation found for show_language_switch_key (5915478828318774384) -->
-    <skip />
-    <!-- no translation found for show_language_switch_key_summary (7343403647474265713) -->
-    <skip />
+    <string name="show_language_switch_key" msgid="5915478828318774384">"언어 전환 키"</string>
+    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"여러 입력 언어를 사용하도록 설정할 때 표시"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"키 팝업 해제 지연"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"지연 없음"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"기본값"</string>
diff --git a/java/res/values-lt/strings.xml b/java/res/values-lt/strings.xml
index cb70d49..8858009 100644
--- a/java/res/values-lt/strings.xml
+++ b/java/res/values-lt/strings.xml
@@ -36,10 +36,8 @@
     <string name="advanced_settings_summary" msgid="4487980456152830271">"Parinktys ekspertams"</string>
     <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Perj. į kt. įvesties būd."</string>
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Kalbos perjungimo klavišu taip pat perjungiami įvesties būdai"</string>
-    <!-- no translation found for show_language_switch_key (5915478828318774384) -->
-    <skip />
-    <!-- no translation found for show_language_switch_key_summary (7343403647474265713) -->
-    <skip />
+    <string name="show_language_switch_key" msgid="5915478828318774384">"Kalbos keitimo klavišas"</string>
+    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Rodyti, kai įgalintos kelios įvesties kalbos"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Pagr. išš. l. atsis. d."</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Be delsos"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Numatytasis"</string>
diff --git a/java/res/values-lv/strings.xml b/java/res/values-lv/strings.xml
index f33b776..4827cf1 100644
--- a/java/res/values-lv/strings.xml
+++ b/java/res/values-lv/strings.xml
@@ -36,10 +36,8 @@
     <string name="advanced_settings_summary" msgid="4487980456152830271">"Opcijas ekspertiem"</string>
     <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Pārsl. uz citām iev. met."</string>
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Valodas pārslēgš. taustiņu var lietot arī citām ievades metodēm."</string>
-    <!-- no translation found for show_language_switch_key (5915478828318774384) -->
-    <skip />
-    <!-- no translation found for show_language_switch_key_summary (7343403647474265713) -->
-    <skip />
+    <string name="show_language_switch_key" msgid="5915478828318774384">"Valodas pārslēgšanas taustiņš"</string>
+    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Parādīt, ja ir iespējotas vairākas ievades valodas"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Taust. uzn. loga noraid. aizk."</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Bez aizkaves"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Noklusējums"</string>
diff --git a/java/res/values-nb/strings.xml b/java/res/values-nb/strings.xml
index cbde87f..5bc2b7e 100644
--- a/java/res/values-nb/strings.xml
+++ b/java/res/values-nb/strings.xml
@@ -36,10 +36,8 @@
     <string name="advanced_settings_summary" msgid="4487980456152830271">"Alternativer for eksperter"</string>
     <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Bytt inndatametode"</string>
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Tasten for språkbytte dekker også andre inndatametoder"</string>
-    <!-- no translation found for show_language_switch_key (5915478828318774384) -->
-    <skip />
-    <!-- no translation found for show_language_switch_key_summary (7343403647474265713) -->
-    <skip />
+    <string name="show_language_switch_key" msgid="5915478828318774384">"Nøkkel for språkskifte"</string>
+    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Vis når flere inndataspråk er aktivert"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Tregt tastevindu"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"U/ forsinkelse"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Standard"</string>
diff --git a/java/res/values-nl/strings.xml b/java/res/values-nl/strings.xml
index d8a582d..ede45b6 100644
--- a/java/res/values-nl/strings.xml
+++ b/java/res/values-nl/strings.xml
@@ -36,10 +36,8 @@
     <string name="advanced_settings_summary" msgid="4487980456152830271">"Opties voor experts"</string>
     <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Invoermeth. overschakelen"</string>
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Schakelknop voor taal ook van toepassing op andere invoermethoden"</string>
-    <!-- no translation found for show_language_switch_key (5915478828318774384) -->
-    <skip />
-    <!-- no translation found for show_language_switch_key_summary (7343403647474265713) -->
-    <skip />
+    <string name="show_language_switch_key" msgid="5915478828318774384">"Schakelknop voor taal"</string>
+    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Weergeven wanneer meerdere invoertalen zijn geselecteerd"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Afwijz.vertr. toetspop-up"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Geen vertraging"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Standaard"</string>
diff --git a/java/res/values-pl/strings.xml b/java/res/values-pl/strings.xml
index e54a787..a3a0ff1 100644
--- a/java/res/values-pl/strings.xml
+++ b/java/res/values-pl/strings.xml
@@ -36,10 +36,8 @@
     <string name="advanced_settings_summary" msgid="4487980456152830271">"Opcje dla ekspertów"</string>
     <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Włącz inne metody wprowadzania"</string>
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Klawisz zmiany języka obejmuje też inne metody wprowadzania"</string>
-    <!-- no translation found for show_language_switch_key (5915478828318774384) -->
-    <skip />
-    <!-- no translation found for show_language_switch_key_summary (7343403647474265713) -->
-    <skip />
+    <string name="show_language_switch_key" msgid="5915478828318774384">"Klawisz zmiany języka"</string>
+    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Pokaż, gdy włączonych jest kilka języków wprowadzania"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Opóźnienie znikania klawiszy"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Bez opóźnienia"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Wartość domyślna"</string>
diff --git a/java/res/values-pt-rPT/strings.xml b/java/res/values-pt-rPT/strings.xml
index d761614..e16a074 100644
--- a/java/res/values-pt-rPT/strings.xml
+++ b/java/res/values-pt-rPT/strings.xml
@@ -36,10 +36,8 @@
     <string name="advanced_settings_summary" msgid="4487980456152830271">"Opções para especialistas"</string>
     <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Mudar p/ outros mét. ent."</string>
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"A tecla de mudança de idioma abrange outros métodos de entrada"</string>
-    <!-- no translation found for show_language_switch_key (5915478828318774384) -->
-    <skip />
-    <!-- no translation found for show_language_switch_key_summary (7343403647474265713) -->
-    <skip />
+    <string name="show_language_switch_key" msgid="5915478828318774384">"Tecla alterar idioma"</string>
+    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Mostrar quando estão ativados vários idiomas de entrada"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Atraso p/ ignorar pop-up"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Sem atraso"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predefinido"</string>
diff --git a/java/res/values-ru/strings.xml b/java/res/values-ru/strings.xml
index 5f1953c..fba7176 100644
--- a/java/res/values-ru/strings.xml
+++ b/java/res/values-ru/strings.xml
@@ -36,10 +36,8 @@
     <string name="advanced_settings_summary" msgid="4487980456152830271">"Для опытных пользователей"</string>
     <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Другой способ ввода"</string>
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Клавиша переключения языков также служит для смены способа ввода"</string>
-    <!-- no translation found for show_language_switch_key (5915478828318774384) -->
-    <skip />
-    <!-- no translation found for show_language_switch_key_summary (7343403647474265713) -->
-    <skip />
+    <string name="show_language_switch_key" msgid="5915478828318774384">"Клавиша переключения языков"</string>
+    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Показывать, когда включено несколько раскладок"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Задержка закрытия"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Без задержки"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"По умолчанию"</string>
diff --git a/java/res/values-sl/strings.xml b/java/res/values-sl/strings.xml
index 9c1a9e6..db1fdc6 100644
--- a/java/res/values-sl/strings.xml
+++ b/java/res/values-sl/strings.xml
@@ -36,10 +36,8 @@
     <string name="advanced_settings_summary" msgid="4487980456152830271">"Možnosti za strokovnjake"</string>
     <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Prekl. na drug nač. vnosa"</string>
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Tipka za preklop jezika, ki vključuje tudi druge načine vnosa"</string>
-    <!-- no translation found for show_language_switch_key (5915478828318774384) -->
-    <skip />
-    <!-- no translation found for show_language_switch_key_summary (7343403647474265713) -->
-    <skip />
+    <string name="show_language_switch_key" msgid="5915478828318774384">"Tipka za preklop med jeziki"</string>
+    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Pokaži, ko je omogočenih več jezikov vnosa"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Trajanje povečanja tipke"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Brez zakasnitve"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Privzeto"</string>
diff --git a/java/res/values-sr/strings.xml b/java/res/values-sr/strings.xml
index af20852..3425b0f 100644
--- a/java/res/values-sr/strings.xml
+++ b/java/res/values-sr/strings.xml
@@ -36,10 +36,8 @@
     <string name="advanced_settings_summary" msgid="4487980456152830271">"Опције за стручњаке"</string>
     <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Пребаци на друге методе уноса"</string>
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Тастер за пребацивање језика обухвата и друге методе уноса"</string>
-    <!-- no translation found for show_language_switch_key (5915478828318774384) -->
-    <skip />
-    <!-- no translation found for show_language_switch_key_summary (7343403647474265713) -->
-    <skip />
+    <string name="show_language_switch_key" msgid="5915478828318774384">"Тастер за пребацивање језика"</string>
+    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Прикажи када је омогућено више језика уноса"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Одложи одбац. иск. прозора тастера"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Без одлагања"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Подразумевано"</string>
diff --git a/java/res/values-th/strings.xml b/java/res/values-th/strings.xml
index 49124e2..492dc54 100644
--- a/java/res/values-th/strings.xml
+++ b/java/res/values-th/strings.xml
@@ -36,10 +36,8 @@
     <string name="advanced_settings_summary" msgid="4487980456152830271">"ตัวเลือกสำหรับผู้เชี่ยวชาญ"</string>
     <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"ใช้วิธีการป้อนข้อมูลอื่น"</string>
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"แป้นสลับภาษาครอบคลุมวิธีการป้อนข้อมูลอื่นๆ ด้วย"</string>
-    <!-- no translation found for show_language_switch_key (5915478828318774384) -->
-    <skip />
-    <!-- no translation found for show_language_switch_key_summary (7343403647474265713) -->
-    <skip />
+    <string name="show_language_switch_key" msgid="5915478828318774384">"แป้นสลับภาษา"</string>
+    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"แสดงเมื่อเปิดใช้งานภาษาสำำหรับอินพุตหลายภาษา"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"หน่วงเวลาก่อนปิดป๊อปอัพหลัก"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"ไม่มีการหน่วงเวลา"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"ค่าเริ่มต้น"</string>
diff --git a/java/res/values-tl/strings.xml b/java/res/values-tl/strings.xml
index dae846c..7d2a0e8 100644
--- a/java/res/values-tl/strings.xml
+++ b/java/res/values-tl/strings.xml
@@ -36,10 +36,8 @@
     <string name="advanced_settings_summary" msgid="4487980456152830271">"Mga pagpipilian para sa mga dalubhasa"</string>
     <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Lipat iba paraan ng input"</string>
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Saklaw din ng key ng pagpalit ng wika ang ibang paraan ng input"</string>
-    <!-- no translation found for show_language_switch_key (5915478828318774384) -->
-    <skip />
-    <!-- no translation found for show_language_switch_key_summary (7343403647474265713) -->
-    <skip />
+    <string name="show_language_switch_key" msgid="5915478828318774384">"Key ng panlipat ng wika"</string>
+    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Ipakita kapag maraming wika ng input na pinagana"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Balewala antala key popup"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Walang antala"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Default"</string>
diff --git a/java/res/values-uk/strings.xml b/java/res/values-uk/strings.xml
index a3b1b3f..89608d8 100644
--- a/java/res/values-uk/strings.xml
+++ b/java/res/values-uk/strings.xml
@@ -36,10 +36,8 @@
     <string name="advanced_settings_summary" msgid="4487980456152830271">"Налаштування для досвідчених користувачів"</string>
     <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Інші методи введення"</string>
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Клавіша зміни мови дозволяє змінювати методи введення"</string>
-    <!-- no translation found for show_language_switch_key (5915478828318774384) -->
-    <skip />
-    <!-- no translation found for show_language_switch_key_summary (7343403647474265713) -->
-    <skip />
+    <string name="show_language_switch_key" msgid="5915478828318774384">"Клавіша зміни мови"</string>
+    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Показувати, коли ввімкнено декілька мов введення"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Затримка клавіши закриття"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Без затримки"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"За умовчанням"</string>
diff --git a/java/res/values-vi/strings.xml b/java/res/values-vi/strings.xml
index 6b32c78..efd85a4 100644
--- a/java/res/values-vi/strings.xml
+++ b/java/res/values-vi/strings.xml
@@ -36,10 +36,8 @@
     <string name="advanced_settings_summary" msgid="4487980456152830271">"Tùy chọn dành cho chuyên gia"</string>
     <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Phương thức nhập khác"</string>
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Khóa chuyển ngôn ngữ bao gồm cả các phương thức nhập liệu khác"</string>
-    <!-- no translation found for show_language_switch_key (5915478828318774384) -->
-    <skip />
-    <!-- no translation found for show_language_switch_key_summary (7343403647474265713) -->
-    <skip />
+    <string name="show_language_switch_key" msgid="5915478828318774384">"Phím chuyển đổi ngôn ngữ"</string>
+    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Hiển thị khi nhiều ngôn ngữ đầu vào được bật"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Loại bỏ hiển thị phím trễ"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Không có tgian trễ"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Mặc định"</string>
diff --git a/java/res/values-zh-rCN/strings.xml b/java/res/values-zh-rCN/strings.xml
index f111e4f..dde471c 100644
--- a/java/res/values-zh-rCN/strings.xml
+++ b/java/res/values-zh-rCN/strings.xml
@@ -36,10 +36,8 @@
     <string name="advanced_settings_summary" msgid="4487980456152830271">"高级选项"</string>
     <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"切换到其他输入法"</string>
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"语言切换键也可用于切换其他输入法"</string>
-    <!-- no translation found for show_language_switch_key (5915478828318774384) -->
-    <skip />
-    <!-- no translation found for show_language_switch_key_summary (7343403647474265713) -->
-    <skip />
+    <string name="show_language_switch_key" msgid="5915478828318774384">"语言切换键"</string>
+    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"启用多种输入语言时显示"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"弹出字符隐藏延迟"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"无延迟"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"默认"</string>
diff --git a/java/res/values-zh-rTW/strings.xml b/java/res/values-zh-rTW/strings.xml
index 20edc02..f54b4cc 100644
--- a/java/res/values-zh-rTW/strings.xml
+++ b/java/res/values-zh-rTW/strings.xml
@@ -36,10 +36,8 @@
     <string name="advanced_settings_summary" msgid="4487980456152830271">"進階選項"</string>
     <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"切換至其他輸入法"</string>
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"使語言切換鍵包含其他輸入法"</string>
-    <!-- no translation found for show_language_switch_key (5915478828318774384) -->
-    <skip />
-    <!-- no translation found for show_language_switch_key_summary (7343403647474265713) -->
-    <skip />
+    <string name="show_language_switch_key" msgid="5915478828318774384">"語言切換鍵"</string>
+    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"可使用多種輸入語言時顯示"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"關閉彈出式鍵盤的延遲時間"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"不延遲"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"預設"</string>
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index 392a070..13aca71 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -49,6 +49,9 @@
     <!-- Category title for text prediction -->
     <string name="correction_category">Text correction</string>
 
+    <!-- Category title for gesture typing -->
+    <string name="gesture_typing_category">Gesture typing</string>
+
     <!-- Category title for misc options  -->
     <string name="misc_category">Other options</string>
 
@@ -80,6 +83,8 @@
 
     <!-- Option to enable auto capitalization of sentences -->
     <string name="auto_cap">Auto-capitalization</string>
+    <!-- Description for option to enable auto capitalization of sentences -->
+    <string name="auto_cap_summary">Capitalize the first word of each sentence</string>
 
     <!-- Option to configure dictionaries -->
     <string name="configure_dictionaries_title">Add-on dictionaries</string>
@@ -91,12 +96,12 @@
     <!-- Description for show suggestions -->
     <string name="prefs_show_suggestions_summary">Display suggested words while typing</string>
     <string name="prefs_suggestion_visibility_show_name">Always show</string>
-    <string name="prefs_suggestion_visibility_show_only_portrait_name">Show on portrait mode</string>
+    <string name="prefs_suggestion_visibility_show_only_portrait_name">Show in portrait mode</string>
     <string name="prefs_suggestion_visibility_hide_name">Always hide</string>
 
     <!-- Option to decide the auto correction threshold score -->
     <!-- Option to enable auto correction [CHAR LIMIT=20]-->
-    <string name="auto_correction">Auto correction</string>
+    <string name="auto_correction">Auto-correction</string>
     <!-- Description for auto correction [CHAR LIMIT=65 (two lines) or 30 (fits on one line, preferable)] -->
     <string name="auto_correction_summary">Spacebar and punctuation automatically correct mistyped words</string>
     <!-- Option to disable auto correction. [CHAR LIMIT=20] -->
@@ -109,20 +114,20 @@
     <string name="auto_correction_threshold_mode_very_aggeressive">Very aggressive</string>
 
     <!-- Option to enable using next word suggestions. After the user types a space, with this option on, the keyboard will try to predict the next word. -->
-    <string name="bigram_prediction">Next word suggestions</string>
+    <string name="bigram_prediction">Next-word suggestions</string>
     <!-- Description for "next word suggestion" option. This displays suggestions even when there is no input, based on the previous word. -->
-    <string name="bigram_prediction_summary">Based on previous word</string>
+    <string name="bigram_prediction_summary">Use the previous word in making suggestions</string>
 
     <!-- Option to enable gesture input. The user can input a word by tracing the letters of a word without releasing the finger from the screen. [CHAR LIMIT=30]-->
-    <string name="gesture_input">Gesture input</string>
+    <string name="gesture_input">Enable gesture typing</string>
     <!-- Description for "gesture_input" option. The user can input a word by tracing the letters of a word without releasing the finger from the screen. [CHAR LIMIT=65]-->
-    <string name="gesture_input_summary">Input a word by tracing the letters of a word</string>
+    <string name="gesture_input_summary">Input a word by sliding through the letters</string>
     <!-- Option to enable gesture trail preview. The user can see a trail of the gesture during gesture input. [CHAR LIMIT=30]-->
     <string name="gesture_preview_trail">Show gesture trail</string>
     <!-- Option to enable gesture floating text preview. The user can see a suggested word floating under the moving finger during a gesture input. [CHAR LIMIT=30]-->
-    <string name="gesture_floating_preview_text">Show gesture word</string>
+    <string name="gesture_floating_preview_text">Dynamic floating preview</string>
     <!-- Description for "gesture_floating_preview_text" option. The user can see a suggested word floating under the moving finger during a gesture input. [CHAR LIMIT=65]-->
-    <string name="gesture_floating_preview_text_summary">Show floating preview word with gesture</string>
+    <string name="gesture_floating_preview_text_summary">See the suggested word while gesturing</string>
 
     <!-- Indicates that a word has been added to the dictionary -->
     <string name="added_word"><xliff:g id="word">%s</xliff:g> : Saved</string>
diff --git a/java/res/xml/prefs.xml b/java/res/xml/prefs.xml
index 478dc0f..fe82b32 100644
--- a/java/res/xml/prefs.xml
+++ b/java/res/xml/prefs.xml
@@ -24,6 +24,7 @@
         <CheckBoxPreference
             android:key="auto_cap"
             android:title="@string/auto_cap"
+            android:summary="@string/auto_cap_summary"
             android:persistent="true"
             android:defaultValue="true" />
         <CheckBoxPreference
@@ -78,6 +79,27 @@
             android:defaultValue="@string/prefs_suggestion_visibility_default_value" />
     </PreferenceCategory>
     <PreferenceCategory
+        android:title="@string/gesture_typing_category"
+        android:key="gesture_typing_settings">
+        <CheckBoxPreference
+            android:key="gesture_input"
+            android:title="@string/gesture_input"
+            android:summary="@string/gesture_input_summary"
+            android:persistent="true"
+            android:defaultValue="true" />
+        <CheckBoxPreference
+            android:key="pref_show_gesture_floating_preview_text"
+            android:title="@string/gesture_floating_preview_text"
+            android:summary="@string/gesture_floating_preview_text_summary"
+            android:persistent="true"
+            android:defaultValue="false" />
+        <CheckBoxPreference
+            android:key="pref_gesture_preview_trail"
+            android:title="@string/gesture_preview_trail"
+            android:persistent="true"
+            android:defaultValue="true" />
+    </PreferenceCategory>
+    <PreferenceCategory
         android:title="@string/misc_category"
         android:key="misc_settings">
         <CheckBoxPreference
@@ -86,12 +108,6 @@
             android:summary="@string/bigram_prediction_summary"
             android:persistent="true"
             android:defaultValue="true" />
-        <CheckBoxPreference
-            android:key="gesture_input"
-            android:title="@string/gesture_input"
-            android:summary="@string/gesture_input_summary"
-            android:persistent="true"
-            android:defaultValue="true" />
         <PreferenceScreen
             android:key="pref_advanced_settings"
             android:title="@string/advanced_settings"
@@ -128,17 +144,6 @@
             <PreferenceScreen
                 android:key="pref_keypress_sound_volume"
                 android:title="@string/prefs_keypress_sound_volume_settings" />
-            <CheckBoxPreference
-                android:key="pref_gesture_preview_trail"
-                android:title="@string/gesture_preview_trail"
-                android:persistent="true"
-                android:defaultValue="true" />
-            <CheckBoxPreference
-                android:key="pref_show_gesture_floating_preview_text"
-                android:title="@string/gesture_floating_preview_text"
-                android:summary="@string/gesture_floating_preview_text_summary"
-                android:persistent="true"
-                android:defaultValue="false" />
         </PreferenceScreen>
         <PreferenceScreen
             android:key="debug_settings"
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index cf89567..a9856e1 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -781,9 +781,6 @@
 
     public void cancelAllMessages() {
         mDrawingHandler.cancelAllMessages();
-        if (mPreviewPlacerView != null) {
-            mPreviewPlacerView.cancelAllMessages();
-        }
     }
 
     private TextView getKeyPreviewText(final int pointerId) {
@@ -829,7 +826,8 @@
         }
         final int[] viewOrigin = new int[2];
         getLocationInWindow(viewOrigin);
-        mPreviewPlacerView.setOrigin(viewOrigin[0], viewOrigin[1]);
+        mPreviewPlacerView.setKeyboardViewGeometry(
+                viewOrigin[0], viewOrigin[1], getWidth(), getHeight());
         final View rootView = getRootView();
         if (rootView == null) {
             Log.w(TAG, "Cannot find root view");
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index d6c567e..ec8f659 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -560,10 +560,13 @@
         return (sPointerTrackerQueue == null) ? 1 : sPointerTrackerQueue.size();
     }
 
-    private void mayStartBatchInput() {
+    private void mayStartBatchInput(final Key key) {
         if (sInGesture || !mGestureStrokeWithPreviewPoints.isStartOfAGesture()) {
             return;
         }
+        if (key == null || !Character.isLetter(key.mCode)) {
+            return;
+        }
         if (DEBUG_LISTENER) {
             Log.d(TAG, "onStartBatchInput");
         }
@@ -573,18 +576,20 @@
         mDrawingProxy.showGesturePreviewTrail(this, isOldestTracker);
     }
 
-    private void updateBatchInput(final long eventTime) {
-        synchronized (sAggregratedPointers) {
-            mGestureStrokeWithPreviewPoints.appendIncrementalBatchPoints(sAggregratedPointers);
-            final int size = sAggregratedPointers.getPointerSize();
-            if (size > sLastRecognitionPointSize
-                    && GestureStroke.hasRecognitionTimePast(eventTime, sLastRecognitionTime)) {
-                sLastRecognitionPointSize = size;
-                sLastRecognitionTime = eventTime;
-                if (DEBUG_LISTENER) {
-                    Log.d(TAG, "onUpdateBatchInput: batchPoints=" + size);
+    private void mayUpdateBatchInput(final long eventTime, final Key key) {
+        if (key != null) {
+            synchronized (sAggregratedPointers) {
+                mGestureStrokeWithPreviewPoints.appendIncrementalBatchPoints(sAggregratedPointers);
+                final int size = sAggregratedPointers.getPointerSize();
+                if (size > sLastRecognitionPointSize
+                        && GestureStroke.hasRecognitionTimePast(eventTime, sLastRecognitionTime)) {
+                    sLastRecognitionPointSize = size;
+                    sLastRecognitionTime = eventTime;
+                    if (DEBUG_LISTENER) {
+                        Log.d(TAG, "onUpdateBatchInput: batchPoints=" + size);
+                    }
+                    mListener.onUpdateBatchInput(sAggregratedPointers);
                 }
-                mListener.onUpdateBatchInput(sAggregratedPointers);
             }
         }
         final boolean isOldestTracker = sPointerTrackerQueue.getOldestElement() == this;
@@ -742,9 +747,9 @@
         final int gestureTime = (int)(eventTime - sGestureFirstDownTime);
         if (mIsDetectingGesture) {
             mGestureStrokeWithPreviewPoints.addPoint(x, y, gestureTime, isMajorEvent);
-            mayStartBatchInput();
-            if (sInGesture && key != null) {
-                updateBatchInput(eventTime);
+            mayStartBatchInput(key);
+            if (sInGesture) {
+                mayUpdateBatchInput(eventTime, key);
             }
         }
     }
diff --git a/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java b/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java
index 15170e0..72be7fc 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java
@@ -40,6 +40,10 @@
 import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
 
 public class PreviewPlacerView extends RelativeLayout {
+    // The height of extra area above the keyboard to draw gesture trails.
+    // Proportional to the keyboard height.
+    private static final float EXTRA_GESTURE_TRAIL_AREA_ABOVE_KEYBOARD_RATIO = 0.25f;
+
     private final int mGestureFloatingPreviewTextColor;
     private final int mGestureFloatingPreviewTextOffset;
     private final int mGestureFloatingPreviewColor;
@@ -47,14 +51,17 @@
     private final float mGestureFloatingPreviewVerticalPadding;
     private final float mGestureFloatingPreviewRoundRadius;
 
-    private int mXOrigin;
-    private int mYOrigin;
+    private int mKeyboardViewOriginX;
+    private int mKeyboardViewOriginY;
 
     private final SparseArray<GesturePreviewTrail> mGesturePreviewTrails =
             CollectionUtils.newSparseArray();
     private final Params mGesturePreviewTrailParams;
     private final Paint mGesturePaint;
     private boolean mDrawsGesturePreviewTrail;
+    private int mOffscreenWidth;
+    private int mOffscreenHeight;
+    private int mOffscreenOffsetY;
     private Bitmap mOffscreenBuffer;
     private final Canvas mOffscreenCanvas = new Canvas();
     private final Rect mOffscreenDirtyRect = new Rect();
@@ -101,29 +108,17 @@
             }
         }
 
-        private void cancelDismissGestureFloatingPreviewText() {
-            removeMessages(MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT);
-        }
-
         public void dismissGestureFloatingPreviewText() {
-            cancelDismissGestureFloatingPreviewText();
+            removeMessages(MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT);
             sendMessageDelayed(obtainMessage(MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT),
                     mGestureFloatingPreviewTextLingerTimeout);
         }
 
-        private void cancelUpdateGestureTrailPreview() {
-            removeMessages(MSG_UPDATE_GESTURE_PREVIEW_TRAIL);
-        }
-
         public void postUpdateGestureTrailPreview() {
-            cancelUpdateGestureTrailPreview();
+            removeMessages(MSG_UPDATE_GESTURE_PREVIEW_TRAIL);
             sendMessageDelayed(obtainMessage(MSG_UPDATE_GESTURE_PREVIEW_TRAIL),
                     mGesturePreviewTrailParams.mUpdateInterval);
         }
-
-        public void cancelAllMessages() {
-            cancelUpdateGestureTrailPreview();
-        }
     }
 
     public PreviewPlacerView(final Context context, final AttributeSet attrs) {
@@ -177,9 +172,12 @@
         setLayerType(LAYER_TYPE_HARDWARE, layerPaint);
     }
 
-    public void setOrigin(final int x, final int y) {
-        mXOrigin = x;
-        mYOrigin = y;
+    public void setKeyboardViewGeometry(final int x, final int y, final int w, final int h) {
+        mKeyboardViewOriginX = x;
+        mKeyboardViewOriginY = y;
+        mOffscreenOffsetY = (int)(h * EXTRA_GESTURE_TRAIL_AREA_ABOVE_KEYBOARD_RATIO);
+        mOffscreenWidth = w;
+        mOffscreenHeight = mOffscreenOffsetY + h;
     }
 
     public void setGesturePreviewMode(final boolean drawsGesturePreviewTrail,
@@ -216,45 +214,42 @@
 
     @Override
     protected void onDetachedFromWindow() {
+        freeOffscreenBuffer();
+    }
+
+    private void freeOffscreenBuffer() {
         if (mOffscreenBuffer != null) {
             mOffscreenBuffer.recycle();
             mOffscreenBuffer = null;
         }
     }
 
+    private void mayAllocateOffscreenBuffer() {
+        if (mOffscreenBuffer != null && mOffscreenBuffer.getWidth() == mOffscreenWidth
+                && mOffscreenBuffer.getHeight() == mOffscreenHeight) {
+            return;
+        }
+        freeOffscreenBuffer();
+        mOffscreenBuffer = Bitmap.createBitmap(
+                mOffscreenWidth, mOffscreenHeight, Bitmap.Config.ARGB_8888);
+        mOffscreenCanvas.setBitmap(mOffscreenBuffer);
+    }
+
     @Override
     public void onDraw(final Canvas canvas) {
         super.onDraw(canvas);
-        canvas.translate(mXOrigin, mYOrigin);
         if (mDrawsGesturePreviewTrail) {
-            if (mOffscreenBuffer == null) {
-                mOffscreenBuffer = Bitmap.createBitmap(
-                        getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
-                mOffscreenCanvas.setBitmap(mOffscreenBuffer);
-            }
+            mayAllocateOffscreenBuffer();
+            // Draw gesture trails to offscreen buffer.
+            final boolean needsUpdatingGesturePreviewTrail = drawGestureTrails(
+                    mOffscreenCanvas, mGesturePaint, mOffscreenDirtyRect);
+            // Transfer offscreen buffer to screen.
             if (!mOffscreenDirtyRect.isEmpty()) {
-                // Clear previous dirty rectangle.
-                mGesturePaint.setColor(Color.TRANSPARENT);
-                mGesturePaint.setStyle(Paint.Style.FILL);
-                mOffscreenCanvas.drawRect(mOffscreenDirtyRect, mGesturePaint);
-                mOffscreenDirtyRect.setEmpty();
-            }
-            boolean needsUpdatingGesturePreviewTrail = false;
-            synchronized (mGesturePreviewTrails) {
-                // Trails count == fingers count that have ever been active.
-                final int trailsCount = mGesturePreviewTrails.size();
-                for (int index = 0; index < trailsCount; index++) {
-                    final GesturePreviewTrail trail = mGesturePreviewTrails.valueAt(index);
-                    needsUpdatingGesturePreviewTrail |=
-                            trail.drawGestureTrail(mOffscreenCanvas, mGesturePaint,
-                                    mGesturePreviewTrailBoundsRect, mGesturePreviewTrailParams);
-                    // {@link #mGesturePreviewTrailBoundsRect} has bounding box of the trail.
-                    mOffscreenDirtyRect.union(mGesturePreviewTrailBoundsRect);
-                }
-            }
-            if (!mOffscreenDirtyRect.isEmpty()) {
+                final int offsetY = mKeyboardViewOriginY - mOffscreenOffsetY;
+                canvas.translate(mKeyboardViewOriginX, offsetY);
                 canvas.drawBitmap(mOffscreenBuffer, mOffscreenDirtyRect, mOffscreenDirtyRect,
                         mGesturePaint);
+                canvas.translate(-mKeyboardViewOriginX, -offsetY);
                 // Note: Defer clearing the dirty rectangle here because we will get cleared
                 // rectangle on the canvas.
             }
@@ -263,9 +258,49 @@
             }
         }
         if (mDrawsGestureFloatingPreviewText) {
+            canvas.translate(mKeyboardViewOriginX, mKeyboardViewOriginY);
             drawGestureFloatingPreviewText(canvas, mGestureFloatingPreviewText);
+            canvas.translate(-mKeyboardViewOriginX, -mKeyboardViewOriginY);
         }
-        canvas.translate(-mXOrigin, -mYOrigin);
+    }
+
+    private boolean drawGestureTrails(final Canvas offscreenCanvas, final Paint paint,
+            final Rect dirtyRect) {
+        // Clear previous dirty rectangle.
+        if (!dirtyRect.isEmpty()) {
+            paint.setColor(Color.TRANSPARENT);
+            paint.setStyle(Paint.Style.FILL);
+            offscreenCanvas.drawRect(dirtyRect, paint);
+        }
+        dirtyRect.setEmpty();
+
+        // Draw gesture trails to offscreen buffer.
+        offscreenCanvas.translate(0, mOffscreenOffsetY);
+        boolean needsUpdatingGesturePreviewTrail = false;
+        synchronized (mGesturePreviewTrails) {
+            // Trails count == fingers count that have ever been active.
+            final int trailsCount = mGesturePreviewTrails.size();
+            for (int index = 0; index < trailsCount; index++) {
+                final GesturePreviewTrail trail = mGesturePreviewTrails.valueAt(index);
+                needsUpdatingGesturePreviewTrail |=
+                        trail.drawGestureTrail(offscreenCanvas, paint,
+                                mGesturePreviewTrailBoundsRect, mGesturePreviewTrailParams);
+                // {@link #mGesturePreviewTrailBoundsRect} has bounding box of the trail.
+                dirtyRect.union(mGesturePreviewTrailBoundsRect);
+            }
+        }
+        offscreenCanvas.translate(0, -mOffscreenOffsetY);
+
+        // Clip dirty rectangle with offscreen buffer width/height.
+        dirtyRect.offset(0, mOffscreenOffsetY);
+        clipRect(dirtyRect, 0, 0, mOffscreenWidth, mOffscreenHeight);
+        return needsUpdatingGesturePreviewTrail;
+    }
+
+    private static void clipRect(final Rect out, final int left, final int top, final int right,
+            final int bottom) {
+        out.set(Math.max(out.left, left), Math.max(out.top, top), Math.min(out.right, right),
+                Math.min(out.bottom, bottom));
     }
 
     public void setGestureFloatingPreviewText(final String gestureFloatingPreviewText) {
@@ -278,10 +313,6 @@
         mDrawingHandler.dismissGestureFloatingPreviewText();
     }
 
-    public void cancelAllMessages() {
-        mDrawingHandler.cancelAllMessages();
-    }
-
     private void drawGestureFloatingPreviewText(final Canvas canvas,
             final String gestureFloatingPreviewText) {
         if (TextUtils.isEmpty(gestureFloatingPreviewText)) {
diff --git a/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java b/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java
index 05255a6..519165d 100644
--- a/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java
+++ b/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java
@@ -115,11 +115,10 @@
     public static void writeDictionaryBinary(final OutputStream destination,
             final BigramDictionaryInterface dict, final UserHistoryDictionaryBigramList bigrams,
             final FormatOptions formatOptions) {
-
         final FusionDictionary fusionDict = constructFusionDictionary(dict, bigrams);
-
         try {
             BinaryDictInputOutput.writeDictionaryBinary(destination, fusionDict, formatOptions);
+            Log.d(TAG, "end writing");
         } catch (IOException e) {
             Log.e(TAG, "IO exception while writing file: " + e);
         } catch (UnsupportedFormatException e) {
@@ -132,16 +131,18 @@
      */
     /* packages for test */ static FusionDictionary constructFusionDictionary(
             final BigramDictionaryInterface dict, final UserHistoryDictionaryBigramList bigrams) {
-
         final FusionDictionary fusionDict = new FusionDictionary(new Node(),
-                new FusionDictionary.DictionaryOptions(
-                        new HashMap<String,String>(), false, false));
-
+                new FusionDictionary.DictionaryOptions(new HashMap<String, String>(), false,
+                        false));
+        int profTotal = 0;
         for (final String word1 : bigrams.keySet()) {
             final HashMap<String, Byte> word1Bigrams = bigrams.getBigrams(word1);
             for (final String word2 : word1Bigrams.keySet()) {
                 final int freq = dict.getFrequency(word1, word2);
-
+                if (freq == -1) {
+                    // don't add this bigram.
+                    continue;
+                }
                 if (DEBUG) {
                     if (word1 == null) {
                         Log.d(TAG, "add unigram: " + word2 + "," + Integer.toString(freq));
@@ -149,17 +150,22 @@
                         Log.d(TAG, "add bigram: " + word1
                                 + "," + word2 + "," + Integer.toString(freq));
                     }
+                    profTotal++;
                 }
-
                 if (word1 == null) { // unigram
                     fusionDict.add(word2, freq, null, false /* isNotAWord */);
                 } else { // bigram
+                    if (FusionDictionary.findWordInTree(fusionDict.mRoot, word1) == null) {
+                        fusionDict.add(word1, 2, null, false /* isNotAWord */);
+                    }
                     fusionDict.setBigram(word1, word2, freq);
                 }
                 bigrams.updateBigram(word1, word2, (byte)freq);
             }
         }
-
+        if (DEBUG) {
+            Log.d(TAG, "add " + profTotal + "words");
+        }
         return fusionDict;
     }
 
@@ -171,7 +177,6 @@
         final Map<Integer, String> unigrams = CollectionUtils.newTreeMap();
         final Map<Integer, Integer> frequencies = CollectionUtils.newTreeMap();
         final Map<Integer, ArrayList<PendingAttribute>> bigrams = CollectionUtils.newTreeMap();
-
         try {
             BinaryDictIOUtils.readUnigramsAndBigramsBinary(buffer, unigrams, frequencies,
                     bigrams);
@@ -189,14 +194,11 @@
     /* package for test */ static void addWordsFromWordMap(final Map<Integer, String> unigrams,
             final Map<Integer, Integer> frequencies,
             final Map<Integer, ArrayList<PendingAttribute>> bigrams, final OnAddWordListener to) {
-
         for (Map.Entry<Integer, String> entry : unigrams.entrySet()) {
             final String word1 = entry.getValue();
             final int unigramFrequency = frequencies.get(entry.getKey());
             to.setUnigram(word1, null, unigramFrequency);
-
             final ArrayList<PendingAttribute> attrList = bigrams.get(entry.getKey());
-
             if (attrList != null) {
                 for (final PendingAttribute attr : attrList) {
                     to.setBigram(word1, unigrams.get(attr.mAddress),
diff --git a/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java b/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java
index e03af64..202aa64 100644
--- a/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java
@@ -16,24 +16,25 @@
 
 package com.android.inputmethod.latin;
 
-import android.content.ContentValues;
 import android.content.Context;
 import android.content.SharedPreferences;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteOpenHelper;
-import android.database.sqlite.SQLiteQueryBuilder;
 import android.os.AsyncTask;
-import android.provider.BaseColumns;
 import android.util.Log;
 
 import com.android.inputmethod.keyboard.ProximityInfo;
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import com.android.inputmethod.latin.UserHistoryDictIOUtils.BigramDictionaryInterface;
+import com.android.inputmethod.latin.UserHistoryDictIOUtils.OnAddWordListener;
 import com.android.inputmethod.latin.UserHistoryForgettingCurveUtils.ForgettingCurveParams;
+import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
 
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
 import java.lang.ref.SoftReference;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.locks.ReentrantLock;
 
@@ -43,45 +44,27 @@
  */
 public class UserHistoryDictionary extends ExpandableDictionary {
     private static final String TAG = UserHistoryDictionary.class.getSimpleName();
+    private static final String NAME = UserHistoryDictionary.class.getSimpleName();
     public static final boolean DBG_SAVE_RESTORE = false;
     public static final boolean DBG_STRESS_TEST = false;
     public static final boolean DBG_ALWAYS_WRITE = false;
     public static final boolean PROFILE_SAVE_RESTORE = LatinImeLogger.sDBG;
 
+    private static final FormatOptions VERSION3 = new FormatOptions(3,
+            true /* supportsDynamicUpdate */);
+
     /** Any pair being typed or picked */
     private static final int FREQUENCY_FOR_TYPED = 2;
 
     /** Maximum number of pairs. Pruning will start when databases goes above this number. */
-    public static final int sMaxHistoryBigrams = 10000;
+    public static final int MAX_HISTORY_BIGRAMS = 10000;
 
     /**
      * When it hits maximum bigram pair, it will delete until you are left with
      * only (sMaxHistoryBigrams - sDeleteHistoryBigrams) pairs.
      * Do not keep this number small to avoid deleting too often.
      */
-    public static final int sDeleteHistoryBigrams = 1000;
-
-    /**
-     * Database version should increase if the database structure changes
-     */
-    private static final int DATABASE_VERSION = 1;
-
-    private static final String DATABASE_NAME = "userbigram_dict.db";
-
-    /** Name of the words table in the database */
-    private static final String MAIN_TABLE_NAME = "main";
-    // TODO: Consume less space by using a unique id for locale instead of the whole
-    // 2-5 character string.
-    private static final String MAIN_COLUMN_ID = BaseColumns._ID;
-    private static final String MAIN_COLUMN_WORD1 = "word1";
-    private static final String MAIN_COLUMN_WORD2 = "word2";
-    private static final String MAIN_COLUMN_LOCALE = "locale";
-
-    /** Name of the frequency table in the database */
-    private static final String FREQ_TABLE_NAME = "frequency";
-    private static final String FREQ_COLUMN_ID = BaseColumns._ID;
-    private static final String FREQ_COLUMN_PAIR_ID = "pair_id";
-    private static final String COLUMN_FORGETTING_CURVE_VALUE = "freq";
+    public static final int DELETE_HISTORY_BIGRAMS = 1000;
 
     /** Locale for which this user history dictionary is storing words */
     private final String mLocale;
@@ -91,29 +74,13 @@
     private final ReentrantLock mBigramListLock = new ReentrantLock();
     private final SharedPreferences mPrefs;
 
-    private final static HashMap<String, String> sDictProjectionMap;
-    private final static ConcurrentHashMap<String, SoftReference<UserHistoryDictionary>>
+    // Should always be false except when we use this class for test
+    /* package for test */ boolean isTest = false;
+
+    private static final ConcurrentHashMap<String, SoftReference<UserHistoryDictionary>>
             sLangDictCache = CollectionUtils.newConcurrentHashMap();
 
-    static {
-        sDictProjectionMap = CollectionUtils.newHashMap();
-        sDictProjectionMap.put(MAIN_COLUMN_ID, MAIN_COLUMN_ID);
-        sDictProjectionMap.put(MAIN_COLUMN_WORD1, MAIN_COLUMN_WORD1);
-        sDictProjectionMap.put(MAIN_COLUMN_WORD2, MAIN_COLUMN_WORD2);
-        sDictProjectionMap.put(MAIN_COLUMN_LOCALE, MAIN_COLUMN_LOCALE);
-
-        sDictProjectionMap.put(FREQ_COLUMN_ID, FREQ_COLUMN_ID);
-        sDictProjectionMap.put(FREQ_COLUMN_PAIR_ID, FREQ_COLUMN_PAIR_ID);
-        sDictProjectionMap.put(COLUMN_FORGETTING_CURVE_VALUE, COLUMN_FORGETTING_CURVE_VALUE);
-    }
-
-    private static DatabaseHelper sOpenHelper = null;
-
-    public String getLocale() {
-        return mLocale;
-    }
-
-    public synchronized static UserHistoryDictionary getInstance(
+    public static synchronized UserHistoryDictionary getInstance(
             final Context context, final String locale, final SharedPreferences sp) {
         if (sLangDictCache.containsKey(locale)) {
             final SoftReference<UserHistoryDictionary> ref = sLangDictCache.get(locale);
@@ -136,9 +103,6 @@
         super(context, Dictionary.TYPE_USER_HISTORY);
         mLocale = locale;
         mPrefs = sp;
-        if (sOpenHelper == null) {
-            sOpenHelper = new DatabaseHelper(getContext());
-        }
         if (mLocale != null && mLocale.length() > 1) {
             loadDictionary();
         }
@@ -190,6 +154,7 @@
             try {
                 super.addWord(
                         word2, null /* the "shortcut" parameter is null */, FREQUENCY_FOR_TYPED);
+                mBigramList.addBigram(null, word2, (byte)FREQUENCY_FOR_TYPED);
                 // Do not insert a word as a bigram of itself
                 if (word2.equals(word1)) {
                     return 0;
@@ -227,11 +192,8 @@
      * Schedules a background thread to write any pending words to the database.
      */
     private void flushPendingWrites() {
-        if (mBigramListLock.isLocked()) {
-            return;
-        }
         // Create a background thread to write the pending entries
-        new UpdateDbTask(sOpenHelper, mBigramList, mLocale, this, mPrefs).execute();
+        new UpdateBinaryTask(mBigramList, mLocale, this, mPrefs, getContext()).execute();
     }
 
     @Override
@@ -245,6 +207,8 @@
         }
     }
 
+    private int profTotal;
+
     private void loadDictionaryAsyncLocked() {
         if (DBG_STRESS_TEST) {
             try {
@@ -257,343 +221,181 @@
         final long last = SettingsValues.getLastUserHistoryWriteTime(mPrefs, mLocale);
         final boolean initializing = last == 0;
         final long now = System.currentTimeMillis();
-        // Load the words that correspond to the current input locale
-        final Cursor cursor = query(MAIN_COLUMN_LOCALE + "=?", new String[] { mLocale });
-        if (null == cursor) return;
-        try {
-            // TODO: Call SQLiteDataBase.beginTransaction / SQLiteDataBase.endTransaction
-            if (cursor.moveToFirst()) {
-                final int word1Index = cursor.getColumnIndex(MAIN_COLUMN_WORD1);
-                final int word2Index = cursor.getColumnIndex(MAIN_COLUMN_WORD2);
-                final int fcIndex = cursor.getColumnIndex(COLUMN_FORGETTING_CURVE_VALUE);
-                while (!cursor.isAfterLast()) {
-                    final String word1 = cursor.getString(word1Index);
-                    final String word2 = cursor.getString(word2Index);
-                    final int fc = cursor.getInt(fcIndex);
+        profTotal = 0;
+        final String fileName = NAME + "." + mLocale + ".dict";
+        final ExpandableDictionary dictionary = this;
+        final OnAddWordListener listener = new OnAddWordListener() {
+            @Override
+            public void setUnigram(String word, String shortcutTarget, int frequency) {
+                profTotal++;
+                if (DBG_SAVE_RESTORE) {
+                    Log.d(TAG, "load unigram: " + word + "," + frequency);
+                }
+                dictionary.addWord(word, shortcutTarget, frequency);
+                mBigramList.addBigram(null, word, (byte)frequency);
+            }
+
+            @Override
+            public void setBigram(String word1, String word2, int frequency) {
+                if (word1.length() < BinaryDictionary.MAX_WORD_LENGTH
+                        && word2.length() < BinaryDictionary.MAX_WORD_LENGTH) {
+                    profTotal++;
                     if (DBG_SAVE_RESTORE) {
-                        Log.d(TAG, "--- Load user history: " + word1 + ", " + word2 + ","
-                                + mLocale + "," + this);
+                        Log.d(TAG, "load bigram: " + word1 + "," + word2 + "," + frequency);
                     }
-                    // Safeguard against adding really long words. Stack may overflow due
-                    // to recursive lookup
-                    if (null == word1) {
-                        super.addWord(word2, null /* shortcut */, fc);
-                    } else if (word1.length() < BinaryDictionary.MAX_WORD_LENGTH
-                            && word2.length() < BinaryDictionary.MAX_WORD_LENGTH) {
-                        super.setBigramAndGetFrequency(
-                                word1, word2, initializing ? new ForgettingCurveParams(true)
-                                : new ForgettingCurveParams(fc, now, last));
-                    }
-                    mBigramList.addBigram(word1, word2, (byte)fc);
-                    cursor.moveToNext();
+                    dictionary.setBigramAndGetFrequency(
+                            word1, word2, initializing ? new ForgettingCurveParams(true)
+                            : new ForgettingCurveParams(frequency, now, last));
+                }
+                mBigramList.addBigram(word1, word2, (byte)frequency);
+            }
+        };
+        
+        // Load the dictionary from binary file
+        FileInputStream inStream = null;
+        try {
+            final File file = new File(getContext().getFilesDir(), fileName);
+            final byte[] buffer = new byte[(int)file.length()];
+            inStream = new FileInputStream(file);
+            inStream.read(buffer);
+            UserHistoryDictIOUtils.readDictionaryBinary(
+                    new UserHistoryDictIOUtils.ByteArrayWrapper(buffer), listener);
+        } catch (FileNotFoundException e) {
+            Log.e(TAG, "when loading: file not found" + e);
+        } catch (IOException e) {
+            Log.e(TAG, "IOException when open bytebuffer: " + e);
+        } finally {
+            if (inStream != null) {
+                try {
+                    inStream.close();
+                } catch (IOException e) {
+                    // do nothing
                 }
             }
-        } finally {
-            cursor.close();
             if (PROFILE_SAVE_RESTORE) {
                 final long diff = System.currentTimeMillis() - now;
-                Log.w(TAG, "PROF: Load User HistoryDictionary: "
-                        + mLocale + ", " + diff + "ms.");
+                Log.d(TAG, "PROF: Load UserHistoryDictionary: "
+                        + mLocale + ", " + diff + "ms. load " + profTotal + "entries.");
             }
         }
     }
 
     /**
-     * Query the database
+     * Async task to write pending words to the binarydicts.
      */
-    private static Cursor query(String selection, String[] selectionArgs) {
-        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
-
-        // main INNER JOIN frequency ON (main._id=freq.pair_id)
-        qb.setTables(MAIN_TABLE_NAME + " INNER JOIN " + FREQ_TABLE_NAME + " ON ("
-                + MAIN_TABLE_NAME + "." + MAIN_COLUMN_ID + "=" + FREQ_TABLE_NAME + "."
-                + FREQ_COLUMN_PAIR_ID +")");
-
-        qb.setProjectionMap(sDictProjectionMap);
-
-        // Get the database and run the query
-        try {
-            SQLiteDatabase db = sOpenHelper.getReadableDatabase();
-            Cursor c = qb.query(db,
-                    new String[] {
-                            MAIN_COLUMN_WORD1, MAIN_COLUMN_WORD2, COLUMN_FORGETTING_CURVE_VALUE },
-                    selection, selectionArgs, null, null, null);
-            return c;
-        } catch (android.database.sqlite.SQLiteCantOpenDatabaseException e) {
-            // Can't open the database : presumably we can't access storage. That may happen
-            // when the device is wedged; do a best effort to still start the keyboard.
-            return null;
-        }
-    }
-
-    /**
-     * This class helps open, create, and upgrade the database file.
-     */
-    private static class DatabaseHelper extends SQLiteOpenHelper {
-
-        DatabaseHelper(Context context) {
-            super(context, DATABASE_NAME, null, DATABASE_VERSION);
-        }
-
-        @Override
-        public void onCreate(SQLiteDatabase db) {
-            db.execSQL("PRAGMA foreign_keys = ON;");
-            db.execSQL("CREATE TABLE " + MAIN_TABLE_NAME + " ("
-                    + MAIN_COLUMN_ID + " INTEGER PRIMARY KEY,"
-                    + MAIN_COLUMN_WORD1 + " TEXT,"
-                    + MAIN_COLUMN_WORD2 + " TEXT,"
-                    + MAIN_COLUMN_LOCALE + " TEXT"
-                    + ");");
-            db.execSQL("CREATE TABLE " + FREQ_TABLE_NAME + " ("
-                    + FREQ_COLUMN_ID + " INTEGER PRIMARY KEY,"
-                    + FREQ_COLUMN_PAIR_ID + " INTEGER,"
-                    + COLUMN_FORGETTING_CURVE_VALUE + " INTEGER,"
-                    + "FOREIGN KEY(" + FREQ_COLUMN_PAIR_ID + ") REFERENCES " + MAIN_TABLE_NAME
-                    + "(" + MAIN_COLUMN_ID + ")" + " ON DELETE CASCADE"
-                    + ");");
-        }
-
-        @Override
-        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
-            Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
-                    + newVersion + ", which will destroy all old data");
-            db.execSQL("DROP TABLE IF EXISTS " + MAIN_TABLE_NAME);
-            db.execSQL("DROP TABLE IF EXISTS " + FREQ_TABLE_NAME);
-            onCreate(db);
-        }
-    }
-
-    /**
-     * Async task to write pending words to the database so that it stays in sync with
-     * the in-memory trie.
-     */
-    private static class UpdateDbTask extends AsyncTask<Void, Void, Void> {
+    private static class UpdateBinaryTask extends AsyncTask<Void, Void, Void>
+            implements BigramDictionaryInterface {
         private final UserHistoryDictionaryBigramList mBigramList;
-        private final DatabaseHelper mDbHelper;
+        private final boolean mAddLevel0Bigrams;
         private final String mLocale;
         private final UserHistoryDictionary mUserHistoryDictionary;
         private final SharedPreferences mPrefs;
+        private final Context mContext;
 
-        public UpdateDbTask(
-                DatabaseHelper openHelper, UserHistoryDictionaryBigramList pendingWrites,
-                String locale, UserHistoryDictionary dict, SharedPreferences prefs) {
+        public UpdateBinaryTask(UserHistoryDictionaryBigramList pendingWrites, String locale,
+                UserHistoryDictionary dict, SharedPreferences prefs, Context context) {
             mBigramList = pendingWrites;
             mLocale = locale;
-            mDbHelper = openHelper;
             mUserHistoryDictionary = dict;
             mPrefs = prefs;
-        }
-
-        /** Prune any old data if the database is getting too big. */
-        private static void checkPruneData(SQLiteDatabase db) {
-            db.execSQL("PRAGMA foreign_keys = ON;");
-            Cursor c = db.query(FREQ_TABLE_NAME, new String[] { FREQ_COLUMN_PAIR_ID },
-                    null, null, null, null, null);
-            try {
-                int totalRowCount = c.getCount();
-                // prune out old data if we have too much data
-                if (totalRowCount > sMaxHistoryBigrams) {
-                    int numDeleteRows = (totalRowCount - sMaxHistoryBigrams)
-                            + sDeleteHistoryBigrams;
-                    int pairIdColumnId = c.getColumnIndex(FREQ_COLUMN_PAIR_ID);
-                    c.moveToFirst();
-                    int count = 0;
-                    while (count < numDeleteRows && !c.isAfterLast()) {
-                        String pairId = c.getString(pairIdColumnId);
-                        // Deleting from MAIN table will delete the frequencies
-                        // due to FOREIGN KEY .. ON DELETE CASCADE
-                        db.delete(MAIN_TABLE_NAME, MAIN_COLUMN_ID + "=?",
-                            new String[] { pairId });
-                        c.moveToNext();
-                        count++;
-                    }
-                }
-            } finally {
-                c.close();
-            }
+            mContext = context;
+            mAddLevel0Bigrams = mBigramList.size() <= MAX_HISTORY_BIGRAMS;
         }
 
         @Override
         protected Void doInBackground(Void... v) {
-            SQLiteDatabase db = null;
-            if (mUserHistoryDictionary.mBigramListLock.tryLock()) {
+            if (mUserHistoryDictionary.isTest) {
+                // If isTest == true, wait until the lock is released.
+                mUserHistoryDictionary.mBigramListLock.lock();
                 try {
-                    try {
-                        db = mDbHelper.getWritableDatabase();
-                    } catch (android.database.sqlite.SQLiteCantOpenDatabaseException e) {
-                        // If we can't open the db, don't do anything. Exit through the next test
-                        // for non-nullity of the db variable.
-                    }
-                    if (null == db) {
-                        // Not much we can do. Just exit.
-                        return null;
-                    }
-                    db.beginTransaction();
-                    return doLoadTaskLocked(db);
+                    doWriteTaskLocked();
                 } finally {
-                    if (db != null) {
-                        db.endTransaction();
-                    }
                     mUserHistoryDictionary.mBigramListLock.unlock();
                 }
+            } else if (mUserHistoryDictionary.mBigramListLock.tryLock()) {
+                doWriteTaskLocked();
             }
             return null;
         }
 
-        private Void doLoadTaskLocked(SQLiteDatabase db) {
+        private void doWriteTaskLocked() {
             if (DBG_STRESS_TEST) {
                 try {
                     Log.w(TAG, "Start stress in closing: " + mLocale);
                     Thread.sleep(15000);
                     Log.w(TAG, "End stress in closing");
                 } catch (InterruptedException e) {
+                    Log.e(TAG, "In stress test: " + e);
                 }
             }
+
             final long now = PROFILE_SAVE_RESTORE ? System.currentTimeMillis() : 0;
-            int profTotal = 0;
-            int profInsert = 0;
-            int profDelete = 0;
-            db.execSQL("PRAGMA foreign_keys = ON;");
-            final boolean addLevel0Bigram = mBigramList.size() <= sMaxHistoryBigrams;
+            final String fileName = NAME + "." + mLocale + ".dict";
+            final File file = new File(mContext.getFilesDir(), fileName);
+            FileOutputStream out = null;
 
-            // Write all the entries to the db
-            for (String word1 : mBigramList.keySet()) {
-                final HashMap<String, Byte> word1Bigrams = mBigramList.getBigrams(word1);
-                for (String word2 : word1Bigrams.keySet()) {
-                    if (PROFILE_SAVE_RESTORE) {
-                        ++profTotal;
-                    }
-                    // Get new frequency. Do not insert unigrams/bigrams which freq is "-1".
-                    final int freq; // -1, or 0~255
-                    if (word1 == null) { // unigram
-                        freq = FREQUENCY_FOR_TYPED;
-                        final byte prevFc = word1Bigrams.get(word2);
-                        if (prevFc == FREQUENCY_FOR_TYPED) {
-                            // No need to update since we found no changes for this entry.
-                            // Just skip to the next entry.
-                            if (DBG_SAVE_RESTORE) {
-                                Log.d(TAG, "Skip update user history: " + word1 + "," + word2
-                                        + "," + prevFc);
-                            }
-                            if (!DBG_ALWAYS_WRITE) {
-                                continue;
-                            }
-                        }
-                    } else { // bigram
-                        final NextWord nw = mUserHistoryDictionary.getBigramWord(word1, word2);
-                        if (nw != null) {
-                            final ForgettingCurveParams fcp = nw.getFcParams();
-                            final byte prevFc = word1Bigrams.get(word2);
-                            final byte fc = (byte)fcp.getFc();
-                            final boolean isValid = fcp.isValid();
-                            if (prevFc > 0 && prevFc == fc) {
-                                // No need to update since we found no changes for this entry.
-                                // Just skip to the next entry.
-                                if (DBG_SAVE_RESTORE) {
-                                    Log.d(TAG, "Skip update user history: " + word1 + ","
-                                            + word2 + "," + prevFc);
-                                }
-                                if (!DBG_ALWAYS_WRITE) {
-                                    continue;
-                                } else {
-                                    freq = fc;
-                                }
-                            } else if (UserHistoryForgettingCurveUtils.
-                                    needsToSave(fc, isValid, addLevel0Bigram)) {
-                                freq = fc;
-                            } else {
-                                // Delete this entry
-                                freq = -1;
-                            }
-                        } else {
-                            // Delete this entry
-                            freq = -1;
-                        }
-                    }
-                    // TODO: this process of making a text search for each pair each time
-                    // is terribly inefficient. Optimize this.
-                    // Find pair id
-                    Cursor c = null;
+            try {
+                out = new FileOutputStream(file);
+                UserHistoryDictIOUtils.writeDictionaryBinary(out, this, mBigramList, VERSION3);
+                out.flush();
+                out.close();
+            } catch (IOException e) {
+                Log.e(TAG, "IO Exception while writing file: " + e);
+            } finally {
+                if (out != null) {
                     try {
-                        if (null != word1) {
-                            c = db.query(MAIN_TABLE_NAME, new String[] { MAIN_COLUMN_ID },
-                                    MAIN_COLUMN_WORD1 + "=? AND " + MAIN_COLUMN_WORD2 + "=? AND "
-                                            + MAIN_COLUMN_LOCALE + "=?",
-                                            new String[] { word1, word2, mLocale }, null, null,
-                                            null);
-                        } else {
-                            c = db.query(MAIN_TABLE_NAME, new String[] { MAIN_COLUMN_ID },
-                                    MAIN_COLUMN_WORD1 + " IS NULL AND " + MAIN_COLUMN_WORD2
-                                    + "=? AND " + MAIN_COLUMN_LOCALE + "=?",
-                                    new String[] { word2, mLocale }, null, null, null);
-                        }
-
-                        final int pairId;
-                        if (c.moveToFirst()) {
-                            if (PROFILE_SAVE_RESTORE) {
-                                ++profDelete;
-                            }
-                            // Delete existing pair
-                            pairId = c.getInt(c.getColumnIndex(MAIN_COLUMN_ID));
-                            db.delete(FREQ_TABLE_NAME, FREQ_COLUMN_PAIR_ID + "=?",
-                                    new String[] { Integer.toString(pairId) });
-                        } else {
-                            // Create new pair
-                            Long pairIdLong = db.insert(MAIN_TABLE_NAME, null,
-                                    getContentValues(word1, word2, mLocale));
-                            pairId = pairIdLong.intValue();
-                        }
-                        // Eliminate freq == 0 because that word is profanity.
-                        if (freq > 0) {
-                            if (PROFILE_SAVE_RESTORE) {
-                                ++profInsert;
-                            }
-                            if (DBG_SAVE_RESTORE) {
-                                Log.d(TAG, "--- Save user history: " + word1 + ", " + word2
-                                        + mLocale + "," + this);
-                            }
-                            // Insert new frequency
-                            db.insert(FREQ_TABLE_NAME, null,
-                                    getFrequencyContentValues(pairId, freq));
-                            // Update an existing bigram entry in mBigramList too in order to
-                            // synchronize the SQL DB and mBigramList.
-                            mBigramList.updateBigram(word1, word2, (byte)freq);
-                        }
-                    } finally {
-                        if (c != null) {
-                            c.close();
-                        }
+                        out.close();
+                    } catch (IOException e) {
+                        // ignore
                     }
                 }
             }
 
-            checkPruneData(db);
-            // Save the timestamp after we finish writing the SQL DB.
+            // Save the timestamp after we finish writing the binary dictionary.
             SettingsValues.setLastUserHistoryWriteTime(mPrefs, mLocale);
             if (PROFILE_SAVE_RESTORE) {
                 final long diff = System.currentTimeMillis() - now;
-                Log.w(TAG, "PROF: Write User HistoryDictionary: " + mLocale + ", "+ diff
-                        + "ms. Total: " + profTotal + ". Insert: " + profInsert + ". Delete: "
-                        + profDelete);
+                Log.w(TAG, "PROF: Write User HistoryDictionary: " + mLocale + ", " + diff + "ms.");
             }
-            db.setTransactionSuccessful();
-            return null;
         }
 
-        private static ContentValues getContentValues(String word1, String word2, String locale) {
-            ContentValues values = new ContentValues(3);
-            values.put(MAIN_COLUMN_WORD1, word1);
-            values.put(MAIN_COLUMN_WORD2, word2);
-            values.put(MAIN_COLUMN_LOCALE, locale);
-            return values;
-        }
-
-        private static ContentValues getFrequencyContentValues(int pairId, int frequency) {
-           ContentValues values = new ContentValues(2);
-           values.put(FREQ_COLUMN_PAIR_ID, pairId);
-           values.put(COLUMN_FORGETTING_CURVE_VALUE, frequency);
-           return values;
+        @Override
+        public int getFrequency(String word1, String word2) {
+            final int freq;
+            if (word1 == null) { // unigram
+                freq = FREQUENCY_FOR_TYPED;
+                final byte prevFc = mBigramList.getBigrams(word1).get(word2);
+            } else { // bigram
+                final NextWord nw = mUserHistoryDictionary.getBigramWord(word1, word2);
+                if (nw != null) {
+                    final ForgettingCurveParams fcp = nw.getFcParams();
+                    final byte prevFc = mBigramList.getBigrams(word1).get(word2);
+                    final byte fc = fcp.getFc();
+                    final boolean isValid = fcp.isValid();
+                    if (prevFc > 0 && prevFc == fc) {
+                        freq = ((int)fc) & 0xFF;
+                    } else if (UserHistoryForgettingCurveUtils.
+                            needsToSave(fc, isValid, mAddLevel0Bigrams)) {
+                        freq = ((int)fc) & 0xFF;
+                    } else {
+                        // Delete this entry
+                        freq = -1;
+                    }
+                } else {
+                    // Delete this entry
+                    freq = -1;
+                }
+            }
+            return freq;
         }
     }
 
+    void forceAddWordForTest(final String word1, final String word2, final boolean isValid) {
+        mBigramListLock.lock();
+        try {
+            addToUserHistory(word1, word2, isValid);
+        } finally {
+            mBigramListLock.unlock();
+        }
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
index 19da512..a1606db 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
@@ -77,7 +77,10 @@
                 p.mAddress += BinaryDictInputOutput.getGroupCountSize(p.mNumOfCharGroup);
                 p.mPosition = 0;
             }
-
+            if (p.mNumOfCharGroup == 0) {
+                stack.pop();
+                continue;
+            }
             CharGroupInfo info = BinaryDictInputOutput.readCharGroup(buffer,
                     p.mAddress - headerSize, formatOptions);
             for (int i = 0; i < info.mCharacters.length; ++i) {
@@ -85,7 +88,10 @@
             }
             p.mPosition++;
 
-            if (info.mFrequency != FusionDictionary.CharGroup.NOT_A_TERMINAL) { // found word
+            final boolean isMovedGroup = BinaryDictInputOutput.isMovedGroup(info.mFlags,
+                    formatOptions);
+            if (!isMovedGroup
+                    && info.mFrequency != FusionDictionary.CharGroup.NOT_A_TERMINAL) {// found word
                 words.put(info.mOriginalAddress, new String(pushedChars, 0, index));
                 frequencies.put(info.mOriginalAddress, info.mFrequency);
                 if (info.mBigrams != null) bigrams.put(info.mOriginalAddress, info.mBigrams);
@@ -109,7 +115,7 @@
                 p.mAddress = buffer.position();
             }
 
-            if (BinaryDictInputOutput.hasChildrenAddress(info.mChildrenAddress)) {
+            if (!isMovedGroup && BinaryDictInputOutput.hasChildrenAddress(info.mChildrenAddress)) {
                 Position childrenPos = new Position(info.mChildrenAddress + headerSize, index);
                 stack.push(childrenPos);
             }
@@ -168,6 +174,10 @@
                     final int charGroupPos = buffer.position();
                     final CharGroupInfo currentInfo = BinaryDictInputOutput.readCharGroup(buffer,
                             buffer.position(), header.mFormatOptions);
+                    if (BinaryDictInputOutput.isMovedGroup(currentInfo.mFlags,
+                            header.mFormatOptions)) {
+                        continue;
+                    }
                     boolean same = true;
                     for (int p = 0, j = word.offsetByCodePoints(0, wordPos);
                             p < currentInfo.mCharacters.length;
@@ -239,4 +249,34 @@
         buffer.position(wordPosition);
         buffer.put((byte)newFlags);
     }
+
+    private static void putSInt24(final FusionDictionaryBufferInterface buffer,
+            final int value) {
+        final int absValue = Math.abs(value);
+        buffer.put((byte)(((value < 0 ? 0x80 : 0) | (absValue >> 16)) & 0xFF));
+        buffer.put((byte)((absValue >> 8) & 0xFF));
+        buffer.put((byte)(absValue & 0xFF));
+    }
+
+    /**
+     * Update a parent address in a CharGroup that is addressed by groupOriginAddress.
+     *
+     * @param buffer the buffer to write.
+     * @param groupOriginAddress the address of the group.
+     * @param newParentAddress the absolute address of the parent.
+     * @param formatOptions file format options.
+     */
+    public static void updateParentAddress(final FusionDictionaryBufferInterface buffer,
+            final int groupOriginAddress, final int newParentAddress,
+            final FormatOptions formatOptions) {
+        final int originalPosition = buffer.position();
+        buffer.position(groupOriginAddress);
+        if (!formatOptions.mSupportsDynamicUpdate) {
+            throw new RuntimeException("this file format does not support parent addresses");
+        }
+        final int flags = buffer.readUnsignedByte();
+        final int parentOffset = newParentAddress - groupOriginAddress;
+        putSInt24(buffer, parentOffset);
+        buffer.position(originalPosition);
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
index f9339de..9fc6942 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
@@ -53,6 +53,7 @@
     // If the number of passes exceeds this number, makedict bails with an exception on
     // suspicion that a bug might be causing an infinite loop.
     private static final int MAX_PASSES = 24;
+    private static final int MAX_JUMPS = 12;
 
     public interface FusionDictionaryBufferInterface {
         public int readUnsignedByte();
@@ -395,6 +396,13 @@
     }
 
     /**
+     * Helper method to check whether the group is moved.
+     */
+    public static boolean isMovedGroup(final int flags, final FormatOptions options) {
+        return options.mSupportsDynamicUpdate && ((flags & FormatSpec.FLAG_IS_MOVED) == 1);
+    }
+
+    /**
      * Helper method to check whether the dictionary can be updated dynamically.
      */
     public static boolean supportsDynamicUpdate(final FormatOptions options) {
@@ -1374,8 +1382,18 @@
         int index = FormatSpec.MAX_WORD_LENGTH - 1;
         // the length of the path from the root to the leaf is limited by MAX_WORD_LENGTH
         for (int count = 0; count < FormatSpec.MAX_WORD_LENGTH; ++count) {
-            buffer.position(currentAddress + headerSize);
-            final CharGroupInfo currentInfo = readCharGroup(buffer, currentAddress, options);
+            CharGroupInfo currentInfo;
+            int loopCounter = 0;
+            do {
+                buffer.position(currentAddress + headerSize);
+                currentInfo = readCharGroup(buffer, currentAddress, options);
+                if (isMovedGroup(currentInfo.mFlags, options)) {
+                    currentAddress = currentInfo.mParentAddress + currentInfo.mOriginalAddress;
+                }
+                if (DBG && loopCounter++ > MAX_JUMPS) {
+                    MakedictLog.d("Too many jumps - probably a bug");
+                }
+            } while (isMovedGroup(currentInfo.mFlags, options));
             for (int i = 0; i < currentInfo.mCharacters.length; ++i) {
                 sGetWordBuffer[index--] =
                         currentInfo.mCharacters[currentInfo.mCharacters.length - i - 1];
@@ -1457,6 +1475,7 @@
             int groupOffset = nodeHeadPosition + getGroupCountSize(count);
             for (int i = count; i > 0; --i) { // Scan the array of CharGroup.
                 CharGroupInfo info = readCharGroup(buffer, groupOffset, options);
+                if (isMovedGroup(info.mFlags, options)) continue;
                 ArrayList<WeightedString> shortcutTargets = info.mShortcutTargets;
                 ArrayList<WeightedString> bigrams = null;
                 if (null != info.mBigrams) {
diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
index cab0661..35311f0 100644
--- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
+++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
@@ -52,13 +52,18 @@
      */
 
     /* Node(CharGroup) layout is as follows:
-     *   | addressType                         xx     : mask with MASK_GROUP_ADDRESS_TYPE
-     *                                 2 bits, 00 = no children : FLAG_GROUP_ADDRESS_TYPE_NOADDRESS
-     * f |                                     01 = 1 byte      : FLAG_GROUP_ADDRESS_TYPE_ONEBYTE
-     * l |                                     10 = 2 bytes     : FLAG_GROUP_ADDRESS_TYPE_TWOBYTES
-     * a |                                     11 = 3 bytes     : FLAG_GROUP_ADDRESS_TYPE_THREEBYTES
-     * g | has several chars ?         1 bit, 1 = yes, 0 = no   : FLAG_HAS_MULTIPLE_CHARS
-     * s | has a terminal ?            1 bit, 1 = yes, 0 = no   : FLAG_IS_TERMINAL
+     *   | IF !SUPPORTS_DYNAMIC_UPDATE
+     *   |   addressType                         xx     : mask with MASK_GROUP_ADDRESS_TYPE
+     *   |                           2 bits, 00 = no children : FLAG_GROUP_ADDRESS_TYPE_NOADDRESS
+     * f |                                   01 = 1 byte      : FLAG_GROUP_ADDRESS_TYPE_ONEBYTE
+     * l |                                   10 = 2 bytes     : FLAG_GROUP_ADDRESS_TYPE_TWOBYTES
+     * a |                                   11 = 3 bytes     : FLAG_GROUP_ADDRESS_TYPE_THREEBYTES
+     * g | ELSE
+     * s |   is moved ?              2 bits, 11 = no
+     *   |                                   01 = yes
+     *   |                        the new address is stored in the same place as the parent address
+     *   | has several chars ?         1 bit, 1 = yes, 0 = no   : FLAG_HAS_MULTIPLE_CHARS
+     *   | has a terminal ?            1 bit, 1 = yes, 0 = no   : FLAG_IS_TERMINAL
      *   | has shortcut targets ?      1 bit, 1 = yes, 0 = no   : FLAG_HAS_SHORTCUT_TARGETS
      *   | has bigrams ?               1 bit, 1 = yes, 0 = no   : FLAG_HAS_BIGRAMS
      *   | is not a word ?             1 bit, 1 = yes, 0 = no   : FLAG_IS_NOT_A_WORD
@@ -178,6 +183,7 @@
     static final int FLAG_HAS_BIGRAMS = 0x04;
     static final int FLAG_IS_NOT_A_WORD = 0x02;
     static final int FLAG_IS_BLACKLISTED = 0x01;
+    static final int FLAG_IS_MOVED = 0x40;
 
     static final int FLAG_ATTRIBUTE_HAS_NEXT = 0x80;
     static final int FLAG_ATTRIBUTE_OFFSET_NEGATIVE = 0x40;
diff --git a/native/jni/src/char_utils.cpp b/native/jni/src/char_utils.cpp
index 9d886da31..d0547a9 100644
--- a/native/jni/src/char_utils.cpp
+++ b/native/jni/src/char_utils.cpp
@@ -88,6 +88,7 @@
     { 0x00C5, 0x00E5 },  // LATIN CAPITAL LETTER A WITH RING ABOVE
     { 0x00C6, 0x00E6 },  // LATIN CAPITAL LETTER AE
     { 0x00D0, 0x00F0 },  // LATIN CAPITAL LETTER ETH
+    { 0x00D1, 0x00F1 },  // LATIN CAPITAL LETTER N WITH TILDE
     { 0x00D5, 0x00F5 },  // LATIN CAPITAL LETTER O WITH TILDE
     { 0x00D6, 0x00F6 },  // LATIN CAPITAL LETTER O WITH DIAERESIS
     { 0x00D8, 0x00F8 },  // LATIN CAPITAL LETTER O WITH STROKE
@@ -219,6 +220,7 @@
     { 0x0416, 0x0436 },  // CYRILLIC CAPITAL LETTER ZHE
     { 0x0417, 0x0437 },  // CYRILLIC CAPITAL LETTER ZE
     { 0x0418, 0x0438 },  // CYRILLIC CAPITAL LETTER I
+    { 0x0419, 0x0439 },  // CYRILLIC CAPITAL LETTER SHORT I
     { 0x041A, 0x043A },  // CYRILLIC CAPITAL LETTER KA
     { 0x041B, 0x043B },  // CYRILLIC CAPITAL LETTER EL
     { 0x041C, 0x043C },  // CYRILLIC CAPITAL LETTER EM
diff --git a/native/jni/src/char_utils.h b/native/jni/src/char_utils.h
index b17f262..20cf2e8 100644
--- a/native/jni/src/char_utils.h
+++ b/native/jni/src/char_utils.h
@@ -23,7 +23,9 @@
 namespace latinime {
 
 inline static bool isAsciiUpper(unsigned short c) {
-    return isupper(static_cast<int>(c)) != 0;
+    // Note: isupper(...) reports false positives for some Cyrillic characters, causing them to
+    // be incorrectly lower-cased using toAsciiLower(...) rather than latin_tolower(...).
+    return (c >= 'A' && c <= 'Z');
 }
 
 inline static unsigned short toAsciiLower(unsigned short c) {
diff --git a/tests/src/com/android/inputmethod/latin/UserHistoryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/UserHistoryDictionaryTests.java
new file mode 100644
index 0000000..f2a17d2
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/UserHistoryDictionaryTests.java
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ */
+
+package com.android.inputmethod.latin;
+
+import com.android.inputmethod.latin.UserHistoryDictionary;
+
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+
+/**
+ * Unit tests for UserHistoryDictionary
+ */
+public class UserHistoryDictionaryTests extends AndroidTestCase {
+    private static final String TAG = UserHistoryDictionaryTests.class.getSimpleName();
+    private SharedPreferences mPrefs;
+
+    private static final String[] CHARACTERS = {
+        "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
+        "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"
+    };
+
+    @Override
+    public void setUp() {
+        mPrefs = PreferenceManager.getDefaultSharedPreferences(getContext());
+    }
+
+    /**
+     * Generates a random word.
+     */
+    private String generateWord(final int value) {
+        final int lengthOfChars = CHARACTERS.length;
+        StringBuilder builder = new StringBuilder();
+        long lvalue = Math.abs((long)value);
+        while (lvalue > 0) {
+            builder.append(CHARACTERS[(int)(lvalue % lengthOfChars)]);
+            lvalue /= lengthOfChars;
+        }
+        return builder.toString();
+    }
+
+    private List<String> generateWords(final int number, final Random random) {
+        final Set<String> wordSet = CollectionUtils.newHashSet();
+        while (wordSet.size() < number) {
+            wordSet.add(generateWord(random.nextInt()));
+        }
+        return new ArrayList<String>(wordSet);
+    }
+
+    private void addToDict(final UserHistoryDictionary dict, final List<String> words) {
+        String prevWord = null;
+        for (String word : words) {
+            dict.forceAddWordForTest(prevWord, word, true);
+            prevWord = word;
+        }
+    }
+
+    public void testRandomWords() {
+        Log.d(TAG, "This test can be used for profiling.");
+        Log.d(TAG, "Usage: please set UserHisotoryDictionary.PROFILE_SAVE_RESTORE to true.");
+        final int numberOfWords = 1000;
+        final Random random = new Random(123456);
+        List<String> words = generateWords(numberOfWords, random);
+
+        final String locale = "testRandomWords";
+        final UserHistoryDictionary dict = UserHistoryDictionary.getInstance(getContext(),
+                locale, mPrefs);
+        dict.isTest = true;
+
+        addToDict(dict, words);
+
+        try {
+            Log.d(TAG, "waiting for adding the word ...");
+            Thread.sleep(2000);
+        } catch (InterruptedException e) {
+            Log.d(TAG, "InterruptedException: " + e);
+        }
+
+        // write to file
+        dict.close();
+
+        try {
+            Log.d(TAG, "waiting for writing ...");
+            Thread.sleep(5000);
+        } catch (InterruptedException e) {
+            Log.d(TAG, "InterruptedException: " + e);
+        }
+    }
+}