Merge "Add Ime language switching test."
diff --git a/dictionaries/en_GB_wordlist.combined.gz b/dictionaries/en_GB_wordlist.combined.gz
index 839f3ef..4f008ed 100644
--- a/dictionaries/en_GB_wordlist.combined.gz
+++ b/dictionaries/en_GB_wordlist.combined.gz
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_settings_holo_dark.png b/java/res/drawable-hdpi/sym_keyboard_settings_holo_dark.png
index c76008a..5af09ad 100644
--- a/java/res/drawable-hdpi/sym_keyboard_settings_holo_dark.png
+++ b/java/res/drawable-hdpi/sym_keyboard_settings_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_settings_holo_dark.png b/java/res/drawable-mdpi/sym_keyboard_settings_holo_dark.png
index a76a976..36c8c96 100644
--- a/java/res/drawable-mdpi/sym_keyboard_settings_holo_dark.png
+++ b/java/res/drawable-mdpi/sym_keyboard_settings_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_settings_holo_dark.png b/java/res/drawable-xhdpi/sym_keyboard_settings_holo_dark.png
index 05eaffe..99ee97d 100644
--- a/java/res/drawable-xhdpi/sym_keyboard_settings_holo_dark.png
+++ b/java/res/drawable-xhdpi/sym_keyboard_settings_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_settings_holo_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_settings_holo_dark.png
index e435846..7041bb6 100644
--- a/java/res/drawable-xxhdpi/sym_keyboard_settings_holo_dark.png
+++ b/java/res/drawable-xxhdpi/sym_keyboard_settings_holo_dark.png
Binary files differ
diff --git a/java/res/layout/emoji_keyboard_view.xml b/java/res/layout/emoji_palettes_view.xml
similarity index 93%
rename from java/res/layout/emoji_keyboard_view.xml
rename to java/res/layout/emoji_palettes_view.xml
index 4566a5a..1c6da90 100644
--- a/java/res/layout/emoji_keyboard_view.xml
+++ b/java/res/layout/emoji_palettes_view.xml
@@ -18,13 +18,13 @@
 */
 -->
 
-<com.android.inputmethod.keyboard.EmojiKeyboardView
+<com.android.inputmethod.keyboard.EmojiPalettesView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/emoji_keyboard_view"
     android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    style="?attr/emojiKeyboardViewStyle"
+    style="?attr/emojiPalettesViewStyle"
 >
     <LinearLayout
         android:orientation="horizontal"
@@ -101,10 +101,10 @@
             android:layout_weight="0.70"
             android:layout_height="match_parent" />
         <ImageButton
-            android:id="@+id/emoji_keyboard_send"
+            android:id="@+id/emoji_keyboard_alphabet2"
             android:layout_width="0dip"
             android:layout_weight="0.15"
             android:layout_height="match_parent"
-            android:src="@drawable/sym_keyboard_return_holo_dark" />
+            android:src="@drawable/ic_ime_switcher_dark" />
     </LinearLayout>
-</com.android.inputmethod.keyboard.EmojiKeyboardView>
+</com.android.inputmethod.keyboard.EmojiPalettesView>
diff --git a/java/res/layout/input_view.xml b/java/res/layout/input_view.xml
index 0b682d1..1e7a384 100644
--- a/java/res/layout/input_view.xml
+++ b/java/res/layout/input_view.xml
@@ -56,5 +56,5 @@
             android:layout_height="wrap_content" />
     </LinearLayout>
     <include
-        layout="@layout/emoji_keyboard_view" />
+        layout="@layout/emoji_palettes_view" />
 </com.android.inputmethod.latin.InputView>
diff --git a/java/res/values-af/strings.xml b/java/res/values-af/strings.xml
index f187a73..adf9e5b 100644
--- a/java/res/values-af/strings.xml
+++ b/java/res/values-af/strings.xml
@@ -84,8 +84,8 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Koppel \'n kopstuk om te hoor hoe wagwoordsleutels hardop gesê word."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"Huidige teks is %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"Geen teks ingevoer nie"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> korrigeer <xliff:g id="ORIGINAL">%2$s</xliff:g> na <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> het outokorreksie"</string>
+    <string name="spoken_auto_correct" msgid="8005997889020109763">"<xliff:g id="KEY">%1$s</xliff:g> korrigeer <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> na <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+    <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"<xliff:g id="KEY">%1$s</xliff:g> voer outokorreksie uit"</string>
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Sleutelkode %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift aan (tik om te deaktiveer)"</string>
diff --git a/java/res/values-am/strings.xml b/java/res/values-am/strings.xml
index 6504e64..9fa68f3 100644
--- a/java/res/values-am/strings.xml
+++ b/java/res/values-am/strings.xml
@@ -84,8 +84,8 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"የይለፍቃል ቁልፎች ጮክ በለው ሲነገሩ ለመስማት የጆሮ ማዳመጫ ሰካ::"</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"የአሁኑ ፅሁፍ %s ነው"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"ምንም ፅሁፍ አልገባም"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> <xliff:g id="ORIGINAL">%2$s</xliff:g>ን ወደ <xliff:g id="CORRECTED">%3$s</xliff:g> ያርመዋል"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> ራስ-አርም አለው"</string>
+    <string name="spoken_auto_correct" msgid="8005997889020109763">"<xliff:g id="KEY">%1$s</xliff:g> <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g>ን ወደ <xliff:g id="CORRECTED">%3$s</xliff:g> ያርመዋል"</string>
+    <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"<xliff:g id="KEY">%1$s</xliff:g> ራስ-ሰር እርማትን ያከናውናል"</string>
     <string name="spoken_description_unknown" msgid="3197434010402179157">"የቁልፍ ኮድ%d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"ቀይር"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"ቅያር በርቷል (ለማሰናክል ንካ)"</string>
diff --git a/java/res/values-ar/strings.xml b/java/res/values-ar/strings.xml
index 46bff12..1d58832 100644
--- a/java/res/values-ar/strings.xml
+++ b/java/res/values-ar/strings.xml
@@ -84,8 +84,8 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"يمكنك توصيل سماعة رأس لسماع مفاتيح كلمة المرور منطوقة بصوت عالٍ."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"النص الحالي هو %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"لم يتم إدخال نص"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> لتصحيح <xliff:g id="ORIGINAL">%2$s</xliff:g> إلى <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> للتصحيح التلقائي"</string>
+    <string name="spoken_auto_correct" msgid="8005997889020109763">"<xliff:g id="KEY">%1$s</xliff:g> لتصحيح <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> إلى <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+    <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"<xliff:g id="KEY">%1$s</xliff:g> للتصحيح التلقائي"</string>
     <string name="spoken_description_unknown" msgid="3197434010402179157">"رمز المفتاح %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"العالي"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift يعمل (انقر للتعطيل)"</string>
diff --git a/java/res/values-be/strings.xml b/java/res/values-be/strings.xml
index 9e591de..d9b9708 100644
--- a/java/res/values-be/strings.xml
+++ b/java/res/values-be/strings.xml
@@ -87,9 +87,9 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Каб праслухаць паролi, падключыце гарнiтуру."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"Бягучы тэкст %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"Тэкст не ўведзены"</string>
-    <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
+    <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
     <skip />
-    <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
+    <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
     <skip />
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Клавішны код %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Зрух"</string>
diff --git a/java/res/values-bg/strings.xml b/java/res/values-bg/strings.xml
index 0e6c8dd..7064907 100644
--- a/java/res/values-bg/strings.xml
+++ b/java/res/values-bg/strings.xml
@@ -84,8 +84,10 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Включете слушалки, за да чуете клавишите за паролата на висок глас."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"Текущият текст е %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"Няма въведен текст"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"„<xliff:g id="KEY">%1$s</xliff:g>“ коригира „<xliff:g id="ORIGINAL">%2$s</xliff:g>“ на „<xliff:g id="CORRECTED">%3$s</xliff:g>“"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"„<xliff:g id="KEY">%1$s</xliff:g>“ е с автоматично коригиране"</string>
+    <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+    <skip />
+    <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+    <skip />
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Код на клавишa %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"„Shift“ е включен (докоснете за деактивиране)"</string>
diff --git a/java/res/values-ca/strings.xml b/java/res/values-ca/strings.xml
index 043fbd9..ad3c92e 100644
--- a/java/res/values-ca/strings.xml
+++ b/java/res/values-ca/strings.xml
@@ -84,8 +84,8 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Connecta un auricular per escoltar les claus de la contrasenya en veu alta."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"El text actual és %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"No s\'ha introduït cap text"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> corregeix <xliff:g id="ORIGINAL">%2$s</xliff:g> per <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> té correcció automàtica"</string>
+    <string name="spoken_auto_correct" msgid="8005997889020109763">"<xliff:g id="KEY">%1$s</xliff:g> corregeix <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> per <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+    <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"<xliff:g id="KEY">%1$s</xliff:g> aplica correccions automàtiques"</string>
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Clau de codi %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Maj"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Maj activat (pica per desactivar)"</string>
diff --git a/java/res/values-cs/strings.xml b/java/res/values-cs/strings.xml
index 2d5c386..271208d 100644
--- a/java/res/values-cs/strings.xml
+++ b/java/res/values-cs/strings.xml
@@ -84,8 +84,10 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Chcete-li slyšet, které klávesy jste při zadávání hesla stiskli, připojte sluchátka."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"Aktuální text je %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"Není zadán žádný text"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"Klávesou <xliff:g id="KEY">%1$s</xliff:g> opravíte <xliff:g id="ORIGINAL">%2$s</xliff:g> na <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"Klávese <xliff:g id="KEY">%1$s</xliff:g> je přiřazena automatická oprava"</string>
+    <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+    <skip />
+    <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+    <skip />
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Kód klávesy %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Klávesa Shift je zapnutá (vypnete ji klepnutím)."</string>
diff --git a/java/res/values-da/strings.xml b/java/res/values-da/strings.xml
index 0a53691..0439aaf 100644
--- a/java/res/values-da/strings.xml
+++ b/java/res/values-da/strings.xml
@@ -84,8 +84,10 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Tilslut et headset for at høre indtastningen blive læst højt ved angivelse af adgangskode."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"Nuværende tekst er %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"Der er ingen indtastet tekst"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> retter <xliff:g id="ORIGINAL">%2$s</xliff:g> til <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> udfører automatisk rettelse"</string>
+    <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+    <skip />
+    <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+    <skip />
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Tastekode %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Shift-tast"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Skift er slået til (tryk for at deaktivere)"</string>
diff --git a/java/res/values-de/strings.xml b/java/res/values-de/strings.xml
index 1485cc6..28d82ae 100644
--- a/java/res/values-de/strings.xml
+++ b/java/res/values-de/strings.xml
@@ -84,8 +84,8 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Schließen Sie ein Headset an, um das Passwort gesprochen zu hören."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"Aktueller Text lautet %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"Kein Text eingegeben"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"Mit <xliff:g id="KEY">%1$s</xliff:g> wird <xliff:g id="ORIGINAL">%2$s</xliff:g> in <xliff:g id="CORRECTED">%3$s</xliff:g> korrigiert."</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"Mit <xliff:g id="KEY">%1$s</xliff:g> wird automatisch korrigiert."</string>
+    <string name="spoken_auto_correct" msgid="8005997889020109763">"Mit <xliff:g id="KEY">%1$s</xliff:g> wird <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> in <xliff:g id="CORRECTED">%3$s</xliff:g> korrigiert."</string>
+    <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"Mit <xliff:g id="KEY">%1$s</xliff:g> erfolgt eine Autokorrektur."</string>
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Tastencode %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Umschalttaste"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Umschalttaste aktiviert (zum Deaktivieren berühren)"</string>
diff --git a/java/res/values-el/strings.xml b/java/res/values-el/strings.xml
index 8b6d0d1..54b3c3e 100644
--- a/java/res/values-el/strings.xml
+++ b/java/res/values-el/strings.xml
@@ -84,8 +84,8 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Συνδέστε ένα σετ ακουστικών για να ακούσετε τα πλήκτρα του κωδικού πρόσβασης να εκφωνούνται δυνατά."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"Το τρέχον κείμενο είναι %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"Δεν υπάρχει κείμενο"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"Το πλήκτρο <xliff:g id="KEY">%1$s</xliff:g> διορθώνει το στοιχείο <xliff:g id="ORIGINAL">%2$s</xliff:g> σε <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"Το πλήκτρο <xliff:g id="KEY">%1$s</xliff:g> διαθέτει αυτόματη διόρθωση"</string>
+    <string name="spoken_auto_correct" msgid="8005997889020109763">"<xliff:g id="KEY">%1$s</xliff:g> διορθώνει το <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> σε <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+    <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"<xliff:g id="KEY">%1$s</xliff:g> εκτελεί αυτόματη διόρθωση"</string>
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Κωδικός πλήκτρου %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Το Shift είναι ενεργοποιημένο (πατήστε για απενεργοποίηση)"</string>
diff --git a/java/res/values-en-rGB/strings.xml b/java/res/values-en-rGB/strings.xml
index 1891a3f..bdffb94 100644
--- a/java/res/values-en-rGB/strings.xml
+++ b/java/res/values-en-rGB/strings.xml
@@ -84,8 +84,8 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Plug in a headset to hear password keys spoken aloud."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"Current text is %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"No text entered"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> corrects <xliff:g id="ORIGINAL">%2$s</xliff:g> to <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> has auto-correction"</string>
+    <string name="spoken_auto_correct" msgid="8005997889020109763">"<xliff:g id="KEY">%1$s</xliff:g> corrects <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> to <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+    <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"<xliff:g id="KEY">%1$s</xliff:g> performs auto-correction"</string>
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Key code %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift on (tap to disable)"</string>
diff --git a/java/res/values-en-rIN/strings.xml b/java/res/values-en-rIN/strings.xml
index 1891a3f..bdffb94 100644
--- a/java/res/values-en-rIN/strings.xml
+++ b/java/res/values-en-rIN/strings.xml
@@ -84,8 +84,8 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Plug in a headset to hear password keys spoken aloud."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"Current text is %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"No text entered"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> corrects <xliff:g id="ORIGINAL">%2$s</xliff:g> to <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> has auto-correction"</string>
+    <string name="spoken_auto_correct" msgid="8005997889020109763">"<xliff:g id="KEY">%1$s</xliff:g> corrects <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> to <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+    <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"<xliff:g id="KEY">%1$s</xliff:g> performs auto-correction"</string>
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Key code %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift on (tap to disable)"</string>
diff --git a/java/res/values-es-rUS/strings.xml b/java/res/values-es-rUS/strings.xml
index 331eb38..2d7872a 100644
--- a/java/res/values-es-rUS/strings.xml
+++ b/java/res/values-es-rUS/strings.xml
@@ -84,8 +84,8 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Enchufa tus auriculares para escuchar en voz alta qué teclas presionas al ingresar una contraseña."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"El texto actual es %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"No se ingresó texto."</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"La tecla <xliff:g id="KEY">%1$s</xliff:g> corrige <xliff:g id="ORIGINAL">%2$s</xliff:g> por <xliff:g id="CORRECTED">%3$s</xliff:g>."</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"La tecla <xliff:g id="KEY">%1$s</xliff:g> corrige automáticamente."</string>
+    <string name="spoken_auto_correct" msgid="8005997889020109763">"La tecla <xliff:g id="KEY">%1$s</xliff:g> corrige <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> por <xliff:g id="CORRECTED">%3$s</xliff:g>."</string>
+    <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"La tecla <xliff:g id="KEY">%1$s</xliff:g> corrige automáticamente."</string>
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Clave de código %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Mayús"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Se activó el modo Mayúscula (toca para desactivarlo)."</string>
diff --git a/java/res/values-es/strings.xml b/java/res/values-es/strings.xml
index 7506970..4dfc57f 100644
--- a/java/res/values-es/strings.xml
+++ b/java/res/values-es/strings.xml
@@ -62,7 +62,7 @@
     <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"No sugerir palabras potencialmente ofensivas"</string>
     <string name="auto_correction" msgid="7630720885194996950">"Autocorrección"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Pulsar la tecla de espacio o punto para corregir errores"</string>
-    <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Desactivada"</string>
+    <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"No"</string>
     <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Parcial"</string>
     <string name="auto_correction_threshold_mode_aggressive" msgid="7319007299148899623">"Total"</string>
     <string name="auto_correction_threshold_mode_very_aggressive" msgid="1853309024129480416">"Casi total"</string>
@@ -84,8 +84,8 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Conecta un auricular para escuchar las contraseñas en voz alta."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"El texto actual es %s."</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"No se ha introducido texto."</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"La tecla <xliff:g id="KEY">%1$s</xliff:g> corrige <xliff:g id="ORIGINAL">%2$s</xliff:g> a <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"La tecla <xliff:g id="KEY">%1$s</xliff:g> corrige automáticamente"</string>
+    <string name="spoken_auto_correct" msgid="8005997889020109763">"La tecla <xliff:g id="KEY">%1$s</xliff:g> corrige <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> a <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+    <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"La tecla <xliff:g id="KEY">%1$s</xliff:g> corrige automáticamente"</string>
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Código del teclado: %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Mayús"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Mayúsculas activadas (tocar para inhabilitar)"</string>
@@ -126,7 +126,7 @@
     <string name="voice_input" msgid="3583258583521397548">"Tecla de entrada de voz"</string>
     <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"En teclado principal"</string>
     <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"En teclado de símbolos"</string>
-    <string name="voice_input_modes_off" msgid="3745699748218082014">"Desactivada"</string>
+    <string name="voice_input_modes_off" msgid="3745699748218082014">"No"</string>
     <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Micrófono en teclado principal"</string>
     <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Micrófono en teclado de símbolos"</string>
     <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Entrada de voz inhabilitada"</string>
diff --git a/java/res/values-et-rEE/strings.xml b/java/res/values-et-rEE/strings.xml
index c7e6fe9..3a27a88 100644
--- a/java/res/values-et-rEE/strings.xml
+++ b/java/res/values-et-rEE/strings.xml
@@ -84,8 +84,8 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Ühendage peakomplekt, et kuulata paroole."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"Praegune tekst on %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"Teksti ei ole sisestatud"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> parandab valiku <xliff:g id="ORIGINAL">%2$s</xliff:g> valikuks <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"Klahv <xliff:g id="KEY">%1$s</xliff:g> rakendab automaatse paranduse"</string>
+    <string name="spoken_auto_correct" msgid="8005997889020109763">"Klahvi <xliff:g id="KEY">%1$s</xliff:g> vajutamisel parandatakse sõna <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> sõnaks <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+    <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"Klahvi <xliff:g id="KEY">%1$s</xliff:g> vajutamisel tehakse automaatne parandus"</string>
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Klahvi kood: %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Tõstuklahv"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Tõstuklahv sees (puudutage keelamiseks)"</string>
diff --git a/java/res/values-fa/strings.xml b/java/res/values-fa/strings.xml
index febe01b..a6d9bba 100644
--- a/java/res/values-fa/strings.xml
+++ b/java/res/values-fa/strings.xml
@@ -86,8 +86,8 @@
     <!-- no translation found for spoken_current_text_is (2485723011272583845) -->
     <skip />
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"متنی وارد نشده است"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g>، ‏<xliff:g id="ORIGINAL">%2$s</xliff:g> را به <xliff:g id="CORRECTED">%3$s</xliff:g> تصحیح می‌کند"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> دارای تصحیح خودکار است"</string>
+    <string name="spoken_auto_correct" msgid="8005997889020109763">"<xliff:g id="KEY">%1$s</xliff:g>، ‏<xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> را به <xliff:g id="CORRECTED">%3$s</xliff:g> تصحیح می‌کند"</string>
+    <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"<xliff:g id="KEY">%1$s</xliff:g> تصحیح خودکار را انجام می‌دهد"</string>
     <!-- String.format failed for translation -->
     <!-- no translation found for spoken_description_unknown (3197434010402179157) -->
     <skip />
diff --git a/java/res/values-fi/strings.xml b/java/res/values-fi/strings.xml
index 8f2caab..9f098a1 100644
--- a/java/res/values-fi/strings.xml
+++ b/java/res/values-fi/strings.xml
@@ -84,8 +84,10 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Liitä kuulokkeet, niin kuulet mitä näppäimiä painat kirjoittaessasi salasanaa."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"Nykyinen teksti on %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"Ei kirjoitettua tekstiä"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> korjaa kohteen <xliff:g id="ORIGINAL">%2$s</xliff:g> kohteeksi <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> suorittaa automaattisen korjauksen"</string>
+    <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+    <skip />
+    <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+    <skip />
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Näppäimen koodi %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Vaihto"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Vaihto päällä (poista käytöstä napauttamalla)"</string>
diff --git a/java/res/values-fr-rCA/strings.xml b/java/res/values-fr-rCA/strings.xml
index fba0298..0d0c082 100644
--- a/java/res/values-fr-rCA/strings.xml
+++ b/java/res/values-fr-rCA/strings.xml
@@ -84,8 +84,8 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Branchez des écouteurs pour entendre l\'énoncé à haute voix des touches lors de la saisie du mot de passe."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"Le texte actuel est %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"Aucun texte saisi"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> corrige <xliff:g id="ORIGINAL">%2$s</xliff:g> en <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> dispose de la fonctionnalité de correction automatique"</string>
+    <string name="spoken_auto_correct" msgid="8005997889020109763">"La touche <xliff:g id="KEY">%1$s</xliff:g> permet de corriger« <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> » par « <xliff:g id="CORRECTED">%3$s</xliff:g> »"</string>
+    <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"La touche <xliff:g id="KEY">%1$s</xliff:g> permet d\'activer la correction automatique"</string>
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Code touche %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Maj"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Touche Maj activée (appuyer pour désactiver)"</string>
diff --git a/java/res/values-fr/strings.xml b/java/res/values-fr/strings.xml
index 9d6c8f4..0f92379 100644
--- a/java/res/values-fr/strings.xml
+++ b/java/res/values-fr/strings.xml
@@ -84,8 +84,10 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Branchez des écouteurs pour entendre l\'énoncé à haute voix des touches lors de la saisie du mot de passe."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"Le texte actuel est %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"Aucun texte saisi"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"La touche <xliff:g id="KEY">%1$s</xliff:g> permet de remplacer \"<xliff:g id="ORIGINAL">%2$s</xliff:g>\" par \"<xliff:g id="CORRECTED">%3$s</xliff:g>\"."</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"La touche <xliff:g id="KEY">%1$s</xliff:g> permet d\'activer la correction automatique."</string>
+    <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+    <skip />
+    <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+    <skip />
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Code touche %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Maj"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Touche Maj activée (appuyer pour désactiver)"</string>
diff --git a/java/res/values-hi/strings.xml b/java/res/values-hi/strings.xml
index 0e97296..c013e5f 100644
--- a/java/res/values-hi/strings.xml
+++ b/java/res/values-hi/strings.xml
@@ -84,8 +84,10 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"ज़ोर से बोली गई पासवर्ड कुंजियां सुनने के लिए हेडसेट प्‍लग इन करें."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"वर्तमान पाठ %s है"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"कोई पाठ दर्ज नहीं किया गया"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g>, <xliff:g id="ORIGINAL">%2$s</xliff:g> को सुधारकर <xliff:g id="CORRECTED">%3$s</xliff:g> बना देती है"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> से स्‍वत: सुधार होगा"</string>
+    <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+    <skip />
+    <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+    <skip />
     <string name="spoken_description_unknown" msgid="3197434010402179157">"कुंजी कोड %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"शिफ़्ट"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift चालू (अक्षम करने के लिए टैप करें)"</string>
diff --git a/java/res/values-hr/strings.xml b/java/res/values-hr/strings.xml
index f5c9ad5..1928fb9 100644
--- a/java/res/values-hr/strings.xml
+++ b/java/res/values-hr/strings.xml
@@ -84,8 +84,10 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Priključite slušalice da biste čuli tipke zaporke izgovorene naglas."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"Trenutačni tekst je %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"Nije unesen tekst"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> ispravlja <xliff:g id="ORIGINAL">%2$s</xliff:g> u <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> ima samoispravljanje"</string>
+    <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+    <skip />
+    <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+    <skip />
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Kôd tipke %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Uključena tipka Shift (dotaknite da onemogućite)"</string>
diff --git a/java/res/values-hu/strings.xml b/java/res/values-hu/strings.xml
index 5f4a181..1a07772 100644
--- a/java/res/values-hu/strings.xml
+++ b/java/res/values-hu/strings.xml
@@ -84,8 +84,8 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Csatlakoztasson egy headsetet, ha hallani szeretné a jelszót felolvasva."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"A jelenlegi szöveg: %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"Szöveg nincs megadva"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> gomb: a(z) <xliff:g id="ORIGINAL">%2$s</xliff:g> értéket <xliff:g id="CORRECTED">%3$s</xliff:g> értékre javítja"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"Automatikus javítás van beállítva a következőhöz: <xliff:g id="KEY">%1$s</xliff:g>"</string>
+    <string name="spoken_auto_correct" msgid="8005997889020109763">"<xliff:g id="KEY">%1$s</xliff:g> billentyű: <xliff:g id="CORRECTED">%3$s</xliff:g> szóra javítja a következőt: <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g>"</string>
+    <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"<xliff:g id="KEY">%1$s</xliff:g> billentyű: automatikus javítás"</string>
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Billentyűkód: %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift be van kapcsolva (érintse meg a kikapcsoláshoz)"</string>
diff --git a/java/res/values-hy-rAM/strings.xml b/java/res/values-hy-rAM/strings.xml
index 57c0e61..f584cfa 100644
--- a/java/res/values-hy-rAM/strings.xml
+++ b/java/res/values-hy-rAM/strings.xml
@@ -84,8 +84,10 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Միացրեք ականջակալը՝ բարձրաձայն արտասանվող գաղտնաբառը լսելու համար:"</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"Տվյալ տեքստը %s է"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"Տեքստ չի մուտքագրվել"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g>-ը շտկում է <xliff:g id="ORIGINAL">%2$s</xliff:g>-ը և դարձնում <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g>-ն ունի ինքնուրույն շտկում"</string>
+    <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+    <skip />
+    <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+    <skip />
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Բանալու կոդը՝ %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift-ը միացված է (հպել անջատելու համար)"</string>
diff --git a/java/res/values-in/strings.xml b/java/res/values-in/strings.xml
index acd8394..db21673 100644
--- a/java/res/values-in/strings.xml
+++ b/java/res/values-in/strings.xml
@@ -84,8 +84,10 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Pasang headset untuk mendengar tombol sandi yang diucapkan dengan keras."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"Teks saat ini adalah %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"Tidak ada teks yang dimasukkan"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> mengoreksi <xliff:g id="ORIGINAL">%2$s</xliff:g> menjadi <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> memiliki koreksi otomatis"</string>
+    <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+    <skip />
+    <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+    <skip />
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Kode tombol %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift hidup (ketuk untuk mematikan)"</string>
diff --git a/java/res/values-it/strings.xml b/java/res/values-it/strings.xml
index 2357aa2..ec27fd5 100644
--- a/java/res/values-it/strings.xml
+++ b/java/res/values-it/strings.xml
@@ -62,7 +62,7 @@
     <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Non suggerire parole potenzialmente offensive"</string>
     <string name="auto_correction" msgid="7630720885194996950">"Correzione automatica"</string>
     <string name="auto_correction_summary" msgid="5625751551134658006">"Barra spaziatrice/punteggiatura correggono parole con errori"</string>
-    <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Off"</string>
+    <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"OFF"</string>
     <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Media"</string>
     <string name="auto_correction_threshold_mode_aggressive" msgid="7319007299148899623">"Molto elevata"</string>
     <string name="auto_correction_threshold_mode_very_aggressive" msgid="1853309024129480416">"Massima"</string>
@@ -84,8 +84,8 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Collega gli auricolari per ascoltare la pronuncia dei tasti premuti per la password."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"Il testo attuale è %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"Nessun testo inserito"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> corregge <xliff:g id="ORIGINAL">%2$s</xliff:g> con <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> ha la funzione di correzione automatica"</string>
+    <string name="spoken_auto_correct" msgid="8005997889020109763">"<xliff:g id="KEY">%1$s</xliff:g> corregge <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> con <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+    <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"<xliff:g id="KEY">%1$s</xliff:g> esegue correzione automatica"</string>
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Codice tasto %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Maiuscolo"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Maiuscolo attivo (tocca per disattivare)"</string>
@@ -126,7 +126,7 @@
     <string name="voice_input" msgid="3583258583521397548">"Tasto input vocale"</string>
     <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Su tastiera principale"</string>
     <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Su tastiera simboli"</string>
-    <string name="voice_input_modes_off" msgid="3745699748218082014">"Non attivo"</string>
+    <string name="voice_input_modes_off" msgid="3745699748218082014">"OFF"</string>
     <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Microfono su tastiera principale"</string>
     <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Microfono su tastiera simboli"</string>
     <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Input vocale disatt."</string>
diff --git a/java/res/values-iw/strings.xml b/java/res/values-iw/strings.xml
index 1ad2c3e..81d2e40 100644
--- a/java/res/values-iw/strings.xml
+++ b/java/res/values-iw/strings.xml
@@ -84,8 +84,8 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"חבר אוזניות כדי לשמוע הקראה של מפתחות סיסמה."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"הטקסט הנוכחי הוא %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"לא הוזן טקסט"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> מתקן את <xliff:g id="ORIGINAL">%2$s</xliff:g> ל-<xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> מבצע תיקון אוטומטי"</string>
+    <string name="spoken_auto_correct" msgid="8005997889020109763">"<xliff:g id="KEY">%1$s</xliff:g> מתקן את <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> ל-<xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+    <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"<xliff:g id="KEY">%1$s</xliff:g> מבצע תיקון אוטומטי"</string>
     <string name="spoken_description_unknown" msgid="3197434010402179157">"קוד מקש %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift פועל (הקש כדי להשבית)"</string>
diff --git a/java/res/values-ja/strings.xml b/java/res/values-ja/strings.xml
index 9cda30b..7e66a7b 100644
--- a/java/res/values-ja/strings.xml
+++ b/java/res/values-ja/strings.xml
@@ -84,8 +84,10 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"パスワードのキーが音声出力されるのでヘッドセットを接続してください。"</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"現在のテキスト:%s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"テキストが入力されていません"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g>は<xliff:g id="ORIGINAL">%2$s</xliff:g>を<xliff:g id="CORRECTED">%3$s</xliff:g>に修正します"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g>で自動修正が実行されます"</string>
+    <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+    <skip />
+    <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+    <skip />
     <string name="spoken_description_unknown" msgid="3197434010402179157">"キーコード:%d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift有効(タップして解除)"</string>
diff --git a/java/res/values-ka-rGE/strings.xml b/java/res/values-ka-rGE/strings.xml
index 3fd1680..b5da8ba 100644
--- a/java/res/values-ka-rGE/strings.xml
+++ b/java/res/values-ka-rGE/strings.xml
@@ -84,8 +84,10 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"შეაერთეთ ყურსაცვამი, რათა მოისმინოთ აკრეფილი პაროლის კლავიშების სახელები."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"მიმდინარე ტექსტი არის %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"ტექსტი არ შეყვანილა"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> შეასწორებს <xliff:g id="ORIGINAL">%2$s</xliff:g>-ს <xliff:g id="CORRECTED">%3$s</xliff:g>-ად"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g>-ს ავტოკორექცია გააჩნია"</string>
+    <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+    <skip />
+    <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+    <skip />
     <string name="spoken_description_unknown" msgid="3197434010402179157">"კლავიატურის კოდი %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift ჩართულია (შეეხეთ გამოსართავად)"</string>
diff --git a/java/res/values-km-rKH/strings.xml b/java/res/values-km-rKH/strings.xml
index 34c4269..150ec02 100644
--- a/java/res/values-km-rKH/strings.xml
+++ b/java/res/values-km-rKH/strings.xml
@@ -84,8 +84,10 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"ដោត​កាស ដើម្បី​ស្ដាប់​ពាក្យ​សម្ងាត់។"</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"អត្ថបទ​បច្ចុប្បន្ន​គឺ %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"គ្មាន​អត្ថបទ​​​បាន​បញ្ចូល"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> កែ <xliff:g id="ORIGINAL">%2$s</xliff:g> ទៅ <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> មាន​ការ​កែ​ស្វ័យ​ប្រវត្តិ"</string>
+    <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+    <skip />
+    <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+    <skip />
     <string name="spoken_description_unknown" msgid="3197434010402179157">"កូដ​គ្រាប់​ចុច %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"បើក Shift (​ប៉ះ​ដើម្បី​បិទ)"</string>
diff --git a/java/res/values-ko/strings.xml b/java/res/values-ko/strings.xml
index 630ae7e..2253ce3 100644
--- a/java/res/values-ko/strings.xml
+++ b/java/res/values-ko/strings.xml
@@ -84,8 +84,10 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"비밀번호 키를 음성으로 들으려면 헤드셋을 연결하세요."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"입력한 텍스트: %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"입력한 텍스트 없음"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g>을(를) 누르면 <xliff:g id="ORIGINAL">%2$s</xliff:g>을(를) <xliff:g id="CORRECTED">%3$s</xliff:g>(으)로 수정합니다."</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g>을(를) 누르면 자동 수정됩니다."</string>
+    <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+    <skip />
+    <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+    <skip />
     <string name="spoken_description_unknown" msgid="3197434010402179157">"키 코드 %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"시프트 키"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift 사용(사용하지 않으려면 탭하세요.)"</string>
diff --git a/java/res/values-lo-rLA/strings.xml b/java/res/values-lo-rLA/strings.xml
index 0d7f7a2..1597f92 100644
--- a/java/res/values-lo-rLA/strings.xml
+++ b/java/res/values-lo-rLA/strings.xml
@@ -84,8 +84,8 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"ສຽບສາຍຫູຟັງເພື່ອຟັງລະຫັດຜ່ານ."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"ຂໍ້ຄວາມປະຈຸບັນແມ່ນ %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"ບໍ່ມີການໃສ່ຂໍ້ຄວາມ"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> ຖືກແປງຈາກ <xliff:g id="ORIGINAL">%2$s</xliff:g> ເປັນ <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> ບໍ່ມີການກວດຄຳຖືກອັດຕະໂນມັດ"</string>
+    <string name="spoken_auto_correct" msgid="8005997889020109763">"<xliff:g id="KEY">%1$s</xliff:g> ແກ້ໄຂ <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> ເປັນ <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+    <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"<xliff:g id="KEY">%1$s</xliff:g> ປະຕິບັດການແປງຄຳຜິດອັດຕະໂນມັດ"</string>
     <string name="spoken_description_unknown" msgid="3197434010402179157">"ລະຫັດກະແຈ %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift ເປີດນຳໃຊ້ຢູ່ (ກົດເພື່ອປິດນຳໃຊ້)"</string>
diff --git a/java/res/values-lt/strings.xml b/java/res/values-lt/strings.xml
index d40b54b..1cb84d0 100644
--- a/java/res/values-lt/strings.xml
+++ b/java/res/values-lt/strings.xml
@@ -84,8 +84,8 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Prijunkite ausines, kad išgirstumėte sakomus slaptažodžio klavišus."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"Dabartinis tekstas yra %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"Nėra įvesto teksto"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"„<xliff:g id="KEY">%1$s</xliff:g>“ pataiso „<xliff:g id="ORIGINAL">%2$s</xliff:g>“ į „<xliff:g id="CORRECTED">%3$s</xliff:g>“"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"„<xliff:g id="KEY">%1$s</xliff:g>“ atlieka automatinį taisymą"</string>
+    <string name="spoken_auto_correct" msgid="8005997889020109763">"<xliff:g id="KEY">%1$s</xliff:g> pataiso „<xliff:g id="ORIGINAL_WORD">%2$s</xliff:g>“ į „<xliff:g id="CORRECTED">%3$s</xliff:g>“"</string>
+    <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"<xliff:g id="KEY">%1$s</xliff:g> atlieka automatinį taisymą"</string>
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Klavišo kodas %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Antrojo lygio klavišas"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Įjungtas antrasis lygis (palieskite, kad išjungtumėte)"</string>
diff --git a/java/res/values-lv/strings.xml b/java/res/values-lv/strings.xml
index cde9c34..221cc2e 100644
--- a/java/res/values-lv/strings.xml
+++ b/java/res/values-lv/strings.xml
@@ -84,8 +84,8 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Pievienojiet austiņas, lai dzirdētu paroles rakstzīmes."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"Pašreizējais teksts ir %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"Nav ievadīts teksts"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"Nospiežot taustiņu <xliff:g id="KEY">%1$s</xliff:g>, “<xliff:g id="ORIGINAL">%2$s</xliff:g>” tiek labots uz “<xliff:g id="CORRECTED">%3$s</xliff:g>”."</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"Taustiņam <xliff:g id="KEY">%1$s</xliff:g> ir automātiskas labošanas funkcija."</string>
+    <string name="spoken_auto_correct" msgid="8005997889020109763">"Nospiežot taustiņu <xliff:g id="KEY">%1$s</xliff:g>, “<xliff:g id="ORIGINAL_WORD">%2$s</xliff:g>” tiek labots uz “<xliff:g id="CORRECTED">%3$s</xliff:g>”."</string>
+    <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"Taustiņam <xliff:g id="KEY">%1$s</xliff:g> ir automātiskas labošanas funkcija."</string>
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Taustiņu kods %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Pārslēgšanas taustiņš"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Pārslēgšanas taustiņš iespējots (pieskarieties, lai atspējotu)"</string>
diff --git a/java/res/values-mn-rMN/strings.xml b/java/res/values-mn-rMN/strings.xml
index 177b533..05ac4ac 100644
--- a/java/res/values-mn-rMN/strings.xml
+++ b/java/res/values-mn-rMN/strings.xml
@@ -84,8 +84,10 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Нууц үгний товчнуудыг чангаар уншихыг сонсохын тулд чихэвчээ залгана уу."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"Одоогийн текст %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"Текст оруулаагүй"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> нь <xliff:g id="ORIGINAL">%2$s</xliff:g>-г <xliff:g id="CORRECTED">%3$s</xliff:g> болгож залруулна"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> автомат залруулагчтай"</string>
+    <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+    <skip />
+    <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+    <skip />
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Товчийн код %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Сэлгэх"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Сэлгэхийг идэвхжүүлсэн (товшиж идэвхгүйжүүлнэ үү)"</string>
diff --git a/java/res/values-ms-rMY/strings.xml b/java/res/values-ms-rMY/strings.xml
index 0e8b4eb..d59791a 100644
--- a/java/res/values-ms-rMY/strings.xml
+++ b/java/res/values-ms-rMY/strings.xml
@@ -84,8 +84,10 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Pasangkan set kepala untuk mendengar kekunci kata laluan disebut dengan kuat."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"Teks semasa adalah %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"Tiada teks dimasukkan"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> membetulkan <xliff:g id="ORIGINAL">%2$s</xliff:g> menjadi <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> mempunyai auto pembetulan"</string>
+    <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+    <skip />
+    <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+    <skip />
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Kod kunci %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Kunci anjak dihidupkan (ketik untuk melumpuhkan)"</string>
diff --git a/java/res/values-nb/strings.xml b/java/res/values-nb/strings.xml
index 1bd91b6..0a2def8 100644
--- a/java/res/values-nb/strings.xml
+++ b/java/res/values-nb/strings.xml
@@ -84,8 +84,8 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Koble til hodetelefoner for å høre opplesing av bokstavene i passordet."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"Gjeldende tekst er %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"Ingen tekst er skrevet inn"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> korrigerer <xliff:g id="ORIGINAL">%2$s</xliff:g> til <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> har automatisk korrigering"</string>
+    <string name="spoken_auto_correct" msgid="8005997889020109763">"<xliff:g id="KEY">%1$s</xliff:g> retter <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> til <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+    <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"<xliff:g id="KEY">%1$s</xliff:g> utfører automatisk retting"</string>
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Tastaturkode %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift er på (trykk for å deaktivere)"</string>
diff --git a/java/res/values-nl/strings.xml b/java/res/values-nl/strings.xml
index b1d1bb3..60db9f0 100644
--- a/java/res/values-nl/strings.xml
+++ b/java/res/values-nl/strings.xml
@@ -84,8 +84,8 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Sluit een headset aan om wachtwoordtoetsen hardop te laten voorlezen."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"Huidige tekst is %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"Geen tekst ingevoerd"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"Met <xliff:g id="KEY">%1$s</xliff:g> wordt <xliff:g id="ORIGINAL">%2$s</xliff:g> gecorrigeerd naar <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"Met <xliff:g id="KEY">%1$s</xliff:g> voert u automatische correctie uit"</string>
+    <string name="spoken_auto_correct" msgid="8005997889020109763">"Met <xliff:g id="KEY">%1$s</xliff:g> wordt <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> gecorrigeerd naar <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+    <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"Met <xliff:g id="KEY">%1$s</xliff:g> voert u automatische correctie uit"</string>
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Toetscode %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift aan (tik om uit te schakelen)"</string>
diff --git a/java/res/values-pl/strings.xml b/java/res/values-pl/strings.xml
index f830b37..c3ed2d8 100644
--- a/java/res/values-pl/strings.xml
+++ b/java/res/values-pl/strings.xml
@@ -84,8 +84,8 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Podłącz zestaw słuchawkowy, aby usłyszeć znaki hasła wypowiadane na głos."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"Aktualny tekst: %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"Nie wprowadzono tekstu"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> poprawia <xliff:g id="ORIGINAL">%2$s</xliff:g> na <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> zapewnia autokorektę"</string>
+    <string name="spoken_auto_correct" msgid="8005997889020109763">"<xliff:g id="KEY">%1$s</xliff:g> poprawia <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> na <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+    <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"<xliff:g id="KEY">%1$s</xliff:g> wykonuje autokorektę"</string>
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Kod klawisza: %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift włączony (kliknij, by wyłączyć)"</string>
diff --git a/java/res/values-pt-rPT/strings.xml b/java/res/values-pt-rPT/strings.xml
index dbf34f9..2a4c2b8 100644
--- a/java/res/values-pt-rPT/strings.xml
+++ b/java/res/values-pt-rPT/strings.xml
@@ -84,8 +84,8 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Ligar auscultadores com microfone integrado para ouvir as teclas da palavra-passe."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"O texto atual é %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"Nenhum texto digitado"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> corrige <xliff:g id="ORIGINAL">%2$s</xliff:g> para <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> tem correção automática"</string>
+    <string name="spoken_auto_correct" msgid="8005997889020109763">"<xliff:g id="KEY">%1$s</xliff:g> corrige <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> para <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+    <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"<xliff:g id="KEY">%1$s</xliff:g> executa correção automática"</string>
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Código da tecla %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift ativado (tocar para desativar)"</string>
diff --git a/java/res/values-pt/strings.xml b/java/res/values-pt/strings.xml
index 3f98372..8cc0718 100644
--- a/java/res/values-pt/strings.xml
+++ b/java/res/values-pt/strings.xml
@@ -84,8 +84,10 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Conecte um fone de ouvido para ouvir as chaves de senha em voz alta."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"O texto atual é %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"Nenhum texto digitado"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> corrige <xliff:g id="ORIGINAL">%2$s</xliff:g> para <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> possui correção automática"</string>
+    <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+    <skip />
+    <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+    <skip />
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Código de tecla %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift ativado (toque para desativar)"</string>
diff --git a/java/res/values-rm/strings.xml b/java/res/values-rm/strings.xml
index c68d30c..b956619 100644
--- a/java/res/values-rm/strings.xml
+++ b/java/res/values-rm/strings.xml
@@ -139,9 +139,9 @@
     <skip />
     <!-- no translation found for spoken_no_text_entered (7479685225597344496) -->
     <skip />
-    <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
+    <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
     <skip />
-    <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
+    <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
     <skip />
     <!-- no translation found for spoken_description_unknown (3197434010402179157) -->
     <skip />
diff --git a/java/res/values-ro/strings.xml b/java/res/values-ro/strings.xml
index f67f58e..d8b32e7 100644
--- a/java/res/values-ro/strings.xml
+++ b/java/res/values-ro/strings.xml
@@ -84,8 +84,10 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Conectaţi un set căşti-microfon pentru a auzi tastele apăsate când introduceţi parola."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"Textul curent este %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"Nu a fost introdus text"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> corectează <xliff:g id="ORIGINAL">%2$s</xliff:g> cu <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> dispune de corectare automată"</string>
+    <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+    <skip />
+    <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+    <skip />
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Tasta cu codul %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Tasta Shift este activată (apăsaţi pentru a o dezactiva)"</string>
diff --git a/java/res/values-ru/strings.xml b/java/res/values-ru/strings.xml
index 20f358a..d27207c 100644
--- a/java/res/values-ru/strings.xml
+++ b/java/res/values-ru/strings.xml
@@ -84,8 +84,10 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Подключите гарнитуру, чтобы услышать пароль."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"Введенный текст: %s."</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"Текст не введен"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"При нажатии клавиши \"<xliff:g id="KEY">%1$s</xliff:g>\" слово \"<xliff:g id="ORIGINAL">%2$s</xliff:g>\" будет исправлено на \"<xliff:g id="CORRECTED">%3$s</xliff:g>\""</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"Для клавиши \"<xliff:g id="KEY">%1$s</xliff:g>\" назначена операция автоисправления"</string>
+    <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+    <skip />
+    <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+    <skip />
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Код клавиши:%d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Клавиша верхнего регистра"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Верхний регистр включен (нажмите, чтобы отключить)"</string>
diff --git a/java/res/values-sk/strings.xml b/java/res/values-sk/strings.xml
index 3f6706c..4ca07c9 100644
--- a/java/res/values-sk/strings.xml
+++ b/java/res/values-sk/strings.xml
@@ -84,8 +84,10 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Ak si chcete pri zadávaní hesla vypočuť nahlas vyslovené klávesy, pripojte náhlavnú súpravu."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"Aktuálny text je %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"Nie je zadaný žiadny text"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"Klávesom <xliff:g id="KEY">%1$s</xliff:g> opravíte <xliff:g id="ORIGINAL">%2$s</xliff:g> na <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"Klávesom <xliff:g id="KEY">%1$s</xliff:g> spustíte automatické opravy"</string>
+    <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+    <skip />
+    <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+    <skip />
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Kód klávesu %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Kláves Shift je zapnutý (zakážete ho klepnutím)"</string>
diff --git a/java/res/values-sl/strings.xml b/java/res/values-sl/strings.xml
index 6c8115e..34bbe91 100644
--- a/java/res/values-sl/strings.xml
+++ b/java/res/values-sl/strings.xml
@@ -84,8 +84,10 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Priključite slušalke, če želite slišati izgovorjene tipke gesla."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"Trenutno besedilo je %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"Ni vnesenega besedila"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"Tipka <xliff:g id="KEY">%1$s</xliff:g> popravi <xliff:g id="ORIGINAL">%2$s</xliff:g> v <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"Tipka <xliff:g id="KEY">%1$s</xliff:g> izvede samodejno popravljanje"</string>
+    <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+    <skip />
+    <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+    <skip />
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Koda tipke %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift je vklopljen (dotaknite se, da onemogočite)"</string>
diff --git a/java/res/values-sr/strings.xml b/java/res/values-sr/strings.xml
index 92e465c..4d5df50 100644
--- a/java/res/values-sr/strings.xml
+++ b/java/res/values-sr/strings.xml
@@ -84,8 +84,10 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Укључите слушалице да бисте чули наглас изговорене тастере за лозинку."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"Тренутни текст је %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"Текст није унет"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> исправља <xliff:g id="ORIGINAL">%2$s</xliff:g> у <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> има функцију аутоматског исправљања"</string>
+    <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+    <skip />
+    <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+    <skip />
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Кôд тастера %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift је укључен (додирните да бисте га онемогућили)"</string>
diff --git a/java/res/values-sv/strings.xml b/java/res/values-sv/strings.xml
index 009e829..84ee1d8 100644
--- a/java/res/values-sv/strings.xml
+++ b/java/res/values-sv/strings.xml
@@ -84,8 +84,10 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Anslut hörlurar om du vill att lösenordet ska läsas upp."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"Nuvarande text är %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"Ingen text har angetts"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> ändrar <xliff:g id="ORIGINAL">%2$s</xliff:g> till <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"Automatisk korrigering används för <xliff:g id="KEY">%1$s</xliff:g>"</string>
+    <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+    <skip />
+    <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+    <skip />
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Nyckelkod %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Skift"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Skift på (knacka lätt för att inaktivera)"</string>
diff --git a/java/res/values-sw/strings.xml b/java/res/values-sw/strings.xml
index a6ada5b..2f8f85e 100644
--- a/java/res/values-sw/strings.xml
+++ b/java/res/values-sw/strings.xml
@@ -84,8 +84,8 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Chomeka plagi ya kifaa cha kichwa cha kusikiza ili kusikiliza msimbo wa nenosiri inayozungumwa kwa sauti ya juu."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"Maandishi ya sasa ni %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"Hakuna maandishi yaliyoingizwa"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> hurekebisha <xliff:g id="ORIGINAL">%2$s</xliff:g> hadi <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> ina urekebishaji wa kiotomatiki"</string>
+    <string name="spoken_auto_correct" msgid="8005997889020109763">"<xliff:g id="KEY">%1$s</xliff:g> hurekebisha <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> kuwa <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+    <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"<xliff:g id="KEY">%1$s</xliff:g> hutekeleza urekebishaji wa kiotomatiki"</string>
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Msimbo wa kitufe %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Badilisha"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift imewashwa (gonga ili kulemaza)"</string>
diff --git a/java/res/values-sw600dp-land/dimens.xml b/java/res/values-sw600dp-land/dimens.xml
index d067265..d79e8ca 100644
--- a/java/res/values-sw600dp-land/dimens.xml
+++ b/java/res/values-sw600dp-land/dimens.xml
@@ -66,7 +66,7 @@
     <!-- Emoji keyboard -->
     <fraction name="emoji_keyboard_key_width">10%p</fraction>
     <fraction name="emoji_keyboard_row_height">33%p</fraction>
-    <fraction name="emoji_keyboard_key_letter_size">85%p</fraction>
+    <fraction name="emoji_keyboard_key_letter_size">70%p</fraction>
     <integer name="emoji_keyboard_max_key_count">30</integer>
 
 </resources>
diff --git a/java/res/values-sw600dp/dimens.xml b/java/res/values-sw600dp/dimens.xml
index 591355d..b2f4ae0 100644
--- a/java/res/values-sw600dp/dimens.xml
+++ b/java/res/values-sw600dp/dimens.xml
@@ -92,7 +92,7 @@
     <!-- Emoji keyboard -->
     <fraction name="emoji_keyboard_key_width">12.5%p</fraction>
     <fraction name="emoji_keyboard_row_height">33%p</fraction>
-    <fraction name="emoji_keyboard_key_letter_size">76%p</fraction>
+    <fraction name="emoji_keyboard_key_letter_size">60%p</fraction>
     <integer name="emoji_keyboard_max_key_count">24</integer>
 
 </resources>
diff --git a/java/res/values-sw768dp-land/dimens.xml b/java/res/values-sw768dp-land/dimens.xml
index 664630b..ce315b0 100644
--- a/java/res/values-sw768dp-land/dimens.xml
+++ b/java/res/values-sw768dp-land/dimens.xml
@@ -67,7 +67,7 @@
     <!-- Emoji keyboard -->
     <fraction name="emoji_keyboard_key_width">7.69%p</fraction>
     <fraction name="emoji_keyboard_row_height">33%p</fraction>
-    <fraction name="emoji_keyboard_key_letter_size">68%p</fraction>
+    <fraction name="emoji_keyboard_key_letter_size">60%p</fraction>
     <integer name="emoji_keyboard_max_key_count">39</integer>
 
 </resources>
diff --git a/java/res/values-sw768dp/dimens.xml b/java/res/values-sw768dp/dimens.xml
index 1fd933a..c90da7f 100644
--- a/java/res/values-sw768dp/dimens.xml
+++ b/java/res/values-sw768dp/dimens.xml
@@ -92,7 +92,7 @@
     <!-- Emoji keyboard -->
     <fraction name="emoji_keyboard_key_width">10%p</fraction>
     <fraction name="emoji_keyboard_row_height">33%p</fraction>
-    <fraction name="emoji_keyboard_key_letter_size">76%p</fraction>
+    <fraction name="emoji_keyboard_key_letter_size">68%p</fraction>
     <integer name="emoji_keyboard_max_key_count">30</integer>
 
 </resources>
diff --git a/java/res/values-th/strings.xml b/java/res/values-th/strings.xml
index f2e252d..06fa120 100644
--- a/java/res/values-th/strings.xml
+++ b/java/res/values-th/strings.xml
@@ -84,8 +84,10 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"เสียบชุดหูฟังเพื่อฟังเสียงเมื่อพิมพ์รหัสผ่าน"</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"ข้อความปัจจุบันคือ %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"ไม่มีข้อความ"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> จะแก้ไข <xliff:g id="ORIGINAL">%2$s</xliff:g> เป็น <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> จะมีการแก้ไขอัตโนมัติ"</string>
+    <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+    <skip />
+    <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+    <skip />
     <string name="spoken_description_unknown" msgid="3197434010402179157">"รหัสคีย์ %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift เปิดอยู่ (แตะเพื่อปิดใช้งาน)"</string>
diff --git a/java/res/values-tl/strings.xml b/java/res/values-tl/strings.xml
index 9a35545..f914958 100644
--- a/java/res/values-tl/strings.xml
+++ b/java/res/values-tl/strings.xml
@@ -84,8 +84,10 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Mag-plug in ng headset upang marinig ang mga password key na binabanggit nang malakas."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"Ang kasalukuyang teksto ay %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"Walang tekstong inilagay"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"Itatama ng pagpindot sa <xliff:g id="KEY">%1$s</xliff:g> ang <xliff:g id="ORIGINAL">%2$s</xliff:g> at gagawing <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"May awtomatikong pagwasto ang <xliff:g id="KEY">%1$s</xliff:g>"</string>
+    <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+    <skip />
+    <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+    <skip />
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Code ng key %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Naka-on ang shift (i-tap upang huwag paganahin)"</string>
diff --git a/java/res/values-tr/strings.xml b/java/res/values-tr/strings.xml
index ab376e1..bd20e56 100644
--- a/java/res/values-tr/strings.xml
+++ b/java/res/values-tr/strings.xml
@@ -84,8 +84,10 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Şifre tuşlarının sesli okunmasını dinlemek için mikrofonlu kulaklık takın."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"Mevcut metin: %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"Hiç metin girilmedi"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> tuşuna basıldığında <xliff:g id="ORIGINAL">%2$s</xliff:g>, <xliff:g id="CORRECTED">%3$s</xliff:g> olarak düzeltilir"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> tuşunda otomatik düzeltme işlevi var"</string>
+    <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+    <skip />
+    <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+    <skip />
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Tuş kodu: %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Üst Karakter"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Üst karakter açık (devre dışı bırakmak için hafifçe vurun)"</string>
diff --git a/java/res/values-uk/strings.xml b/java/res/values-uk/strings.xml
index 2c51652..2a8b937 100644
--- a/java/res/values-uk/strings.xml
+++ b/java/res/values-uk/strings.xml
@@ -84,8 +84,8 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Підключіть гарнітуру, щоб прослухати відтворені вголос символи пароля."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"Поточний текст – %s."</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"Текст не введено"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> виправляє <xliff:g id="ORIGINAL">%2$s</xliff:g> на <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> має функцію автоматичного виправлення"</string>
+    <string name="spoken_auto_correct" msgid="8005997889020109763">"<xliff:g id="KEY">%1$s</xliff:g> виправляє <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> на <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+    <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"<xliff:g id="KEY">%1$s</xliff:g> здійснює автоматичне виправлення"</string>
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Код клавіші – %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Клавіша Shift"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift увімкнено (швидко торкніться, щоб вимкнути)"</string>
diff --git a/java/res/values-v19/emoji-categories.xml b/java/res/values-v19/emoji-categories.xml
index 0d5aa1a..51aad6e 100644
--- a/java/res/values-v19/emoji-categories.xml
+++ b/java/res/values-v19/emoji-categories.xml
@@ -214,7 +214,7 @@
         <item>fe835|0038,20e3|99</item> <!-- TODO: fix support min sdk version (99) -->
         <item>fe836|0039,20e3|99</item> <!-- TODO: fix support min sdk version (99) -->
         <item>fe837|0030,20e3|99</item> <!-- TODO: fix support min sdk version (99) -->
-        <item>1f51f</item>
+        <item>1f51f||99</item> <!-- TODO: fix support min sdk version (99) -->
         <item>fe82c|0023,20e3|99</item> <!-- TODO: fix support min sdk version (99) -->
         <item>1f51d</item>
         <item>1f519</item>
diff --git a/java/res/values-vi/strings.xml b/java/res/values-vi/strings.xml
index 64b804a..c103e96 100644
--- a/java/res/values-vi/strings.xml
+++ b/java/res/values-vi/strings.xml
@@ -84,8 +84,10 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Cắm tai nghe để nghe mật khẩu."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"Ký tự hiện tại là %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"Không có ký tự nào được nhập"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> sửa <xliff:g id="ORIGINAL">%2$s</xliff:g> thành <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> có tính năng tự động sửa"</string>
+    <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+    <skip />
+    <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+    <skip />
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Mã phím %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift đang bật (bấm để tắt)"</string>
diff --git a/java/res/values-zh-rCN/strings.xml b/java/res/values-zh-rCN/strings.xml
index 7683c84..6df35f1 100644
--- a/java/res/values-zh-rCN/strings.xml
+++ b/java/res/values-zh-rCN/strings.xml
@@ -84,8 +84,10 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"需要插入耳机才能听到密码的按键声。"</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"当前文本为%s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"未输入文字"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"按<xliff:g id="KEY">%1$s</xliff:g>可将<xliff:g id="ORIGINAL">%2$s</xliff:g>更正为<xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"按<xliff:g id="KEY">%1$s</xliff:g>可执行自动更正"</string>
+    <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+    <skip />
+    <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+    <skip />
     <string name="spoken_description_unknown" msgid="3197434010402179157">"键码为 %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift 模式已启用(点按即可停用)"</string>
diff --git a/java/res/values-zh-rHK/strings.xml b/java/res/values-zh-rHK/strings.xml
index b18e0d9..194136f 100644
--- a/java/res/values-zh-rHK/strings.xml
+++ b/java/res/values-zh-rHK/strings.xml
@@ -84,8 +84,10 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"插上耳機即可聽到系統朗讀密碼鍵。"</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"目前文字為 %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"未輸入文字"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"按「<xliff:g id="KEY">%1$s</xliff:g>」可將「<xliff:g id="ORIGINAL">%2$s</xliff:g>」修正為「<xliff:g id="CORRECTED">%3$s</xliff:g>」"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"「<xliff:g id="KEY">%1$s</xliff:g>」鍵具自動修正功能"</string>
+    <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+    <skip />
+    <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+    <skip />
     <string name="spoken_description_unknown" msgid="3197434010402179157">"按鍵代碼 %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Shift 鍵"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift 鍵已開啟 (輕按即可停用)"</string>
diff --git a/java/res/values-zh-rTW/strings.xml b/java/res/values-zh-rTW/strings.xml
index ef3c833..e9853e2 100644
--- a/java/res/values-zh-rTW/strings.xml
+++ b/java/res/values-zh-rTW/strings.xml
@@ -84,8 +84,10 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"連接耳機即可聽取系統朗讀密碼按鍵。"</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"目前文字為 %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"未輸入文字"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"按下「<xliff:g id="KEY">%1$s</xliff:g>」可將「<xliff:g id="ORIGINAL">%2$s</xliff:g>」修正為「<xliff:g id="CORRECTED">%3$s</xliff:g>」"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"按下「<xliff:g id="KEY">%1$s</xliff:g>」可執行自動修正"</string>
+    <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+    <skip />
+    <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+    <skip />
     <string name="spoken_description_unknown" msgid="3197434010402179157">"按鍵代碼 %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Shift 鍵"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift 鍵已開啟 (輕按即可停用)"</string>
diff --git a/java/res/values-zu/strings.xml b/java/res/values-zu/strings.xml
index 2dafde9..d245c32 100644
--- a/java/res/values-zu/strings.xml
+++ b/java/res/values-zu/strings.xml
@@ -84,8 +84,8 @@
     <string name="spoken_use_headphones" msgid="896961781287283493">"Plaka ku-headset ukuze uzwe okhiye bephasiwedi ezindlebeni zakho bezwakala kakhulu."</string>
     <string name="spoken_current_text_is" msgid="2485723011272583845">"Umbhalo wamanje ngu %s"</string>
     <string name="spoken_no_text_entered" msgid="7479685225597344496">"Awukho umbhalo ofakiwe"</string>
-    <string name="spoken_auto_correct" msgid="5381764628886369268">"I-<xliff:g id="KEY">%1$s</xliff:g> ilungisa i-<xliff:g id="ORIGINAL">%2$s</xliff:g> ibe yi-<xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
-    <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"I-<xliff:g id="KEY">%1$s</xliff:g> inokulungiswa okuzenzakalelayo"</string>
+    <string name="spoken_auto_correct" msgid="8005997889020109763">"I-<xliff:g id="KEY">%1$s</xliff:g> ilungisa i-<xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> ibe yi-<xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+    <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"I-<xliff:g id="KEY">%1$s</xliff:g> yenza ukulungiswa kokuzenzakalela"</string>
     <string name="spoken_description_unknown" msgid="3197434010402179157">"Ikhodi yokhiye %d"</string>
     <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
     <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"U-Shift uvuliwe (thepha ukuwuvimbela)"</string>
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index 0978214..31945d0 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -26,8 +26,8 @@
         <attr name="keyboardViewStyle" format="reference" />
         <!-- MainKeyboardView style -->
         <attr name="mainKeyboardViewStyle" format="reference" />
-        <!-- EmojiKeyboardView style -->
-        <attr name="emojiKeyboardViewStyle" format="reference" />
+        <!-- EmojiPalettesView style -->
+        <attr name="emojiPalettesViewStyle" format="reference" />
         <!-- MoreKeysKeyboard style -->
         <attr name="moreKeysKeyboardStyle" format="reference" />
         <!-- MoreKeysKeyboardView style -->
@@ -167,7 +167,7 @@
         <attr name="suppressKeyPreviewAfterBatchInputDuration" format="integer" />
     </declare-styleable>
 
-    <declare-styleable name="EmojiKeyboardView">
+    <declare-styleable name="EmojiPalettesView">
         <attr name="emojiTabLabelColor" format="reference" />
     </declare-styleable>
 
diff --git a/java/res/values/config.xml b/java/res/values/config.xml
index 465d52c..66b9b70 100644
--- a/java/res/values/config.xml
+++ b/java/res/values/config.xml
@@ -103,7 +103,7 @@
      -->
     <string-array name="auto_correction_threshold_values" translatable="false">
         <!-- Off, When auto correction setting is Off, this value is not used. -->
-        <item></item>
+        <item>floatMaxValue</item>
         <!-- Modest : Suggestion whose normalized score is greater than this value
              will be subject to auto-correction. -->
         <item>0.185</item>
diff --git a/java/res/values/dimens.xml b/java/res/values/dimens.xml
index 2d626db..18cb262 100644
--- a/java/res/values/dimens.xml
+++ b/java/res/values/dimens.xml
@@ -118,7 +118,7 @@
     <!-- Emoji keyboard -->
     <fraction name="emoji_keyboard_key_width">14.2857%p</fraction>
     <fraction name="emoji_keyboard_row_height">33%p</fraction>
-    <fraction name="emoji_keyboard_key_letter_size">81%p</fraction>
+    <fraction name="emoji_keyboard_key_letter_size">68%p</fraction>
     <integer name="emoji_keyboard_max_key_count">21</integer>
     <dimen name="emoji_category_page_id_height">3dp</dimen>
 
diff --git a/java/res/values/themes-common.xml b/java/res/values/themes-common.xml
index 8e9cfc9..3760771 100644
--- a/java/res/values/themes-common.xml
+++ b/java/res/values/themes-common.xml
@@ -104,10 +104,10 @@
     <style
         name="MainKeyboardView"
         parent="KeyboardView" />
-    <!-- Though {@link EmojiKeyboardView} doesn't extend {@link KeyboardView}, some views inside it,
+    <!-- Though {@link EmojiPalettesView} doesn't extend {@link KeyboardView}, some views inside it,
          for instance delete button, need themed {@link KeyboardView} attributes. -->
     <style
-        name="EmojiKeyboardView"
+        name="EmojiPalettesView"
         parent="KeyboardView"
     >
         <item name="emojiTabLabelColor">@color/emoji_tab_label_color_ics</item>
diff --git a/java/res/values/themes-gb.xml b/java/res/values/themes-gb.xml
index d9ac4ac..f52695f 100644
--- a/java/res/values/themes-gb.xml
+++ b/java/res/values/themes-gb.xml
@@ -23,7 +23,7 @@
         <item name="keyboardStyle">@style/Keyboard.GB</item>
         <item name="keyboardViewStyle">@style/KeyboardView.GB</item>
         <item name="mainKeyboardViewStyle">@style/MainKeyboardView.GB</item>
-        <item name="emojiKeyboardViewStyle">@style/EmojiKeyboardView.GB</item>
+        <item name="emojiPalettesViewStyle">@style/EmojiPalettesView.GB</item>
         <item name="moreKeysKeyboardStyle">@style/MoreKeysKeyboard.GB</item>
         <item name="moreKeysKeyboardViewStyle">@style/MoreKeysKeyboardView.GB</item>
         <item name="moreKeysKeyboardContainerStyle">@style/MoreKeysKeyboardContainer.GB</item>
@@ -96,10 +96,10 @@
         <item name="spacebarTextColor">@color/spacebar_text_color_gb</item>
         <item name="spacebarTextShadowColor">@color/spacebar_text_shadow_color_gb</item>
     </style>
-    <!-- Though {@link EmojiKeyboardView} doesn't extend {@link KeyboardView}, some views inside it,
+    <!-- Though {@link EmojiPalettesView} doesn't extend {@link KeyboardView}, some views inside it,
          for instance delete button, need themed {@link KeyboardView} attributes. -->
     <style
-        name="EmojiKeyboardView.GB"
+        name="EmojiPalettesView.GB"
         parent="KeyboardView.GB"
     >
         <item name="keyBackground">@drawable/btn_keyboard_key_functional_gb</item>
diff --git a/java/res/values/themes-ics.xml b/java/res/values/themes-ics.xml
index 33dd50c..a77e685 100644
--- a/java/res/values/themes-ics.xml
+++ b/java/res/values/themes-ics.xml
@@ -23,7 +23,7 @@
         <item name="keyboardStyle">@style/Keyboard.ICS</item>
         <item name="keyboardViewStyle">@style/KeyboardView.ICS</item>
         <item name="mainKeyboardViewStyle">@style/MainKeyboardView.ICS</item>
-        <item name="emojiKeyboardViewStyle">@style/EmojiKeyboardView.ICS</item>
+        <item name="emojiPalettesViewStyle">@style/EmojiPalettesView.ICS</item>
         <item name="moreKeysKeyboardStyle">@style/MoreKeysKeyboard.ICS</item>
         <item name="moreKeysKeyboardViewStyle">@style/MoreKeysKeyboardView.ICS</item>
         <item name="moreKeysKeyboardContainerStyle">@style/MoreKeysKeyboardContainer.ICS</item>
@@ -97,10 +97,10 @@
         <item name="spacebarTextColor">@color/spacebar_text_color_ics</item>
         <item name="spacebarTextShadowColor">@color/spacebar_text_shadow_color_ics</item>
     </style>
-    <!-- Though {@link EmojiKeyboardView} doesn't extend {@link KeyboardView}, some views inside it,
+    <!-- Though {@link EmojiPalettesView} doesn't extend {@link KeyboardView}, some views inside it,
          for instance delete button, need themed {@link KeyboardView} attributes. -->
     <style
-        name="EmojiKeyboardView.ICS"
+        name="EmojiPalettesView.ICS"
         parent="KeyboardView.ICS"
     >
         <item name="keyBackgroundEmojiFunctional">@drawable/btn_keyboard_key_functional_ics</item>
diff --git a/java/res/xml/key_styles_enter.xml b/java/res/xml/key_styles_enter.xml
index 5976e95..568c602 100644
--- a/java/res/xml/key_styles_enter.xml
+++ b/java/res/xml/key_styles_enter.xml
@@ -21,11 +21,14 @@
 <merge
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
 >
+    <!-- TODO: Stop using many conditional cases for emoji_key_as_more_key. There are way too many to maintain. -->
     <!-- Navigate more keys style -->
     <switch>
+        <!-- latin:passwordInput="true" -->
         <case
             latin:imeAction="actionNext"
             latin:navigatePrevious="true"
+            latin:passwordInput="true"
         >
             <key-style
                 latin:styleName="navigateMoreKeysStyle"
@@ -35,6 +38,7 @@
         <case
             latin:imeAction="actionNext"
             latin:navigatePrevious="false"
+            latin:passwordInput="true"
         >
             <key-style
                 latin:styleName="navigateMoreKeysStyle" />
@@ -42,6 +46,7 @@
         <case
             latin:imeAction="actionPrevious"
             latin:navigateNext="true"
+            latin:passwordInput="true"
         >
             <key-style
                 latin:styleName="navigateMoreKeysStyle"
@@ -51,14 +56,15 @@
         <case
             latin:imeAction="actionPrevious"
             latin:navigateNext="false"
+            latin:passwordInput="true"
         >
             <key-style
                 latin:styleName="navigateMoreKeysStyle" />
         </case>
-        <!-- imeAction!="actionNext" and imeAction!="actionPrevious" -->
         <case
             latin:navigateNext="true"
             latin:navigatePrevious="true"
+            latin:passwordInput="true"
         >
             <key-style
                 latin:styleName="navigateMoreKeysStyle"
@@ -68,6 +74,7 @@
         <case
             latin:navigateNext="true"
             latin:navigatePrevious="false"
+            latin:passwordInput="true"
         >
             <key-style
                 latin:styleName="navigateMoreKeysStyle"
@@ -77,13 +84,166 @@
         <case
             latin:navigateNext="false"
             latin:navigatePrevious="true"
+            latin:passwordInput="true"
         >
             <key-style
                 latin:styleName="navigateMoreKeysStyle"
                 latin:keyLabelFlags="hasPopupHint|preserveCase"
                 latin:moreKeys="!text/action_previous_as_more_key" />
         </case>
-        <!-- naviagteNext="false" and navigatePrevious="false" -->
+        <case
+            latin:navigateNext="false"
+            latin:navigatePrevious="false"
+            latin:passwordInput="true"
+        >
+            <key-style
+                latin:styleName="navigateMoreKeysStyle" />
+        </case>
+        <!-- latin:mode="email|url|phone|number|date|time|datetime" -->
+        <case
+            latin:imeAction="actionNext"
+            latin:navigatePrevious="true"
+            latin:mode="email|url|phone|number|date|time|datetime"
+        >
+            <key-style
+                latin:styleName="navigateMoreKeysStyle"
+                latin:keyLabelFlags="hasPopupHint|preserveCase"
+                latin:moreKeys="!text/action_previous_as_more_key" />
+        </case>
+        <case
+            latin:imeAction="actionNext"
+            latin:navigatePrevious="false"
+            latin:mode="email|url|phone|number|date|time|datetime"
+        >
+            <key-style
+                latin:styleName="navigateMoreKeysStyle" />
+        </case>
+        <case
+            latin:imeAction="actionPrevious"
+            latin:navigateNext="true"
+            latin:mode="email|url|phone|number|date|time|datetime"
+        >
+            <key-style
+                latin:styleName="navigateMoreKeysStyle"
+                latin:keyLabelFlags="hasPopupHint|preserveCase"
+                latin:moreKeys="!text/action_next_as_more_key" />
+        </case>
+        <case
+            latin:imeAction="actionPrevious"
+            latin:navigateNext="false"
+            latin:mode="email|url|phone|number|date|time|datetime"
+        >
+            <key-style
+                latin:styleName="navigateMoreKeysStyle" />
+        </case>
+        <case
+            latin:navigateNext="true"
+            latin:navigatePrevious="true"
+            latin:mode="email|url|phone|number|date|time|datetime"
+        >
+            <key-style
+                latin:styleName="navigateMoreKeysStyle"
+                latin:keyLabelFlags="hasPopupHint|preserveCase"
+                latin:moreKeys="!fixedColumnOrder!2,!needsDividers!,!text/action_previous_as_more_key,!text/action_next_as_more_key" />
+        </case>
+        <case
+            latin:navigateNext="true"
+            latin:navigatePrevious="false"
+            latin:mode="email|url|phone|number|date|time|datetime"
+        >
+            <key-style
+                latin:styleName="navigateMoreKeysStyle"
+                latin:keyLabelFlags="hasPopupHint|preserveCase"
+                latin:moreKeys="!text/action_next_as_more_key" />
+        </case>
+        <case
+            latin:navigateNext="false"
+            latin:navigatePrevious="true"
+            latin:mode="email|url|phone|number|date|time|datetime"
+        >
+            <key-style
+                latin:styleName="navigateMoreKeysStyle"
+                latin:keyLabelFlags="hasPopupHint|preserveCase"
+                latin:moreKeys="!text/action_previous_as_more_key" />
+        </case>
+        <case
+            latin:navigateNext="false"
+            latin:navigatePrevious="false"
+            latin:mode="email|url|phone|number|date|time|datetime"
+        >
+            <key-style
+                latin:styleName="navigateMoreKeysStyle" />
+        </case>
+        <!-- default -->
+        <case
+            latin:imeAction="actionNext"
+            latin:navigatePrevious="true"
+        >
+            <key-style
+                latin:styleName="navigateMoreKeysStyle"
+                latin:keyLabelFlags="hasPopupHint|preserveCase"
+                latin:moreKeys="!fixedColumnOrder!2,!needsDividers!,!text/emoji_key_as_more_key,!text/action_previous_as_more_key" />
+        </case>
+        <case
+            latin:imeAction="actionNext"
+            latin:navigatePrevious="false"
+        >
+            <key-style
+                latin:styleName="navigateMoreKeysStyle"
+                latin:moreKeys="!text/emoji_key_as_more_key" />
+        </case>
+        <case
+            latin:imeAction="actionPrevious"
+            latin:navigateNext="true"
+        >
+            <key-style
+                latin:styleName="navigateMoreKeysStyle"
+                latin:keyLabelFlags="hasPopupHint|preserveCase"
+                latin:moreKeys="!fixedColumnOrder!2,!needsDividers!,!text/emoji_key_as_more_key,!text/action_next_as_more_key" />
+        </case>
+        <case
+            latin:imeAction="actionPrevious"
+            latin:navigateNext="false"
+        >
+            <key-style
+                latin:styleName="navigateMoreKeysStyle"
+                latin:moreKeys="!text/emoji_key_as_more_key" />
+        </case>
+        <case
+            latin:navigateNext="true"
+            latin:navigatePrevious="true"
+        >
+            <key-style
+                latin:styleName="navigateMoreKeysStyle"
+                latin:keyLabelFlags="hasPopupHint|preserveCase"
+                latin:moreKeys="!fixedColumnOrder!3,!needsDividers!,!text/emoji_key_as_more_key,!text/action_previous_as_more_key,!text/action_next_as_more_key" />
+        </case>
+        <case
+            latin:navigateNext="true"
+            latin:navigatePrevious="false"
+        >
+            <key-style
+                latin:styleName="navigateMoreKeysStyle"
+                latin:keyLabelFlags="hasPopupHint|preserveCase"
+                latin:moreKeys="!fixedColumnOrder!2,!needsDividers!,!text/emoji_key_as_more_key,!text/action_next_as_more_key" />
+        </case>
+        <case
+            latin:navigateNext="false"
+            latin:navigatePrevious="true"
+        >
+            <key-style
+                latin:styleName="navigateMoreKeysStyle"
+                latin:keyLabelFlags="hasPopupHint|preserveCase"
+                latin:moreKeys="!fixedColumnOrder!2,!needsDividers!,!text/emoji_key_as_more_key,!text/action_previous_as_more_key" />
+        </case>
+        <case
+            latin:navigateNext="false"
+            latin:navigatePrevious="false"
+        >
+            <key-style
+                latin:styleName="navigateMoreKeysStyle"
+                latin:moreKeys="!text/emoji_key_as_more_key" />
+        </case>
         <default>
             <key-style
                 latin:styleName="navigateMoreKeysStyle" />
diff --git a/java/src/com/android/inputmethod/keyboard/EmojiLayoutParams.java b/java/src/com/android/inputmethod/keyboard/EmojiLayoutParams.java
index 71790b7..ceb44e7 100644
--- a/java/src/com/android/inputmethod/keyboard/EmojiLayoutParams.java
+++ b/java/src/com/android/inputmethod/keyboard/EmojiLayoutParams.java
@@ -56,7 +56,7 @@
                 - (mKeyVerticalGap - mBottomPadding) / 2;
         mEmojiPagerHeight = defaultKeyboardHeight - mEmojiActionBarHeight
                 - mEmojiCategoryPageIdViewHeight;
-        mEmojiPagerBottomMargin = mKeyVerticalGap / 2;
+        mEmojiPagerBottomMargin = 0;
         mEmojiKeyboardHeight = mEmojiPagerHeight - mEmojiPagerBottomMargin - 1;
     }
 
diff --git a/java/src/com/android/inputmethod/keyboard/EmojiKeyboardView.java b/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
similarity index 92%
rename from java/src/com/android/inputmethod/keyboard/EmojiKeyboardView.java
rename to java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
index eb48d01..85ae500 100644
--- a/java/src/com/android/inputmethod/keyboard/EmojiKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
@@ -60,8 +60,8 @@
 import java.util.concurrent.ConcurrentHashMap;
 
 /**
- * View class to implement Emoji keyboards.
- * The Emoji keyboard consists of group of views {@link R.layout#emoji_keyboard_view}.
+ * View class to implement Emoji palettes.
+ * The Emoji keyboard consists of group of views {@link R.layout#emoji_palettes_view}.
  * <ol>
  * <li> Emoji category tabs.
  * <li> Delete button.
@@ -70,16 +70,16 @@
  * </ol>
  * Because of the above reasons, this class doesn't extend {@link KeyboardView}.
  */
-public final class EmojiKeyboardView extends LinearLayout implements OnTabChangeListener,
+public final class EmojiPalettesView extends LinearLayout implements OnTabChangeListener,
         ViewPager.OnPageChangeListener, View.OnClickListener,
         ScrollKeyboardView.OnKeyClickListener {
-    private static final String TAG = EmojiKeyboardView.class.getSimpleName();
+    private static final String TAG = EmojiPalettesView.class.getSimpleName();
     private final int mKeyBackgroundId;
     private final int mEmojiFunctionalKeyBackgroundId;
     private final KeyboardLayoutSet mLayoutSet;
     private final ColorStateList mTabLabelColor;
     private final DeleteKeyOnTouchListener mDeleteKeyOnTouchListener;
-    private EmojiKeyboardAdapter mEmojiKeyboardAdapter;
+    private EmojiPalettesAdapter mEmojiPalettesAdapter;
 
     private TabHost mTabHost;
     private ViewPager mEmojiPager;
@@ -378,11 +378,11 @@
 
     private final EmojiCategory mEmojiCategory;
 
-    public EmojiKeyboardView(final Context context, final AttributeSet attrs) {
-        this(context, attrs, R.attr.emojiKeyboardViewStyle);
+    public EmojiPalettesView(final Context context, final AttributeSet attrs) {
+        this(context, attrs, R.attr.emojiPalettesViewStyle);
     }
 
-    public EmojiKeyboardView(final Context context, final AttributeSet attrs, final int defStyle) {
+    public EmojiPalettesView(final Context context, final AttributeSet attrs, final int defStyle) {
         super(context, attrs, defStyle);
         final TypedArray keyboardViewAttr = context.obtainStyledAttributes(attrs,
                 R.styleable.KeyboardView, defStyle, R.style.KeyboardView);
@@ -391,11 +391,11 @@
         mEmojiFunctionalKeyBackgroundId = keyboardViewAttr.getResourceId(
                 R.styleable.KeyboardView_keyBackgroundEmojiFunctional, 0);
         keyboardViewAttr.recycle();
-        final TypedArray emojiKeyboardViewAttr = context.obtainStyledAttributes(attrs,
-                R.styleable.EmojiKeyboardView, defStyle, R.style.EmojiKeyboardView);
-        mTabLabelColor = emojiKeyboardViewAttr.getColorStateList(
-                R.styleable.EmojiKeyboardView_emojiTabLabelColor);
-        emojiKeyboardViewAttr.recycle();
+        final TypedArray emojiPalettesViewAttr = context.obtainStyledAttributes(attrs,
+                R.styleable.EmojiPalettesView, defStyle, R.style.EmojiPalettesView);
+        mTabLabelColor = emojiPalettesViewAttr.getColorStateList(
+                R.styleable.EmojiPalettesView_emojiTabLabelColor);
+        emojiPalettesViewAttr.recycle();
         final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder(
                 context, null /* editorInfo */);
         final Resources res = context.getResources();
@@ -453,10 +453,10 @@
         mTabHost.setOnTabChangedListener(this);
         mTabHost.getTabWidget().setStripEnabled(true);
 
-        mEmojiKeyboardAdapter = new EmojiKeyboardAdapter(mEmojiCategory, mLayoutSet, this);
+        mEmojiPalettesAdapter = new EmojiPalettesAdapter(mEmojiCategory, mLayoutSet, this);
 
         mEmojiPager = (ViewPager)findViewById(R.id.emoji_keyboard_pager);
-        mEmojiPager.setAdapter(mEmojiKeyboardAdapter);
+        mEmojiPager.setAdapter(mEmojiPalettesAdapter);
         mEmojiPager.setOnPageChangeListener(this);
         mEmojiPager.setOffscreenPageLimit(0);
         final Resources res = getResources();
@@ -484,10 +484,10 @@
         spaceKey.setTag(Constants.CODE_SPACE);
         spaceKey.setOnClickListener(this);
         emojiLp.setKeyProperties(spaceKey);
-        final ImageView sendKey = (ImageView)findViewById(R.id.emoji_keyboard_send);
-        sendKey.setBackgroundResource(mEmojiFunctionalKeyBackgroundId);
-        sendKey.setTag(Constants.CODE_ENTER);
-        sendKey.setOnClickListener(this);
+        final ImageView alphabetKey2 = (ImageView)findViewById(R.id.emoji_keyboard_alphabet2);
+        alphabetKey2.setBackgroundResource(mEmojiFunctionalKeyBackgroundId);
+        alphabetKey2.setTag(Constants.CODE_SWITCH_ALPHA_SYMBOL);
+        alphabetKey2.setOnClickListener(this);
     }
 
     @Override
@@ -551,7 +551,7 @@
 
     @Override
     public void onKeyClick(final Key key) {
-        mEmojiKeyboardAdapter.addRecentKey(key);
+        mEmojiPalettesAdapter.addRecentKey(key);
         mEmojiCategory.saveLastTypedCategoryPage();
         final int code = key.getCode();
         if (code == Constants.CODE_OUTPUT_TEXT) {
@@ -580,10 +580,18 @@
     }
 
     private void setCurrentCategoryId(final int categoryId, final boolean force) {
-        if (mEmojiCategory.getCurrentCategoryId() == categoryId && !force) {
+        final int oldCategoryId = mEmojiCategory.getCurrentCategoryId();
+        if (oldCategoryId == categoryId && !force) {
             return;
         }
 
+        if (oldCategoryId == CATEGORY_ID_RECENTS) {
+            // Needs to save pending updates for recent keys when we get out of the recents
+            // category because we don't want to move the recent emojis around while the user
+            // is in the recents category.
+            mEmojiPalettesAdapter.flushPendingRecentKeys();
+        }
+
         mEmojiCategory.setCurrentCategoryId(categoryId);
         final int newTabId = mEmojiCategory.getTabIdFromCategoryId(categoryId);
         final int newCategoryPageId = mEmojiCategory.getPageIdFromCategoryId(categoryId);
@@ -596,7 +604,7 @@
         }
     }
 
-    private static class EmojiKeyboardAdapter extends PagerAdapter {
+    private static class EmojiPalettesAdapter extends PagerAdapter {
         private final ScrollKeyboardView.OnKeyClickListener mListener;
         private final DynamicGridKeyboard mRecentsKeyboard;
         private final SparseArray<ScrollKeyboardView> mActiveKeyboardView =
@@ -604,7 +612,7 @@
         private final EmojiCategory mEmojiCategory;
         private int mActivePosition = 0;
 
-        public EmojiKeyboardAdapter(final EmojiCategory emojiCategory,
+        public EmojiPalettesAdapter(final EmojiCategory emojiCategory,
                 final KeyboardLayoutSet layoutSet,
                 final ScrollKeyboardView.OnKeyClickListener listener) {
             mEmojiCategory = emojiCategory;
@@ -612,8 +620,18 @@
             mRecentsKeyboard = mEmojiCategory.getKeyboard(CATEGORY_ID_RECENTS, 0);
         }
 
+        public void flushPendingRecentKeys() {
+            mRecentsKeyboard.flushPendingRecentKeys();
+            final KeyboardView recentKeyboardView =
+                    mActiveKeyboardView.get(mEmojiCategory.getRecentTabId());
+            if (recentKeyboardView != null) {
+                recentKeyboardView.invalidateAllKeys();
+            }
+        }
+
         public void addRecentKey(final Key key) {
             if (mEmojiCategory.isInRecentTab()) {
+                mRecentsKeyboard.addPendingKey(key);
                 return;
             }
             mRecentsKeyboard.addKeyFirst(key);
@@ -644,6 +662,12 @@
 
         @Override
         public Object instantiateItem(final ViewGroup container, final int position) {
+            final ScrollKeyboardView oldKeyboardView = mActiveKeyboardView.get(position);
+            if (oldKeyboardView != null) {
+                oldKeyboardView.deallocateMemory();
+                // This may be redundant but wanted to be safer..
+                mActiveKeyboardView.remove(position);
+            }
             final Keyboard keyboard =
                     mEmojiCategory.getKeyboardFromPagePosition(position);
             final LayoutInflater inflater = LayoutInflater.from(container.getContext());
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index ad6e2c0..4fc1082 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -68,7 +68,7 @@
     private InputView mCurrentInputView;
     private View mMainKeyboardFrame;
     private MainKeyboardView mKeyboardView;
-    private EmojiKeyboardView mEmojiKeyboardView;
+    private EmojiPalettesView mEmojiPalettesView;
     private LatinIME mLatinIME;
     private Resources mResources;
 
@@ -169,7 +169,7 @@
     }
 
     private void setKeyboard(final Keyboard keyboard) {
-        // Make {@link MainKeyboardView} visible and hide {@link EmojiKeyboardView}.
+        // Make {@link MainKeyboardView} visible and hide {@link EmojiPalettesView}.
         setMainKeyboardFrame();
         final MainKeyboardView keyboardView = mKeyboardView;
         final Keyboard oldKeyboard = keyboardView.getKeyboard();
@@ -259,14 +259,14 @@
 
     private void setMainKeyboardFrame() {
         mMainKeyboardFrame.setVisibility(View.VISIBLE);
-        mEmojiKeyboardView.setVisibility(View.GONE);
+        mEmojiPalettesView.setVisibility(View.GONE);
     }
 
     // Implements {@link KeyboardState.SwitchActions}.
     @Override
     public void setEmojiKeyboard() {
         mMainKeyboardFrame.setVisibility(View.GONE);
-        mEmojiKeyboardView.setVisibility(View.VISIBLE);
+        mEmojiPalettesView.setVisibility(View.VISIBLE);
     }
 
     // Implements {@link KeyboardState.SwitchActions}.
@@ -315,7 +315,7 @@
     }
 
     public boolean isShowingEmojiKeyboard() {
-        return mEmojiKeyboardView != null && mEmojiKeyboardView.getVisibility() == View.VISIBLE;
+        return mEmojiPalettesView != null && mEmojiPalettesView.getVisibility() == View.VISIBLE;
     }
 
     public boolean isShowingMoreKeysPanel() {
@@ -327,7 +327,7 @@
 
     public View getVisibleKeyboardView() {
         if (isShowingEmojiKeyboard()) {
-            return mEmojiKeyboardView;
+            return mEmojiPalettesView;
         }
         return mKeyboardView;
     }
@@ -345,15 +345,15 @@
         mCurrentInputView = (InputView)LayoutInflater.from(mThemeContext).inflate(
                 R.layout.input_view, null);
         mMainKeyboardFrame = mCurrentInputView.findViewById(R.id.main_keyboard_frame);
-        mEmojiKeyboardView = (EmojiKeyboardView)mCurrentInputView.findViewById(
+        mEmojiPalettesView = (EmojiPalettesView)mCurrentInputView.findViewById(
                 R.id.emoji_keyboard_view);
 
         mKeyboardView = (MainKeyboardView) mCurrentInputView.findViewById(R.id.keyboard_view);
         mKeyboardView.setHardwareAcceleratedDrawingEnabled(isHardwareAcceleratedDrawingEnabled);
         mKeyboardView.setKeyboardActionListener(mLatinIME);
-        mEmojiKeyboardView.setHardwareAcceleratedDrawingEnabled(
+        mEmojiPalettesView.setHardwareAcceleratedDrawingEnabled(
                 isHardwareAcceleratedDrawingEnabled);
-        mEmojiKeyboardView.setKeyboardActionListener(mLatinIME);
+        mEmojiPalettesView.setKeyboardActionListener(mLatinIME);
 
         // This always needs to be set since the accessibility state can
         // potentially change without the input view being re-created.
diff --git a/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java b/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java
index 0dd71e2..09766ac 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java
@@ -20,7 +20,7 @@
 import android.text.TextUtils;
 import android.util.Log;
 
-import com.android.inputmethod.keyboard.EmojiKeyboardView;
+import com.android.inputmethod.keyboard.EmojiPalettesView;
 import com.android.inputmethod.keyboard.Key;
 import com.android.inputmethod.keyboard.Keyboard;
 import com.android.inputmethod.latin.settings.Settings;
@@ -39,15 +39,16 @@
     private static final String TAG = DynamicGridKeyboard.class.getSimpleName();
     private static final int TEMPLATE_KEY_CODE_0 = 0x30;
     private static final int TEMPLATE_KEY_CODE_1 = 0x31;
+    private final Object mLock = new Object();
 
     private final SharedPreferences mPrefs;
-    private final int mLeftPadding;
     private final int mHorizontalStep;
     private final int mVerticalStep;
     private final int mColumnsNum;
     private final int mMaxKeyCount;
     private final boolean mIsRecents;
     private final ArrayDeque<GridKey> mGridKeys = CollectionUtils.newArrayDeque();
+    private final ArrayDeque<Key> mPendingKeys = CollectionUtils.newArrayDeque();
 
     private Key[] mCachedGridKeys;
 
@@ -56,12 +57,11 @@
         super(templateKeyboard);
         final Key key0 = getTemplateKey(TEMPLATE_KEY_CODE_0);
         final Key key1 = getTemplateKey(TEMPLATE_KEY_CODE_1);
-        mLeftPadding = key0.getX();
         mHorizontalStep = Math.abs(key1.getX() - key0.getX());
         mVerticalStep = key0.getHeight() + mVerticalGap;
         mColumnsNum = mBaseWidth / mHorizontalStep;
         mMaxKeyCount = maxKeyCount;
-        mIsRecents = categoryId == EmojiKeyboardView.CATEGORY_ID_RECENTS;
+        mIsRecents = categoryId == EmojiPalettesView.CATEGORY_ID_RECENTS;
         mPrefs = prefs;
     }
 
@@ -74,6 +74,21 @@
         throw new RuntimeException("Can't find template key: code=" + code);
     }
 
+    public void addPendingKey(final Key usedKey) {
+        synchronized (mLock) {
+            mPendingKeys.addLast(usedKey);
+        }
+    }
+
+    public void flushPendingRecentKeys() {
+        synchronized (mLock) {
+            while (!mPendingKeys.isEmpty()) {
+                addKey(mPendingKeys.pollFirst(), true);
+            }
+            saveRecentKeys();
+        }
+    }
+
     public void addKeyFirst(final Key usedKey) {
         addKey(usedKey, true);
         if (mIsRecents) {
@@ -89,7 +104,7 @@
         if (usedKey == null) {
             return;
         }
-        synchronized (mGridKeys) {
+        synchronized (mLock) {
             mCachedGridKeys = null;
             final GridKey key = new GridKey(usedKey);
             while (mGridKeys.remove(key)) {
@@ -105,9 +120,11 @@
             }
             int index = 0;
             for (final GridKey gridKey : mGridKeys) {
-                final int keyX = getKeyX(index);
-                final int keyY = getKeyY(index);
-                gridKey.updateCorrdinates(keyX, keyY);
+                final int keyX0 = getKeyX0(index);
+                final int keyY0 = getKeyY0(index);
+                final int keyX1 = getKeyX1(index);
+                final int keyY1 = getKeyY1(index);
+                gridKey.updateCorrdinates(keyX0, keyY0, keyX1, keyY1);
                 index++;
             }
         }
@@ -155,19 +172,29 @@
         }
     }
 
-    private int getKeyX(final int index) {
+    private int getKeyX0(final int index) {
         final int column = index % mColumnsNum;
-        return column * mHorizontalStep + mLeftPadding;
+        return column * mHorizontalStep;
     }
 
-    private int getKeyY(final int index) {
+    private int getKeyX1(final int index) {
+        final int column = index % mColumnsNum + 1;
+        return column * mHorizontalStep;
+    }
+
+    private int getKeyY0(final int index) {
         final int row = index / mColumnsNum;
-        return row * mVerticalStep + mTopPadding;
+        return row * mVerticalStep;
+    }
+
+    private int getKeyY1(final int index) {
+        final int row = index / mColumnsNum + 1;
+        return row * mVerticalStep;
     }
 
     @Override
     public Key[] getKeys() {
-        synchronized (mGridKeys) {
+        synchronized (mLock) {
             if (mCachedGridKeys != null) {
                 return mCachedGridKeys;
             }
@@ -190,10 +217,10 @@
             super(originalKey);
         }
 
-        public void updateCorrdinates(final int x, final int y) {
-            mCurrentX = x;
-            mCurrentY = y;
-            getHitBox().set(x, y, x + getWidth(), y + getHeight());
+        public void updateCorrdinates(final int x0, final int y0, final int x1, final int y1) {
+            mCurrentX = x0;
+            mCurrentY = y0;
+            getHitBox().set(x0, y0, x1, y1);
         }
 
         @Override
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
index 506dfa7..9f9fdaa 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
@@ -178,8 +178,6 @@
             if (!state.mIsAlphabetShiftLocked) {
                 setShifted(state.mShiftMode);
             }
-            // TODO: is this the right place to do this? Should we do this in setShift* instead?
-            mSwitchActions.requestUpdatingShiftState();
         } else {
             mPrevMainKeyboardWasShiftLocked = state.mIsAlphabetShiftLocked;
         }
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
index 684cf63..d219e81 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
@@ -251,6 +251,7 @@
         /* 146 */ "more_keys_for_single_quote",
         /* 147 */ "more_keys_for_double_quote",
         /* 148 */ "more_keys_for_tablet_double_quote",
+        /* 149 */ "emoji_key_as_more_key",
     };
 
     private static final String EMPTY = "";
@@ -439,6 +440,7 @@
         /* 146 */ "!fixedColumnOrder!5,!text/single_quotes,!text/single_angle_quotes",
         /* 147 */ "!fixedColumnOrder!5,!text/double_quotes,!text/double_angle_quotes",
         /* 148 */ "!fixedColumnOrder!6,!text/double_quotes,!text/single_quotes,!text/double_angle_quotes,!text/single_angle_quotes",
+        /* 149 */ "!icon/emoji_key|!code/key_emoji",
     };
 
     /* Language af: Afrikaans */
diff --git a/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java b/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java
index 4a0ce37..463d093 100644
--- a/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java
+++ b/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java
@@ -41,8 +41,17 @@
 
     abstract public void clear();
 
+    /**
+     * Add a unigram with an optional shortcut to the dictionary.
+     * @param word The word to add.
+     * @param shortcutTarget A shortcut target for this word, or null if none.
+     * @param frequency The frequency for this unigram.
+     * @param shortcutFreq The frequency of the shortcut (0~15, with 15 = whitelist). Ignored
+     *   if shortcutTarget is null.
+     * @param isNotAWord true if this is not a word, i.e. shortcut only.
+     */
     abstract public void addUnigramWord(final String word, final String shortcutTarget,
-            final int frequency, final boolean isNotAWord);
+            final int frequency, final int shortcutFreq, final boolean isNotAWord);
 
     // TODO: Remove lastModifiedTime after making binary dictionary support forgetting curve.
     abstract public void addBigramWords(final String word0, final String word1,
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index 541e697..fd29698 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -52,6 +52,10 @@
     public static final String UNIGRAM_COUNT_QUERY = "UNIGRAM_COUNT";
     @UsedForTesting
     public static final String BIGRAM_COUNT_QUERY = "BIGRAM_COUNT";
+    @UsedForTesting
+    public static final String MAX_UNIGRAM_COUNT_QUERY = "MAX_UNIGRAM_COUNT";
+    @UsedForTesting
+    public static final String MAX_BIGRAM_COUNT_QUERY = "MAX_BIGRAM_COUNT";
 
     private long mNativeDict;
     private final Locale mLocale;
diff --git a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
index ffeb927..47891c6 100644
--- a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
@@ -127,7 +127,7 @@
             if (DEBUG) {
                 Log.d(TAG, "loadAccountVocabulary: " + word);
             }
-            super.addWord(word, null /* shortcut */, FREQUENCY_FOR_CONTACTS,
+            super.addWord(word, null /* shortcut */, FREQUENCY_FOR_CONTACTS, 0 /* shortcutFreq */,
                     false /* isNotAWord */);
         }
     }
@@ -213,7 +213,7 @@
                         Log.d(TAG, "addName " + name + ", " + word + ", " + prevWord);
                     }
                     super.addWord(word, null /* shortcut */, FREQUENCY_FOR_CONTACTS,
-                            false /* isNotAWord */);
+                            0 /* shortcutFreq */, false /* isNotAWord */);
                     if (!TextUtils.isEmpty(prevWord)) {
                         if (mUseFirstLastBigrams) {
                             super.addBigram(prevWord, word, FREQUENCY_FOR_CONTACTS_BIGRAM,
diff --git a/java/src/com/android/inputmethod/latin/DictionaryWriter.java b/java/src/com/android/inputmethod/latin/DictionaryWriter.java
index 84abfa6..3df2a2b 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryWriter.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryWriter.java
@@ -62,13 +62,13 @@
     // considering performance regression.
     @Override
     public void addUnigramWord(final String word, final String shortcutTarget, final int frequency,
-            final boolean isNotAWord) {
+            final int shortcutFreq, final boolean isNotAWord) {
         if (shortcutTarget == null) {
             mFusionDictionary.add(word, frequency, null, isNotAWord);
         } else {
             // TODO: Do this in the subclass, with this class taking an arraylist.
             final ArrayList<WeightedString> shortcutTargets = CollectionUtils.newArrayList();
-            shortcutTargets.add(new WeightedString(shortcutTarget, frequency));
+            shortcutTargets.add(new WeightedString(shortcutTarget, shortcutFreq));
             mFusionDictionary.add(word, frequency, shortcutTargets, isNotAWord);
         }
     }
diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
index c79a4ff..eb8650e 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
@@ -261,10 +261,16 @@
 
     /**
      * Adds a word unigram to the dictionary. Used for loading a dictionary.
+     * @param word The word to add.
+     * @param shortcutTarget A shortcut target for this word, or null if none.
+     * @param frequency The frequency for this unigram.
+     * @param shortcutFreq The frequency of the shortcut (0~15, with 15 = whitelist). Ignored
+     *   if shortcutTarget is null.
+     * @param isNotAWord true if this is not a word, i.e. shortcut only.
      */
     protected void addWord(final String word, final String shortcutTarget,
-            final int frequency, final boolean isNotAWord) {
-        mDictionaryWriter.addUnigramWord(word, shortcutTarget, frequency, isNotAWord);
+            final int frequency, final int shortcutFreq, final boolean isNotAWord) {
+        mDictionaryWriter.addUnigramWord(word, shortcutTarget, frequency, shortcutFreq, isNotAWord);
     }
 
     /**
@@ -313,7 +319,7 @@
      * Dynamically adds a word unigram to the dictionary. May overwrite an existing entry.
      */
     protected void addWordDynamically(final String word, final String shortcutTarget,
-            final int frequency, final boolean isNotAWord) {
+            final int frequency, final int shortcutFreq, final boolean isNotAWord) {
         if (!mIsUpdatable) {
             Log.w(TAG, "addWordDynamically is called for non-updatable dictionary: " + mFilename);
             return;
@@ -326,7 +332,8 @@
                     mBinaryDictionary.addUnigramWord(word, frequency);
                 } else {
                     // TODO: Remove.
-                    mDictionaryWriter.addUnigramWord(word, shortcutTarget, frequency, isNotAWord);
+                    mDictionaryWriter.addUnigramWord(word, shortcutTarget, frequency, shortcutFreq,
+                            isNotAWord);
                 }
             }
         });
diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
index d491f98..95c9bca 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
@@ -156,15 +156,36 @@
         return Constants.DICTIONARY_MAX_WORD_LENGTH;
     }
 
-    public void addWord(final String word, final String shortcutTarget, final int frequency) {
+    /**
+     * Add a word with an optional shortcut to the dictionary.
+     * @param word The word to add.
+     * @param shortcutTarget A shortcut target for this word, or null if none.
+     * @param frequency The frequency for this unigram.
+     * @param shortcutFreq The frequency of the shortcut (0~15, with 15 = whitelist). Ignored
+     *   if shortcutTarget is null.
+     */
+    public void addWord(final String word, final String shortcutTarget, final int frequency,
+            final int shortcutFreq) {
         if (word.length() >= Constants.DICTIONARY_MAX_WORD_LENGTH) {
             return;
         }
-        addWordRec(mRoots, word, 0, shortcutTarget, frequency, null);
+        addWordRec(mRoots, word, 0, shortcutTarget, frequency, shortcutFreq, null);
     }
 
+    /**
+     * Add a word, recursively searching for its correct place in the trie tree.
+     * @param children The node to recursively search for addition. Initially, the root of the tree.
+     * @param word The word to add.
+     * @param depth The current depth in the tree.
+     * @param shortcutTarget A shortcut target for this word, or null if none.
+     * @param frequency The frequency for this unigram.
+     * @param shortcutFreq The frequency of the shortcut (0~15, with 15 = whitelist). Ignored
+     *   if shortcutTarget is null.
+     * @param parentNode The parent node, for up linking. Initially null, as the root has no parent.
+     */
     private void addWordRec(final NodeArray children, final String word, final int depth,
-            final String shortcutTarget, final int frequency, final Node parentNode) {
+            final String shortcutTarget, final int frequency, final int shortcutFreq,
+            final Node parentNode) {
         final int wordLength = word.length();
         if (wordLength <= depth) return;
         final char c = word.charAt(depth);
@@ -204,7 +225,8 @@
         if (childNode.mChildren == null) {
             childNode.mChildren = new NodeArray();
         }
-        addWordRec(childNode.mChildren, word, depth + 1, shortcutTarget, frequency, childNode);
+        addWordRec(childNode.mChildren, word, depth + 1, shortcutTarget, frequency, shortcutFreq,
+                childNode);
     }
 
     @Override
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 0f3d289..0e93590 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -605,8 +605,24 @@
     }
 
     private void initSuggest() {
-        final Locale subtypeLocale = mSubtypeSwitcher.getCurrentSubtypeLocale();
-        final String localeStr = subtypeLocale.toString();
+        final Locale switcherSubtypeLocale = mSubtypeSwitcher.getCurrentSubtypeLocale();
+        final String switcherLocaleStr = switcherSubtypeLocale.toString();
+        final Locale subtypeLocale;
+        final String localeStr;
+        if (TextUtils.isEmpty(switcherLocaleStr)) {
+            // This happens in very rare corner cases - for example, immediately after a switch
+            // to LatinIME has been requested, about a frame later another switch happens. In this
+            // case, we are about to go down but we still don't know it, however the system tells
+            // us there is no current subtype so the locale is the empty string. Take the best
+            // possible guess instead -- it's bound to have no consequences, and we have no way
+            // of knowing anyway.
+            Log.e(TAG, "System is reporting no current subtype.");
+            subtypeLocale = getResources().getConfiguration().locale;
+            localeStr = subtypeLocale.toString();
+        } else {
+            subtypeLocale = switcherSubtypeLocale;
+            localeStr = switcherLocaleStr;
+        }
 
         final Suggest newSuggest = new Suggest(this /* Context */, subtypeLocale,
                 this /* SuggestInitializationListener */);
@@ -792,6 +808,7 @@
     @SuppressWarnings("deprecation")
     private void onStartInputViewInternal(final EditorInfo editorInfo, final boolean restarting) {
         super.onStartInputView(editorInfo, restarting);
+        mRichImm.clearSubtypeCaches();
         final KeyboardSwitcher switcher = mKeyboardSwitcher;
         final MainKeyboardView mainKeyboardView = switcher.getMainKeyboardView();
         // If we are starting input in a different text field from before, we'll have to reload
@@ -887,12 +904,17 @@
         // Sometimes, while rotating, for some reason the framework tells the app we are not
         // connected to it and that means we can't refresh the cache. In this case, schedule a
         // refresh later.
+        final boolean canReachInputConnection;
         if (!mConnection.resetCachesUponCursorMoveAndReturnSuccess(editorInfo.initialSelStart,
                 false /* shouldFinishComposition */)) {
             // We try resetting the caches up to 5 times before giving up.
             mHandler.postResetCaches(isDifferentTextField, 5 /* remainingTries */);
+            canReachInputConnection = false;
         } else {
-            if (isDifferentTextField) mHandler.postResumeSuggestions();
+            if (isDifferentTextField) {
+                mHandler.postResumeSuggestions();
+            }
+            canReachInputConnection = true;
         }
 
         if (isDifferentTextField) {
@@ -904,7 +926,11 @@
                 suggest.setAutoCorrectionThreshold(currentSettingsValues.mAutoCorrectionThreshold);
             }
 
-            switcher.loadKeyboard(editorInfo, currentSettingsValues);
+            if (canReachInputConnection) {
+                // If we can't reach the input connection, we don't want to call loadKeyboard yet.
+                // It will be done in #retryResetCaches.
+                switcher.loadKeyboard(editorInfo, currentSettingsValues);
+            }
         } else if (restarting) {
             // TODO: Come up with a more comprehensive way to reset the keyboard layout when
             // a keyboard layout set doesn't get reloaded in this method.
@@ -1033,7 +1059,6 @@
         // Should do the following in onFinishInputInternal but until JB MR2 it's not called :(
         if (mWordComposer.isComposingWord()) mConnection.finishComposingText();
         resetComposingState(true /* alsoResetLastComposedWord */);
-        mRichImm.clearSubtypeCaches();
         // Notify ResearchLogger
         if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
             ResearchLogger.latinIME_onFinishInputViewInternal(finishingInput, mLastSelectionStart,
@@ -1438,11 +1463,21 @@
         if (!settingsValues.mCorrectionEnabled) return false;
         if (!settingsValues.mUseDoubleSpacePeriod) return false;
         if (!mHandler.isAcceptingDoubleSpacePeriod()) return false;
-        final CharSequence lastThree = mConnection.getTextBeforeCursor(3, 0);
-        if (lastThree != null && lastThree.length() == 3
-                && canBeFollowedByDoubleSpacePeriod(lastThree.charAt(0))
-                && lastThree.charAt(1) == Constants.CODE_SPACE
-                && lastThree.charAt(2) == Constants.CODE_SPACE) {
+        // We only do this when we see two spaces and an accepted code point before the cursor.
+        // The code point may be a surrogate pair but the two spaces may not, so we need 4 chars.
+        final CharSequence lastThree = mConnection.getTextBeforeCursor(4, 0);
+        if (null == lastThree) return false;
+        final int length = lastThree.length();
+        if (length < 3) return false;
+        if (lastThree.charAt(length - 1) != Constants.CODE_SPACE) return false;
+        if (lastThree.charAt(length - 2) != Constants.CODE_SPACE) return false;
+        // We know there are spaces in pos -1 and -2, and we have at least three chars.
+        // If we have only three chars, isSurrogatePairs can't return true as charAt(1) is a space,
+        // so this is fine.
+        final int firstCodePoint =
+                Character.isSurrogatePair(lastThree.charAt(0), lastThree.charAt(1)) ?
+                        Character.codePointAt(lastThree, 0) : lastThree.charAt(length - 3);
+        if (canBeFollowedByDoubleSpacePeriod(firstCodePoint)) {
             mHandler.cancelDoubleSpacePeriodTimer();
             mConnection.deleteSurroundingText(2, 0);
             final String textToInsert = ". ";
@@ -1467,7 +1502,8 @@
                 || codePoint == Constants.CODE_CLOSING_SQUARE_BRACKET
                 || codePoint == Constants.CODE_CLOSING_CURLY_BRACKET
                 || codePoint == Constants.CODE_CLOSING_ANGLE_BRACKET
-                || codePoint == Constants.CODE_PLUS;
+                || codePoint == Constants.CODE_PLUS
+                || Character.getType(codePoint) == Character.OTHER_SYMBOL;
     }
 
     // Callback for the {@link SuggestionStripView}, to call when the "add to dictionary" hint is
@@ -2928,11 +2964,13 @@
         if (!mConnection.resetCachesUponCursorMoveAndReturnSuccess(mLastSelectionStart, false)) {
             if (0 < remainingTries) {
                 mHandler.postResetCaches(tryResumeSuggestions, remainingTries - 1);
+                return;
             }
-            return;
+            // If remainingTries is 0, we should stop waiting for new tries, but it's still
+            // better to load the keyboard (less things will be broken).
         }
         tryFixLyingCursorPosition();
-        mKeyboardSwitcher.updateShiftState();
+        mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(), mSettings.getCurrent());
         if (tryResumeSuggestions) mHandler.postResumeSuggestions();
     }
 
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 9fd1f53..c270d47 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -286,14 +286,16 @@
         // the word *would* have been auto-corrected.
         if (!isCorrectionEnabled || !allowsToBeAutoCorrected || !wordComposer.isComposingWord()
                 || suggestionsSet.isEmpty() || wordComposer.hasDigits()
-                || wordComposer.isMostlyCaps() || wordComposer.isResumed()
-                || !hasMainDictionary()) {
+                || wordComposer.isMostlyCaps() || wordComposer.isResumed() || !hasMainDictionary()
+                || SuggestedWordInfo.KIND_SHORTCUT == suggestionsSet.first().mKind) {
             // If we don't have a main dictionary, we never want to auto-correct. The reason for
             // this is, the user may have a contact whose name happens to match a valid word in
             // their language, and it will unexpectedly auto-correct. For example, if the user
             // types in English with no dictionary and has a "Will" in their contact list, "will"
             // would always auto-correct to "Will" which is unwanted. Hence, no main dict => no
             // auto-correct.
+            // Also, shortcuts should never auto-correct unless they are whitelist entries.
+            // TODO: we may want to have shortcut-only entries auto-correct in the future.
             hasAutoCorrection = false;
         } else {
             hasAutoCorrection = AutoCorrectionUtils.suggestionExceedsAutoCorrectionThreshold(
diff --git a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
index 864a173..15b3d8d 100644
--- a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
@@ -47,6 +47,9 @@
     private static final String USER_DICTIONARY_ALL_LANGUAGES = "";
     private static final int HISTORICAL_DEFAULT_USER_DICTIONARY_FREQUENCY = 250;
     private static final int LATINIME_DEFAULT_USER_DICTIONARY_FREQUENCY = 160;
+    // Shortcut frequency is 0~15, with 15 = whitelist. We don't want user dictionary entries
+    // to auto-correct, so we set this to the highest frequency that won't, i.e. 14.
+    private static final int USER_DICT_SHORTCUT_FREQUENCY = 14;
 
     // TODO: use Words.SHORTCUT when we target JellyBean or above
     final static String SHORTCUT = "shortcut";
@@ -243,10 +246,12 @@
                 final int adjustedFrequency = scaleFrequencyFromDefaultToLatinIme(frequency);
                 // Safeguard against adding really long words.
                 if (word.length() < MAX_WORD_LENGTH) {
-                    super.addWord(word, null, adjustedFrequency, false /* isNotAWord */);
+                    super.addWord(word, null, adjustedFrequency, 0 /* shortcutFreq */,
+                            false /* isNotAWord */);
                 }
                 if (null != shortcut && shortcut.length() < MAX_WORD_LENGTH) {
-                    super.addWord(shortcut, word, adjustedFrequency, true /* isNotAWord */);
+                    super.addWord(shortcut, word, adjustedFrequency, USER_DICT_SHORTCUT_FREQUENCY,
+                            true /* isNotAWord */);
                 }
                 cursor.moveToNext();
             }
diff --git a/java/src/com/android/inputmethod/latin/makedict/DictUpdater.java b/java/src/com/android/inputmethod/latin/makedict/DictUpdater.java
index 709ea33..c4f7ec9 100644
--- a/java/src/com/android/inputmethod/latin/makedict/DictUpdater.java
+++ b/java/src/com/android/inputmethod/latin/makedict/DictUpdater.java
@@ -16,6 +16,7 @@
 
 package com.android.inputmethod.latin.makedict;
 
+import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
 
 import java.io.IOException;
@@ -24,6 +25,7 @@
 /**
  * An interface of a binary dictionary updater.
  */
+@UsedForTesting
 public interface DictUpdater extends DictDecoder {
 
     /**
@@ -31,6 +33,7 @@
      *
      * @param word the word to be deleted.
      */
+    @UsedForTesting
     public void deleteWord(final String word) throws IOException, UnsupportedFormatException;
 
     /**
@@ -43,6 +46,7 @@
      * @param isBlackListEntry whether this should be a blacklist entry.
      */
     // TODO: Support batch insertion.
+    @UsedForTesting
     public void insertWord(final String word, final int frequency,
             final ArrayList<WeightedString> bigramStrings,
             final ArrayList<WeightedString> shortcuts, final boolean isNotAWord,
diff --git a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
index be653fe..3bb218b 100644
--- a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
+++ b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
@@ -367,10 +367,11 @@
      * Helper method to convert a String to an int array.
      */
     static int[] getCodePoints(final String word) {
-        // TODO: this is a copy-paste of the contents of StringUtils.toCodePointArray,
+        // TODO: this is a copy-paste of the old contents of StringUtils.toCodePointArray,
         // which is not visible from the makedict package. Factor this code.
+        final int length = word.length();
+        if (length <= 0) return new int[] {};
         final char[] characters = word.toCharArray();
-        final int length = characters.length;
         final int[] codePoints = new int[Character.codePointCount(characters, 0, length)];
         int codePoint = Character.codePointAt(characters, 0);
         int dsti = 0;
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver3DictUpdater.java b/java/src/com/android/inputmethod/latin/makedict/Ver3DictUpdater.java
index fa7ae31..07adda6 100644
--- a/java/src/com/android/inputmethod/latin/makedict/Ver3DictUpdater.java
+++ b/java/src/com/android/inputmethod/latin/makedict/Ver3DictUpdater.java
@@ -57,7 +57,7 @@
     public void deleteWord(final String word) throws IOException, UnsupportedFormatException {
         if (mOutStream == null) openStreamAndBuffer();
         mDictBuffer.position(0);
-        super.readHeader();
+        readHeader();
         final int wordPos = getTerminalPosition(word);
         if (wordPos != FormatSpec.NOT_VALID_WORD) {
             mDictBuffer.position(wordPos);
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java
index bab24e3..5372907 100644
--- a/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java
+++ b/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java
@@ -48,7 +48,7 @@
 
     private final File mDictDirectory;
     private final DictionaryBufferFactory mBufferFactory;
-    private DictBuffer mDictBuffer;
+    protected DictBuffer mDictBuffer;
     private DictBuffer mFrequencyBuffer;
     private DictBuffer mTerminalAddressTableBuffer;
     private DictBuffer mBigramBuffer;
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver4DictUpdater.java b/java/src/com/android/inputmethod/latin/makedict/Ver4DictUpdater.java
new file mode 100644
index 0000000..3d8f186
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/makedict/Ver4DictUpdater.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.makedict;
+
+import com.android.inputmethod.annotations.UsedForTesting;
+import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * An implementation of DictUpdater for version 4 binary dictionary.
+ */
+@UsedForTesting
+public class Ver4DictUpdater extends Ver4DictDecoder implements DictUpdater {
+
+    @UsedForTesting
+    public Ver4DictUpdater(final File dictDirectory, final int factoryType) {
+        // DictUpdater must have an updatable DictBuffer.
+        super(dictDirectory, ((factoryType & MASK_DICTBUFFER) == USE_BYTEARRAY)
+                ? USE_BYTEARRAY : USE_WRITABLE_BYTEBUFFER);
+    }
+
+    @Override
+    public void deleteWord(final String word) throws IOException, UnsupportedFormatException {
+        if (mDictBuffer == null) openDictBuffer();
+        readHeader();
+        final int wordPos = getTerminalPosition(word);
+        if (wordPos != FormatSpec.NOT_VALID_WORD) {
+            mDictBuffer.position(wordPos);
+            final int flags = PtNodeReader.readPtNodeOptionFlags(mDictBuffer);
+            mDictBuffer.position(wordPos);
+            mDictBuffer.put((byte) DynamicBinaryDictIOUtils.markAsDeleted(flags));
+        }
+    }
+
+    @Override
+    public void insertWord(final String word, final int frequency,
+        final ArrayList<WeightedString> bigramStrings, final ArrayList<WeightedString> shortcuts,
+        final boolean isNotAWord, final boolean isBlackListEntry)
+                throws IOException, UnsupportedFormatException {
+        // TODO: Implement this method.
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
index c8b62b6..a1e3600 100644
--- a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
+++ b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
@@ -138,7 +138,7 @@
         final int frequency = ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE ?
                 (isValid ? FREQUENCY_FOR_WORDS_IN_DICTS : FREQUENCY_FOR_WORDS_NOT_IN_DICTS) :
                         FREQUENCY_FOR_TYPED;
-        addWordDynamically(word1, null /* the "shortcut" parameter is null */, frequency,
+        addWordDynamically(word1, null /* shortcutTarget */, frequency, 0 /* shortcutFreq */,
                 false /* isNotAWord */);
         // Do not insert a word as a bigram of itself
         if (word1.equals(word0)) {
@@ -171,11 +171,11 @@
         final OnAddWordListener listener = new OnAddWordListener() {
             @Override
             public void setUnigram(final String word, final String shortcutTarget,
-                    final int frequency) {
+                    final int frequency, final int shortcutFreq) {
                 if (DBG_SAVE_RESTORE) {
                     Log.d(TAG, "load unigram: " + word + "," + frequency);
                 }
-                addWord(word, shortcutTarget, frequency, false /* isNotAWord */);
+                addWord(word, shortcutTarget, frequency, shortcutFreq, false /* isNotAWord */);
                 ++profTotalCount[0];
             }
 
diff --git a/java/src/com/android/inputmethod/latin/personalization/DynamicPersonalizationDictionaryWriter.java b/java/src/com/android/inputmethod/latin/personalization/DynamicPersonalizationDictionaryWriter.java
index 039b253..6f152bb 100644
--- a/java/src/com/android/inputmethod/latin/personalization/DynamicPersonalizationDictionaryWriter.java
+++ b/java/src/com/android/inputmethod/latin/personalization/DynamicPersonalizationDictionaryWriter.java
@@ -75,15 +75,21 @@
     /**
      * Adds a word unigram to the fusion dictionary. Call updateBinaryDictionary when all changes
      * are done to update the binary dictionary.
+     * @param word The word to add.
+     * @param shortcutTarget A shortcut target for this word, or null if none.
+     * @param frequency The frequency for this unigram.
+     * @param shortcutFreq The frequency of the shortcut (0~15, with 15 = whitelist). Ignored
+     *   if shortcutTarget is null.
+     * @param isNotAWord true if this is not a word, i.e. shortcut only.
      */
     @Override
     public void addUnigramWord(final String word, final String shortcutTarget, final int frequency,
-            final boolean isNotAWord) {
+            final int shortcutFreq, final boolean isNotAWord) {
         if (mBigramList.size() > mMaxHistoryBigrams * 2) {
             // Too many entries: just stop adding new vocabulary and wait next refresh.
             return;
         }
-        mExpandableDictionary.addWord(word, shortcutTarget, frequency);
+        mExpandableDictionary.addWord(word, shortcutTarget, frequency, shortcutFreq);
         mBigramList.addBigram(null, word, (byte)frequency);
     }
 
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
index ee322e9..2abcdc7 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
@@ -45,8 +45,9 @@
  */
 public final class SettingsValues {
     private static final String TAG = SettingsValues.class.getSimpleName();
-    // "floatNegativeInfinity" is a special marker string for Float.NEGATIVE_INFINITE
-    // currently used for auto-correction
+    // "floatMaxValue" and "floatNegativeInfinity" are special marker strings for
+    // Float.NEGATIVE_INFINITE and Float.MAX_VALUE. Currently used for auto-correction settings.
+    private static final String FLOAT_MAX_VALUE_MARKER_STRING = "floatMaxValue";
     private static final String FLOAT_NEGATIVE_INFINITY_MARKER_STRING = "floatNegativeInfinity";
 
     // From resources:
@@ -343,24 +344,28 @@
         final String[] autoCorrectionThresholdValues = res.getStringArray(
                 R.array.auto_correction_threshold_values);
         // When autoCorrectionThreshold is greater than 1.0, it's like auto correction is off.
-        float autoCorrectionThreshold = Float.MAX_VALUE;
+        final float autoCorrectionThreshold;
         try {
             final int arrayIndex = Integer.valueOf(currentAutoCorrectionSetting);
             if (arrayIndex >= 0 && arrayIndex < autoCorrectionThresholdValues.length) {
                 final String val = autoCorrectionThresholdValues[arrayIndex];
-                if (FLOAT_NEGATIVE_INFINITY_MARKER_STRING.equals(val)) {
+                if (FLOAT_MAX_VALUE_MARKER_STRING.equals(val)) {
+                    autoCorrectionThreshold = Float.MAX_VALUE;
+                } else if (FLOAT_NEGATIVE_INFINITY_MARKER_STRING.equals(val)) {
                     autoCorrectionThreshold = Float.NEGATIVE_INFINITY;
                 } else {
                     autoCorrectionThreshold = Float.parseFloat(val);
                 }
+            } else {
+                autoCorrectionThreshold = Float.MAX_VALUE;
             }
-        } catch (NumberFormatException e) {
+        } catch (final NumberFormatException e) {
             // Whenever the threshold settings are correct, never come here.
-            autoCorrectionThreshold = Float.MAX_VALUE;
             Log.w(TAG, "Cannot load auto correction threshold setting."
                     + " currentAutoCorrectionSetting: " + currentAutoCorrectionSetting
                     + ", autoCorrectionThresholdValues: "
                     + Arrays.toString(autoCorrectionThresholdValues), e);
+            return Float.MAX_VALUE;
         }
         return autoCorrectionThreshold;
     }
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
index eb6d7c1..503b18b 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
@@ -204,10 +204,20 @@
         return AndroidSpellCheckerSessionFactory.newInstance(this);
     }
 
-    public static SuggestionsInfo getNotInDictEmptySuggestions() {
-        return new SuggestionsInfo(0, EMPTY_STRING_ARRAY);
+    /**
+     * Returns an empty SuggestionsInfo with flags signaling the word is not in the dictionary.
+     * @param reportAsTypo whether this should include the flag LOOKS_LIKE_TYPO, for red underline.
+     * @return the empty SuggestionsInfo with the appropriate flags set.
+     */
+    public static SuggestionsInfo getNotInDictEmptySuggestions(final boolean reportAsTypo) {
+        return new SuggestionsInfo(reportAsTypo ? SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO : 0,
+                EMPTY_STRING_ARRAY);
     }
 
+    /**
+     * Returns an empty suggestionInfo with flags signaling the word is in the dictionary.
+     * @return the empty SuggestionsInfo with the appropriate flags set.
+     */
     public static SuggestionsInfo getInDictEmptySuggestions() {
         return new SuggestionsInfo(SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY,
                 EMPTY_STRING_ARRAY);
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
index 69f9a46..d6e5b75 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
@@ -161,6 +161,12 @@
         }
     }
 
+    private static final int CHECKABILITY_CHECKABLE = 0;
+    private static final int CHECKABILITY_TOO_MANY_NON_LETTERS = 1;
+    private static final int CHECKABILITY_CONTAINS_PERIOD = 2;
+    private static final int CHECKABILITY_EMAIL_OR_URL = 3;
+    private static final int CHECKABILITY_FIRST_LETTER_UNCHECKABLE = 4;
+    private static final int CHECKABILITY_TOO_SHORT = 5;
     /**
      * Finds out whether a particular string should be filtered out of spell checking.
      *
@@ -171,10 +177,10 @@
      *
      * @param text the string to evaluate.
      * @param script the identifier for the script this spell checker recognizes
-     * @return true if we should filter this text out, false otherwise
+     * @return one of the FILTER_OUT_* constants above.
      */
-    private static boolean shouldFilterOut(final String text, final int script) {
-        if (TextUtils.isEmpty(text) || text.length() <= 1) return true;
+    private static int getCheckabilityInScript(final String text, final int script) {
+        if (TextUtils.isEmpty(text) || text.length() <= 1) return CHECKABILITY_TOO_SHORT;
 
         // TODO: check if an equivalent processing can't be done more quickly with a
         // compiled regexp.
@@ -182,7 +188,7 @@
         final int firstCodePoint = text.codePointAt(0);
         // Filter out words that don't start with a letter or an apostrophe
         if (!isLetterCheckableByLanguage(firstCodePoint, script)
-                && '\'' != firstCodePoint) return true;
+                && '\'' != firstCodePoint) return CHECKABILITY_FIRST_LETTER_UNCHECKABLE;
 
         // Filter contents
         final int length = text.length();
@@ -193,13 +199,21 @@
             // Any word containing a SLASH is probably either an ad-hoc combination of two
             // words or a URI - in either case we don't want to spell check that
             if (Constants.CODE_COMMERCIAL_AT == codePoint || Constants.CODE_SLASH == codePoint) {
-                return true;
+                return CHECKABILITY_EMAIL_OR_URL;
+            }
+            // If the string contains a period, native returns strange suggestions (it seems
+            // to return suggestions for everything up to the period only and to ignore the
+            // rest), so we suppress lookup if there is a period.
+            // TODO: investigate why native returns these suggestions and remove this code.
+            if (Constants.CODE_PERIOD == codePoint) {
+                return CHECKABILITY_CONTAINS_PERIOD;
             }
             if (isLetterCheckableByLanguage(codePoint, script)) ++letterCount;
         }
         // Guestimate heuristic: perform spell checking if at least 3/4 of the characters
         // in this word are letters
-        return (letterCount * 4 < length * 3);
+        return (letterCount * 4 < length * 3)
+                ? CHECKABILITY_TOO_MANY_NON_LETTERS : CHECKABILITY_CHECKABLE;
     }
 
     /**
@@ -256,16 +270,20 @@
                         cachedSuggestionsParams.mFlags, cachedSuggestionsParams.mSuggestions);
             }
 
-            if (shouldFilterOut(inText, mScript)) {
+            final int checkability = getCheckabilityInScript(inText, mScript);
+            if (CHECKABILITY_CHECKABLE != checkability) {
                 DictAndKeyboard dictInfo = null;
                 try {
                     dictInfo = mDictionaryPool.pollWithDefaultTimeout();
                     if (!DictionaryPool.isAValidDictionary(dictInfo)) {
-                        return AndroidSpellCheckerService.getNotInDictEmptySuggestions();
+                        return AndroidSpellCheckerService.getNotInDictEmptySuggestions(
+                                false /* reportAsTypo */);
                     }
                     return dictInfo.mDictionary.isValidWord(inText)
                             ? AndroidSpellCheckerService.getInDictEmptySuggestions()
-                            : AndroidSpellCheckerService.getNotInDictEmptySuggestions();
+                            : AndroidSpellCheckerService.getNotInDictEmptySuggestions(
+                                    CHECKABILITY_CONTAINS_PERIOD == checkability
+                                    /* reportAsTypo */);
                 } finally {
                     if (null != dictInfo) {
                         if (!mDictionaryPool.offer(dictInfo)) {
@@ -290,7 +308,8 @@
             try {
                 dictInfo = mDictionaryPool.pollWithDefaultTimeout();
                 if (!DictionaryPool.isAValidDictionary(dictInfo)) {
-                    return AndroidSpellCheckerService.getNotInDictEmptySuggestions();
+                    return AndroidSpellCheckerService.getNotInDictEmptySuggestions(
+                            false /* reportAsTypo */);
                 }
                 final WordComposer composer = new WordComposer();
                 final int length = text.length();
@@ -351,7 +370,8 @@
                 throw e;
             } else {
                 Log.e(TAG, "Exception while spellcheking", e);
-                return AndroidSpellCheckerService.getNotInDictEmptySuggestions();
+                return AndroidSpellCheckerService.getNotInDictEmptySuggestions(
+                        false /* reportAsTypo */);
             }
         }
     }
diff --git a/java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java b/java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java
index ea32a74..635afe7 100644
--- a/java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java
@@ -49,7 +49,16 @@
     private static final String LAST_UPDATED_TIME_KEY = "date";
 
     public interface OnAddWordListener {
-        public void setUnigram(final String word, final String shortcutTarget, final int frequency);
+        /**
+         * Callback to be notified when a word is added to the dictionary.
+         * @param word The added word.
+         * @param shortcutTarget A shortcut target for this word, or null if none.
+         * @param frequency The frequency for this word.
+         * @param shortcutFreq The frequency of the shortcut (0~15, with 15 = whitelist).
+         *   Unspecified if shortcutTarget is null - do not rely on its value.
+         */
+        public void setUnigram(final String word, final String shortcutTarget, final int frequency,
+                final int shortcutFreq);
         public void setBigram(final String word1, final String word2, final int frequency);
     }
 
@@ -153,7 +162,7 @@
         for (Entry<Integer, String> entry : unigrams.entrySet()) {
             final String word1 = entry.getValue();
             final int unigramFrequency = frequencies.get(entry.getKey());
-            to.setUnigram(word1, null, unigramFrequency);
+            to.setUnigram(word1, null /* shortcutTarget */, unigramFrequency, 0 /* shortcutFreq */);
             final ArrayList<PendingAttribute> attrList = bigrams.get(entry.getKey());
             if (attrList != null) {
                 for (final PendingAttribute attr : attrList) {
diff --git a/native/jni/src/suggest/core/dictionary/shortcut_utils.h b/native/jni/src/suggest/core/dictionary/shortcut_utils.h
index 461d7b4..9ccef02 100644
--- a/native/jni/src/suggest/core/dictionary/shortcut_utils.h
+++ b/native/jni/src/suggest/core/dictionary/shortcut_utils.h
@@ -44,7 +44,7 @@
                 shortcutScore = finalScore;
                 // Protection against int underflow
                 shortcutScore = max(S_INT_MIN + 1, shortcutScore) - 1;
-                kind = Dictionary::KIND_CORRECTION;
+                kind = Dictionary::KIND_SHORTCUT;
             }
             outputTypes[outputWordIndex] = kind;
             frequencies[outputWordIndex] = shortcutScore;
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_gc_event_listeners.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_gc_event_listeners.cpp
index a17a0ac..5724c5d 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_gc_event_listeners.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_gc_event_listeners.cpp
@@ -39,7 +39,7 @@
             return false;
         }
         if (!ForgettingCurveUtils::isValidEncodedProbability(newProbability)) {
-            isUselessPtNode = false;
+            isUselessPtNode = true;
         }
     }
     if (mChildrenValue > 0) {
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_gc_event_listeners.h b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_gc_event_listeners.h
index 3ca2f2a..9755120 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_gc_event_listeners.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_gc_event_listeners.h
@@ -60,6 +60,7 @@
 
         bool onDescend(const int ptNodeArrayPos) {
             mValueStack.push_back(0);
+            mChildrenValue = 0;
             return true;
         }
 
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp
index 31e3fb4..3d07c9d 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp
@@ -37,6 +37,8 @@
 // BinaryDictionaryDecayingTests.
 const char *const DynamicPatriciaTriePolicy::UNIGRAM_COUNT_QUERY = "UNIGRAM_COUNT";
 const char *const DynamicPatriciaTriePolicy::BIGRAM_COUNT_QUERY = "BIGRAM_COUNT";
+const char *const DynamicPatriciaTriePolicy::MAX_UNIGRAM_COUNT_QUERY = "MAX_UNIGRAM_COUNT";
+const char *const DynamicPatriciaTriePolicy::MAX_BIGRAM_COUNT_QUERY = "MAX_BIGRAM_COUNT";
 const char *const DynamicPatriciaTriePolicy::SET_NEEDS_TO_DECAY_FOR_TESTING_QUERY =
         "SET_NEEDS_TO_DECAY_FOR_TESTING";
 const int DynamicPatriciaTriePolicy::MAX_DICT_EXTENDED_REGION_SIZE = 1024 * 1024;
@@ -355,6 +357,14 @@
         snprintf(outResult, maxResultLength, "%d", mUnigramCount);
     } else if (strncmp(query, BIGRAM_COUNT_QUERY, maxResultLength) == 0) {
         snprintf(outResult, maxResultLength, "%d", mBigramCount);
+    } else if (strncmp(query, MAX_UNIGRAM_COUNT_QUERY, maxResultLength) == 0) {
+        snprintf(outResult, maxResultLength, "%d",
+                mHeaderPolicy.isDecayingDict() ? ForgettingCurveUtils::MAX_UNIGRAM_COUNT :
+                        DynamicPatriciaTrieWritingHelper::MAX_DICTIONARY_SIZE);
+    } else if (strncmp(query, MAX_BIGRAM_COUNT_QUERY, maxResultLength) == 0) {
+        snprintf(outResult, maxResultLength, "%d",
+                mHeaderPolicy.isDecayingDict() ? ForgettingCurveUtils::MAX_BIGRAM_COUNT :
+                        DynamicPatriciaTrieWritingHelper::MAX_DICTIONARY_SIZE);
     } else if (strncmp(query, SET_NEEDS_TO_DECAY_FOR_TESTING_QUERY, maxResultLength) == 0) {
         mNeedsToDecayForTesting = true;
     }
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.h
index 903f65e..be97ee1 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.h
@@ -102,6 +102,8 @@
 
     static const char *const UNIGRAM_COUNT_QUERY;
     static const char *const BIGRAM_COUNT_QUERY;
+    static const char *const MAX_UNIGRAM_COUNT_QUERY;
+    static const char *const MAX_BIGRAM_COUNT_QUERY;
     static const char *const SET_NEEDS_TO_DECAY_FOR_TESTING_QUERY;
     static const int MAX_DICT_EXTENDED_REGION_SIZE;
     static const int MIN_DICT_SIZE_TO_REFUSE_DYNAMIC_OPERATIONS;
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.cpp
index 601ee66..f108c21 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.cpp
@@ -93,6 +93,12 @@
     if (!listener->onDescend(getPosOfLastPtNodeArrayHead())) {
         return false;
     }
+    if (isEnd()) {
+        // Empty dictionary. Needs to notify the listener of the tail of empty PtNode array.
+        if (!listener->onReadingPtNodeArrayTail()) {
+            return false;
+        }
+    }
     pushReadingStateToStack();
     while (!isEnd()) {
         if (alreadyVisitedAllPtNodesInArray) {
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.h b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.h
index 512a4d8..a71c069 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.h
@@ -279,7 +279,9 @@
         } else {
             mReadingState = mReadingStateStack.back();
             mReadingStateStack.pop_back();
-            fetchPtNodeInfo();
+            if (!isEnd()) {
+                fetchPtNodeInfo();
+            }
         }
     }
 };
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp
index 067c8ec..052558b 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp
@@ -240,7 +240,8 @@
             int parentOffsetFieldPos = nodeReader->getHeadPos()
                     + DynamicPatriciaTrieWritingUtils::NODE_FLAG_FIELD_SIZE;
             if (!DynamicPatriciaTrieWritingUtils::writeParentPosOffsetAndAdvancePosition(
-                    mBuffer, movedPos, nodeReader->getHeadPos(), &parentOffsetFieldPos)) {
+                    mBuffer, bigramLinkedNodePos, nodeReader->getHeadPos(),
+                    &parentOffsetFieldPos)) {
                 // Parent offset cannot be written because of a bug or a broken dictionary; thus,
                 // we give up to update dictionary.
                 return false;
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.cpp
index 19ca354..1632fd0 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.cpp
@@ -93,8 +93,7 @@
     for (int i = 0; i < decayIterationCount; ++i) {
         const float currentRate = static_cast<float>(currentEncodedProbability)
                 / static_cast<float>(MAX_ENCODED_PROBABILITY);
-        const float thresholdToDecay = MIN_PROBABILITY_TO_DECAY
-                + (1.0f - MIN_PROBABILITY_TO_DECAY) * currentRate;
+        const float thresholdToDecay = (1.0f - MIN_PROBABILITY_TO_DECAY) * currentRate;
         const float randValue = static_cast<float>(rand()) / static_cast<float>(RAND_MAX);
         if (thresholdToDecay < randValue) {
             currentEncodedProbability = max(currentEncodedProbability - ENCODED_PROBABILITY_STEP,
diff --git a/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp b/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp
index 66637ac..104eb2a 100644
--- a/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp
+++ b/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp
@@ -35,7 +35,7 @@
 const float ScoringParams::OMISSION_COST_SAME_CHAR = 0.399f;
 const float ScoringParams::OMISSION_COST_FIRST_CHAR = 0.5256f;
 const float ScoringParams::INSERTION_COST = 0.7248f;
-const float ScoringParams::TERMINAL_INSERTION_COST = 0.9828f;
+const float ScoringParams::TERMINAL_INSERTION_COST = 0.8128f;
 const float ScoringParams::INSERTION_COST_SAME_CHAR = 0.5508f;
 const float ScoringParams::INSERTION_COST_PROXIMITY_CHAR = 0.674f;
 const float ScoringParams::INSERTION_COST_FIRST_CHAR = 0.639f;
@@ -43,10 +43,10 @@
 const float ScoringParams::SPACE_SUBSTITUTION_COST = 0.339f;
 const float ScoringParams::ADDITIONAL_PROXIMITY_COST = 0.4576f;
 const float ScoringParams::SUBSTITUTION_COST = 0.3806f;
-const float ScoringParams::COST_NEW_WORD = 0.0292f;
+const float ScoringParams::COST_NEW_WORD = 0.0312f;
 const float ScoringParams::COST_SECOND_OR_LATER_WORD_FIRST_CHAR_UPPERCASE = 0.3224f;
 const float ScoringParams::DISTANCE_WEIGHT_LANGUAGE = 1.1214f;
-const float ScoringParams::COST_FIRST_LOOKAHEAD = 0.4786f;
+const float ScoringParams::COST_FIRST_LOOKAHEAD = 0.4836f;
 const float ScoringParams::COST_LOOKAHEAD = 0.00624f;
 const float ScoringParams::HAS_PROXIMITY_TERMINAL_COST = 0.06836f;
 const float ScoringParams::HAS_EDIT_CORRECTION_TERMINAL_COST = 0.0362f;
diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
index ded8eaa..cd5384e 100644
--- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
+++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
@@ -18,14 +18,18 @@
 
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Pair;
 
+import com.android.inputmethod.latin.makedict.CodePointUtils;
 import com.android.inputmethod.latin.makedict.FormatSpec;
 
 import java.io.File;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Random;
 
 @LargeTest
 public class BinaryDictionaryDecayingTests extends AndroidTestCase {
@@ -121,11 +125,16 @@
         binaryDictionary.addBigramWords("a", "c", DUMMY_PROBABILITY);
         assertTrue(binaryDictionary.isValidBigram("a", "c"));
 
+        // Add bigrams of not valid unigrams.
+        binaryDictionary.addBigramWords("x", "y", Dictionary.NOT_A_PROBABILITY);
+        assertFalse(binaryDictionary.isValidBigram("x", "y"));
+        binaryDictionary.addBigramWords("x", "y", DUMMY_PROBABILITY);
+        assertFalse(binaryDictionary.isValidBigram("x", "y"));
+
         binaryDictionary.close();
         dictFile.delete();
     }
 
-    // TODO: Add large tests.
     public void testDecayingProbability() {
         File dictFile = null;
         try {
@@ -179,4 +188,121 @@
         binaryDictionary.close();
         dictFile.delete();
     }
+
+    public void testAddManyUnigramsToDecayingDict() {
+        final int unigramCount = 30000;
+        final int unigramTypedCount = 100000;
+        final int codePointSetSize = 50;
+        final long seed = System.currentTimeMillis();
+        final Random random = new Random(seed);
+
+        File dictFile = null;
+        try {
+            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary");
+        } catch (IOException e) {
+            fail("IOException while writing an initial dictionary : " + e);
+        }
+        BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+
+        final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random);
+        final ArrayList<String> words = new ArrayList<String>();
+
+        for (int i = 0; i < unigramCount; i++) {
+            final String word = CodePointUtils.generateWord(random, codePointSet);
+            words.add(word);
+        }
+
+        final int maxUnigramCount = Integer.parseInt(
+                binaryDictionary.getPropertyForTests(BinaryDictionary.MAX_UNIGRAM_COUNT_QUERY));
+        for (int i = 0; i < unigramTypedCount; i++) {
+            final String word = words.get(random.nextInt(words.size()));
+            binaryDictionary.addUnigramWord(word, DUMMY_PROBABILITY);
+
+            if (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) {
+                final int unigramCountBeforeGC =
+                        Integer.parseInt(binaryDictionary.getPropertyForTests(
+                                BinaryDictionary.UNIGRAM_COUNT_QUERY));
+                while (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) {
+                    binaryDictionary.flushWithGC();
+                }
+                final int unigramCountAfterGC =
+                        Integer.parseInt(binaryDictionary.getPropertyForTests(
+                                BinaryDictionary.UNIGRAM_COUNT_QUERY));
+                assertTrue(unigramCountBeforeGC > unigramCountAfterGC);
+            }
+        }
+
+        assertTrue(Integer.parseInt(binaryDictionary.getPropertyForTests(
+                BinaryDictionary.UNIGRAM_COUNT_QUERY)) > 0);
+        assertTrue(Integer.parseInt(binaryDictionary.getPropertyForTests(
+                BinaryDictionary.UNIGRAM_COUNT_QUERY)) <= maxUnigramCount);
+    }
+
+    public void testAddManyBigramsToDecayingDict() {
+        final int unigramCount = 5000;
+        final int bigramCount = 30000;
+        final int bigramTypedCount = 100000;
+        final int codePointSetSize = 50;
+        final long seed = System.currentTimeMillis();
+        final Random random = new Random(seed);
+
+        File dictFile = null;
+        try {
+            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary");
+        } catch (IOException e) {
+            fail("IOException while writing an initial dictionary : " + e);
+        }
+        BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+
+        final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random);
+        final ArrayList<String> words = new ArrayList<String>();
+        final ArrayList<Pair<String, String>> bigrams = new ArrayList<Pair<String, String>>();
+
+        for (int i = 0; i < unigramCount; ++i) {
+            final String word = CodePointUtils.generateWord(random, codePointSet);
+            words.add(word);
+        }
+        for (int i = 0; i < bigramCount; ++i) {
+            final int word0Index = random.nextInt(words.size());
+            int word1Index = random.nextInt(words.size() - 1);
+            if (word1Index >= word0Index) {
+                word1Index += 1;
+            }
+            final String word0 = words.get(word0Index);
+            final String word1 = words.get(word1Index);
+            final Pair<String, String> bigram = new Pair<String, String>(word0, word1);
+            bigrams.add(bigram);
+        }
+
+        final int maxBigramCount = Integer.parseInt(
+                binaryDictionary.getPropertyForTests(BinaryDictionary.MAX_BIGRAM_COUNT_QUERY));
+        for (int i = 0; i < bigramTypedCount; ++i) {
+            final Pair<String, String> bigram = bigrams.get(random.nextInt(bigrams.size()));
+            binaryDictionary.addUnigramWord(bigram.first, DUMMY_PROBABILITY);
+            binaryDictionary.addUnigramWord(bigram.second, DUMMY_PROBABILITY);
+            binaryDictionary.addBigramWords(bigram.first, bigram.second, DUMMY_PROBABILITY);
+
+            if (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) {
+                final int bigramCountBeforeGC =
+                        Integer.parseInt(binaryDictionary.getPropertyForTests(
+                                BinaryDictionary.BIGRAM_COUNT_QUERY));
+                while (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) {
+                    binaryDictionary.flushWithGC();
+                }
+                final int bigramCountAfterGC =
+                        Integer.parseInt(binaryDictionary.getPropertyForTests(
+                                BinaryDictionary.BIGRAM_COUNT_QUERY));
+                assertTrue(bigramCountBeforeGC > bigramCountAfterGC);
+            }
+        }
+
+        assertTrue(Integer.parseInt(binaryDictionary.getPropertyForTests(
+                BinaryDictionary.BIGRAM_COUNT_QUERY)) > 0);
+        assertTrue(Integer.parseInt(binaryDictionary.getPropertyForTests(
+                BinaryDictionary.BIGRAM_COUNT_QUERY)) <= maxBigramCount);
+    }
 }
diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java
index 6a21522..5b8f0e9 100644
--- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java
+++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java
@@ -18,6 +18,7 @@
 
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.LargeTest;
+import android.text.TextUtils;
 import android.util.Pair;
 
 import com.android.inputmethod.latin.makedict.CodePointUtils;
@@ -126,7 +127,7 @@
     public void testRandomlyAddUnigramWord() {
         final int wordCount = 1000;
         final int codePointSetSize = 50;
-        final int seed = 123456789;
+        final long seed = System.currentTimeMillis();
 
         File dictFile = null;
         try {
@@ -223,7 +224,8 @@
         final int wordCount = 100;
         final int bigramCount = 1000;
         final int codePointSetSize = 50;
-        final int seed = 11111;
+        final long seed = System.currentTimeMillis();
+        final Random random = new Random(seed);
 
         File dictFile = null;
         try {
@@ -234,43 +236,42 @@
         BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
                 0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
                 Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+
         final ArrayList<String> words = new ArrayList<String>();
-        // Test a word that isn't contained within the dictionary.
-        final Random random = new Random(seed);
+        final ArrayList<Pair<String, String>> bigramWords = new ArrayList<Pair<String,String>>();
         final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random);
-        final int[] unigramProbabilities = new int[wordCount];
+        final HashMap<String, Integer> unigramProbabilities = new HashMap<String, Integer>();
+        final HashMap<Pair<String, String>, Integer> bigramProbabilities =
+                new HashMap<Pair<String, String>, Integer>();
+
         for (int i = 0; i < wordCount; ++i) {
             final String word = CodePointUtils.generateWord(random, codePointSet);
             words.add(word);
             final int unigramProbability = random.nextInt(0xFF);
-            unigramProbabilities[i] = unigramProbability;
+            unigramProbabilities.put(word, unigramProbability);
             binaryDictionary.addUnigramWord(word, unigramProbability);
         }
 
-        final int[][] probabilities = new int[wordCount][wordCount];
-
-        for (int i = 0; i < wordCount; ++i) {
-            for (int j = 0; j < wordCount; ++j) {
-                probabilities[i][j] = Dictionary.NOT_A_PROBABILITY;
-            }
-        }
-
         for (int i = 0; i < bigramCount; i++) {
-            final int word0Index = random.nextInt(wordCount);
-            final int word1Index = random.nextInt(wordCount);
-            final String word0 = words.get(word0Index);
-            final String word1 = words.get(word1Index);
+            final String word0 = words.get(random.nextInt(wordCount));
+            final String word1 = words.get(random.nextInt(wordCount));
+            if (TextUtils.equals(word0, word1)) {
+                continue;
+            }
+            final Pair<String, String> bigram = new Pair<String, String>(word0, word1);
+            bigramWords.add(bigram);
             final int bigramProbability = random.nextInt(0xF);
-            probabilities[word0Index][word1Index] = binaryDictionary.calculateProbability(
-                    unigramProbabilities[word1Index], bigramProbability);
+            bigramProbabilities.put(bigram, bigramProbability);
             binaryDictionary.addBigramWords(word0, word1, bigramProbability);
         }
 
-        for (int i = 0; i < words.size(); i++) {
-            for (int j = 0; j < words.size(); j++) {
-                assertEquals(probabilities[i][j],
-                        binaryDictionary.getBigramProbability(words.get(i), words.get(j)));
-            }
+        for (final Pair<String, String> bigram : bigramWords) {
+            final int unigramProbability = unigramProbabilities.get(bigram.second);
+            final int bigramProbability = bigramProbabilities.get(bigram);
+            final int probability = binaryDictionary.calculateProbability(unigramProbability,
+                    bigramProbability);
+            assertEquals(probability,
+                    binaryDictionary.getBigramProbability(bigram.first, bigram.second));
         }
 
         dictFile.delete();
@@ -419,8 +420,8 @@
         final int wordCount = 100;
         final int bigramCount = 1000;
         final int codePointSetSize = 30;
-        // TODO: Use various seeds such as a current timestamp to make this test more random.
-        final int seed = 314159265;
+        final long seed = System.currentTimeMillis();
+        final Random random = new Random(seed);
 
         File dictFile = null;
         try {
@@ -432,35 +433,32 @@
         BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
                 0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
                 Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+
         final ArrayList<String> words = new ArrayList<String>();
-        // Test a word that isn't contained within the dictionary.
-        final Random random = new Random(seed);
+        final ArrayList<Pair<String, String>> bigramWords = new ArrayList<Pair<String,String>>();
         final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random);
-        final int[] unigramProbabilities = new int[wordCount];
+        final HashMap<String, Integer> unigramProbabilities = new HashMap<String, Integer>();
+        final HashMap<Pair<String, String>, Integer> bigramProbabilities =
+                new HashMap<Pair<String, String>, Integer>();
+
         for (int i = 0; i < wordCount; ++i) {
             final String word = CodePointUtils.generateWord(random, codePointSet);
             words.add(word);
             final int unigramProbability = random.nextInt(0xFF);
-            unigramProbabilities[i] = unigramProbability;
+            unigramProbabilities.put(word, unigramProbability);
             binaryDictionary.addUnigramWord(word, unigramProbability);
         }
 
-        final int[][] probabilities = new int[wordCount][wordCount];
-
-        for (int i = 0; i < wordCount; ++i) {
-            for (int j = 0; j < wordCount; ++j) {
-                probabilities[i][j] = Dictionary.NOT_A_PROBABILITY;
-            }
-        }
-
         for (int i = 0; i < bigramCount; i++) {
-            final int word0Index = random.nextInt(wordCount);
-            final int word1Index = random.nextInt(wordCount);
-            final String word0 = words.get(word0Index);
-            final String word1 = words.get(word1Index);
+            final String word0 = words.get(random.nextInt(wordCount));
+            final String word1 = words.get(random.nextInt(wordCount));
+            if (TextUtils.equals(word0, word1)) {
+                continue;
+            }
+            final Pair<String, String> bigram = new Pair<String, String>(word0, word1);
+            bigramWords.add(bigram);
             final int bigramProbability = random.nextInt(0xF);
-            probabilities[word0Index][word1Index] = binaryDictionary.calculateProbability(
-                    unigramProbabilities[word1Index], bigramProbability);
+            bigramProbabilities.put(bigram, bigramProbability);
             binaryDictionary.addBigramWords(word0, word1, bigramProbability);
         }
 
@@ -470,12 +468,15 @@
                 0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
                 Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
 
-        for (int i = 0; i < words.size(); i++) {
-            for (int j = 0; j < words.size(); j++) {
-                assertEquals(probabilities[i][j],
-                        binaryDictionary.getBigramProbability(words.get(i), words.get(j)));
-            }
+        for (final Pair<String, String> bigram : bigramWords) {
+            final int unigramProbability = unigramProbabilities.get(bigram.second);
+            final int bigramProbability = bigramProbabilities.get(bigram);
+            final int probability = binaryDictionary.calculateProbability(unigramProbability,
+                    bigramProbability);
+            assertEquals(probability,
+                    binaryDictionary.getBigramProbability(bigram.first, bigram.second));
         }
+
         dictFile.delete();
     }
 
@@ -487,8 +488,8 @@
         final float addBigramProb = 0.8f;
         final float removeBigramProb = 0.2f;
         final int codePointSetSize = 30;
-        final int seed = 141421356;
 
+        final long seed = System.currentTimeMillis();
         final Random random = new Random(seed);
 
         File dictFile = null;
@@ -539,6 +540,9 @@
                     }
                     final String word0 = words.get(word0Index);
                     final String word1 = words.get(word1Index);
+                    if (TextUtils.equals(word0, word1)) {
+                        continue;
+                    }
                     final int bigramProbability = random.nextInt(0xF);
                     final Pair<String, String> bigram = new Pair<String, String>(word0, word1);
                     bigramWords.add(bigram);
@@ -586,8 +590,8 @@
     public void testAddManyUnigramsAndFlushWithGC() {
         final int flashWithGCIterationCount = 3;
         final int codePointSetSize = 50;
-        final int seed = 22360679;
 
+        final long seed = System.currentTimeMillis();
         final Random random = new Random(seed);
 
         File dictFile = null;
@@ -632,8 +636,7 @@
         final int codePointSetSize = 50;
         final int unigramCountPerIteration = 1000;
         final int bigramCountPerIteration = 2000;
-        final int seed = 1123581321;
-
+        final long seed = System.currentTimeMillis();
         final Random random = new Random(seed);
 
         File dictFile = null;
@@ -661,6 +664,9 @@
             for (int j = 0; j < bigramCountPerIteration; j++) {
                 final String word0 = words.get(random.nextInt(words.size()));
                 final String word1 = words.get(random.nextInt(words.size()));
+                if (TextUtils.equals(word0, word1)) {
+                    continue;
+                }
                 bigrams.add(new Pair<String, String>(word0, word1));
                 final int bigramProbability = random.nextInt(0xF);
                 binaryDictionary.addBigramWords(word0, word1, bigramProbability);
diff --git a/tests/src/com/android/inputmethod/latin/ExpandableDictionaryTests.java b/tests/src/com/android/inputmethod/latin/ExpandableDictionaryTests.java
index ecf3af7..6aae104 100644
--- a/tests/src/com/android/inputmethod/latin/ExpandableDictionaryTests.java
+++ b/tests/src/com/android/inputmethod/latin/ExpandableDictionaryTests.java
@@ -26,13 +26,16 @@
 public class ExpandableDictionaryTests extends AndroidTestCase {
 
     private final static int UNIGRAM_FREQ = 50;
+    // See UserBinaryDictionary for more information about this variable.
+    // For tests, its actual value does not matter.
+    private final static int SHORTCUT_FREQ = 14;
 
     public void testAddWordAndGetWordFrequency() {
         final ExpandableDictionary dict = new ExpandableDictionary(Dictionary.TYPE_USER);
 
         // Add words
-        dict.addWord("abcde", "abcde", UNIGRAM_FREQ);
-        dict.addWord("abcef", null, UNIGRAM_FREQ + 1);
+        dict.addWord("abcde", "abcde", UNIGRAM_FREQ, SHORTCUT_FREQ);
+        dict.addWord("abcef", null, UNIGRAM_FREQ + 1, 0);
 
         // Check words
         assertFalse(dict.isValidWord("abcde"));
@@ -40,16 +43,16 @@
         assertTrue(dict.isValidWord("abcef"));
         assertEquals(UNIGRAM_FREQ+1, dict.getWordFrequency("abcef"));
 
-        dict.addWord("abc", null, UNIGRAM_FREQ + 2);
+        dict.addWord("abc", null, UNIGRAM_FREQ + 2, 0);
         assertTrue(dict.isValidWord("abc"));
         assertEquals(UNIGRAM_FREQ + 2, dict.getWordFrequency("abc"));
 
         // Add existing word with lower frequency
-        dict.addWord("abc", null, UNIGRAM_FREQ);
+        dict.addWord("abc", null, UNIGRAM_FREQ, 0);
         assertEquals(UNIGRAM_FREQ + 2, dict.getWordFrequency("abc"));
 
         // Add existing word with higher frequency
-        dict.addWord("abc", null, UNIGRAM_FREQ + 3);
+        dict.addWord("abc", null, UNIGRAM_FREQ + 3, 0);
         assertEquals(UNIGRAM_FREQ + 3, dict.getWordFrequency("abc"));
     }
 }
diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTests.java b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
index cc2569f..6bc8b9d 100644
--- a/tests/src/com/android/inputmethod/latin/InputLogicTests.java
+++ b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
@@ -179,10 +179,17 @@
     }
 
     public void testDoubleSpace() {
-        final String STRING_TO_TYPE = "this  ";
-        final String EXPECTED_RESULT = "this. ";
-        type(STRING_TO_TYPE);
-        assertEquals("double space make a period", EXPECTED_RESULT, mEditText.getText().toString());
+        // U+1F607 is an emoji
+        final String[] STRINGS_TO_TYPE =
+                new String[] { "this   ", "a+  ", "\u1F607  ", "..  ", ")  ", "(  ", "%  " };
+        final String[] EXPECTED_RESULTS =
+                new String[] { "this.  ", "a+. ", "\u1F607. ", "..  ", "). ", "(  ", "%  " };
+        for (int i = 0; i < STRINGS_TO_TYPE.length; ++i) {
+            mEditText.setText("");
+            type(STRINGS_TO_TYPE[i]);
+            assertEquals("double space processing", EXPECTED_RESULTS[i],
+                    mEditText.getText().toString());
+        }
     }
 
     public void testCancelDoubleSpace() {
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
index aa16583..0189b33 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
@@ -646,7 +646,7 @@
         }
     }
 
-    public void testDeleteWord() {
+    private void runTestDeleteWord(final FormatOptions formatOptions) {
         final String dictName = "testDeleteWord";
         final String dictVersion = Long.toString(System.currentTimeMillis());
         final File file = setUpDictionaryFile(dictName, dictVersion);
@@ -655,10 +655,17 @@
                 new FusionDictionary.DictionaryOptions(
                         new HashMap<String, String>(), false, false));
         addUnigrams(sWords.size(), dict, sWords, null /* shortcutMap */);
-        timeWritingDictToFile(file, dict, VERSION3_WITH_DYNAMIC_UPDATE);
+        timeWritingDictToFile(file, dict, formatOptions);
 
-        final Ver3DictUpdater dictUpdater = new Ver3DictUpdater(file,
-                DictDecoder.USE_WRITABLE_BYTEBUFFER);
+        final DictUpdater dictUpdater;
+        if (formatOptions.mVersion == 3) {
+            dictUpdater = new Ver3DictUpdater(file, DictDecoder.USE_WRITABLE_BYTEBUFFER);
+        } else if (formatOptions.mVersion == 4) {
+            dictUpdater = new Ver4DictUpdater(file, DictDecoder.USE_WRITABLE_BYTEBUFFER);
+        } else {
+            throw new RuntimeException("DictUpdater for version " + formatOptions.mVersion
+                    + " doesn't exist.");
+        }
 
         try {
             MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD,
@@ -676,4 +683,9 @@
         } catch (UnsupportedFormatException e) {
         }
     }
+
+    public void testDeleteWord() {
+        runTestDeleteWord(VERSION3_WITH_DYNAMIC_UPDATE);
+        runTestDeleteWord(VERSION4_WITH_DYNAMIC_UPDATE);
+    }
 }
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java
index acd6585..afe5adb 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java
@@ -45,6 +45,9 @@
 
     private static final String TEST_DICT_FILE_EXTENSION = ".testDict";
 
+    private static final int VERSION3 = 3;
+    private static final int VERSION4 = 4;
+
     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",
@@ -183,11 +186,16 @@
     // return amount of time to insert a word
     private long insertAndCheckWord(final File file, final String word, final int frequency,
             final boolean exist, final ArrayList<WeightedString> bigrams,
-            final ArrayList<WeightedString> shortcuts) {
+            final ArrayList<WeightedString> shortcuts, final int formatVersion) {
         long amountOfTime = -1;
         try {
-            final Ver3DictUpdater dictUpdater = new Ver3DictUpdater(file,
-                    DictDecoder.USE_WRITABLE_BYTEBUFFER);
+            final DictUpdater dictUpdater;
+            if (formatVersion == VERSION3) {
+                dictUpdater = new Ver3DictUpdater(file, DictDecoder.USE_WRITABLE_BYTEBUFFER);
+            } else {
+                throw new RuntimeException("DictUpdater for version " + formatVersion + " doesn't"
+                        + " exist.");
+            }
 
             if (!exist) {
                 assertEquals(FormatSpec.NOT_VALID_WORD, getWordPosition(file, word));
@@ -204,10 +212,15 @@
         return amountOfTime;
     }
 
-    private void deleteWord(final File file, final String word) {
+    private void deleteWord(final File file, final String word, final int formatVersion) {
         try {
-            final Ver3DictUpdater dictUpdater = new Ver3DictUpdater(file,
-                    DictDecoder.USE_WRITABLE_BYTEBUFFER);
+            final DictUpdater dictUpdater;
+            if (formatVersion == VERSION3) {
+                dictUpdater = new Ver3DictUpdater(file, DictDecoder.USE_WRITABLE_BYTEBUFFER);
+            } else {
+                throw new RuntimeException("DictUpdater for version " + formatVersion + " doesn't"
+                        + " exist.");
+            }
             dictUpdater.deleteWord(word);
         } catch (IOException e) {
         } catch (UnsupportedFormatException e) {
@@ -229,7 +242,7 @@
         }
     }
 
-    public void testInsertWord() {
+    private void runTestInsertWord(final int formatVersion) {
         File file = null;
         try {
             file = File.createTempFile("testInsertWord", TEST_DICT_FILE_EXTENSION,
@@ -253,33 +266,37 @@
         }
 
         MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD, getWordPosition(file, "abcd"));
-        insertAndCheckWord(file, "abcde", 10, false, null, null);
+        insertAndCheckWord(file, "abcde", 10, false, null, null, formatVersion);
 
-        insertAndCheckWord(file, "abcdefghijklmn", 10, false, null, null);
+        insertAndCheckWord(file, "abcdefghijklmn", 10, false, null, null, formatVersion);
         checkReverseLookup(file, "abcdefghijklmn", getWordPosition(file, "abcdefghijklmn"));
 
-        insertAndCheckWord(file, "abcdabcd", 10, false, null, null);
+        insertAndCheckWord(file, "abcdabcd", 10, false, null, null, formatVersion);
         checkReverseLookup(file, "abcdabcd", getWordPosition(file, "abcdabcd"));
 
         // update the existing word.
-        insertAndCheckWord(file, "abcdabcd", 15, true, null, null);
+        insertAndCheckWord(file, "abcdabcd", 15, true, null, null, formatVersion);
 
         // split 1
-        insertAndCheckWord(file, "ab", 20, false, null, null);
+        insertAndCheckWord(file, "ab", 20, false, null, null, formatVersion);
 
         // split 2
-        insertAndCheckWord(file, "ami", 30, false, null, null);
+        insertAndCheckWord(file, "ami", 30, false, null, null, formatVersion);
 
-        deleteWord(file, "ami");
+        deleteWord(file, "ami", formatVersion);
         assertEquals(FormatSpec.NOT_VALID_WORD, getWordPosition(file, "ami"));
 
-        insertAndCheckWord(file, "abcdabfg", 30, false, null, null);
+        insertAndCheckWord(file, "abcdabfg", 30, false, null, null, formatVersion);
 
-        deleteWord(file, "abcd");
+        deleteWord(file, "abcd", formatVersion);
         assertEquals(FormatSpec.NOT_VALID_WORD, getWordPosition(file, "abcd"));
     }
 
-    public void testInsertWordWithBigrams() {
+    public void testInsertWord() {
+        runTestInsertWord(VERSION3);
+    }
+
+    private void runTestInsertWordWithBigrams(final int formatVersion) {
         File file = null;
         try {
             file = File.createTempFile("testInsertWordWithBigrams", TEST_DICT_FILE_EXTENSION,
@@ -306,8 +323,8 @@
         final ArrayList<WeightedString> banana = new ArrayList<WeightedString>();
         banana.add(new WeightedString("banana", 10));
 
-        insertAndCheckWord(file, "banana", 0, false, null, null);
-        insertAndCheckWord(file, "recursive", 60, true, banana, null);
+        insertAndCheckWord(file, "banana", 0, false, null, null, formatVersion);
+        insertAndCheckWord(file, "recursive", 60, true, banana, null, formatVersion);
 
         final PtNodeInfo info = findWordFromFile(file, "recursive");
         int bananaPos = getWordPosition(file, "banana");
@@ -316,7 +333,11 @@
         assertEquals(info.mBigrams.get(0).mAddress, bananaPos);
     }
 
-    public void testRandomWords() {
+    public void testInsertWordWithBigrams() {
+        runTestInsertWordWithBigrams(VERSION3);
+    }
+
+    private void runTestRandomWords(final int formatVersion) {
         File file = null;
         try {
             file = File.createTempFile("testRandomWord", TEST_DICT_FILE_EXTENSION,
@@ -345,7 +366,7 @@
         int cnt = 0;
         for (final String word : sWords) {
             final long diff = insertAndCheckWord(file, word,
-                    cnt % FormatSpec.MAX_TERMINAL_FREQUENCY, false, null, null);
+                    cnt % FormatSpec.MAX_TERMINAL_FREQUENCY, false, null, null, formatVersion);
             maxTimeToInsert = Math.max(maxTimeToInsert, diff);
             minTimeToInsert = Math.min(minTimeToInsert, diff);
             sum += diff;
@@ -356,8 +377,13 @@
             MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD, getWordPosition(file, word));
         }
 
+        Log.d(TAG, "Test version " + formatVersion);
         Log.d(TAG, "max = " + ((double)maxTimeToInsert/1000000) + " ms.");
         Log.d(TAG, "min = " + ((double)minTimeToInsert/1000000) + " ms.");
         Log.d(TAG, "avg = " + ((double)sum/mMaxUnigrams/1000000) + " ms.");
     }
+
+    public void testRandomWords() {
+        runTestRandomWords(VERSION3);
+    }
 }
diff --git a/tests/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtilsTests.java
index 3eabe2b..1944fd3 100644
--- a/tests/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtilsTests.java
@@ -196,8 +196,8 @@
         final UserHistoryDictionaryBigramList resultList = new UserHistoryDictionaryBigramList();
         final OnAddWordListener listener = new OnAddWordListener() {
             @Override
-            public void setUnigram(final String word,
-                    final String shortcutTarget, final int frequency) {
+            public void setUnigram(final String word, final String shortcutTarget,
+                    final int frequency, final int shortcutFreq) {
                 Log.d(TAG, "in: setUnigram: " + word + "," + frequency);
                 resultList.addBigram(null, word, (byte)frequency);
             }
@@ -220,8 +220,8 @@
         final UserHistoryDictionaryBigramList resultList2 = new UserHistoryDictionaryBigramList();
         final OnAddWordListener listener2 = new OnAddWordListener() {
             @Override
-            public void setUnigram(final String word,
-                    final String shortcutTarget, final int frequency) {
+            public void setUnigram(final String word, final String shortcutTarget,
+                    final int frequency, final int shortcutFreq) {
                 Log.d(TAG, "in: setUnigram: " + word + "," + frequency);
                 resultList2.addBigram(null, word, (byte)frequency);
             }
diff --git a/tools/make-keyboard-text/res/values/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values/donottranslate-more-keys.xml
index 44aa64c..39ecfab 100644
--- a/tools/make-keyboard-text/res/values/donottranslate-more-keys.xml
+++ b/tools/make-keyboard-text/res/values/donottranslate-more-keys.xml
@@ -246,4 +246,5 @@
     <string name="more_keys_for_single_quote">!fixedColumnOrder!5,!text/single_quotes,!text/single_angle_quotes</string>
     <string name="more_keys_for_double_quote">!fixedColumnOrder!5,!text/double_quotes,!text/double_angle_quotes</string>
     <string name="more_keys_for_tablet_double_quote">!fixedColumnOrder!6,!text/double_quotes,!text/single_quotes,!text/double_angle_quotes,!text/single_angle_quotes</string>
+    <string name="emoji_key_as_more_key">!icon/emoji_key|!code/key_emoji</string>
 </resources>