Merge "[IL128] Remove passing some handlers."
diff --git a/java/res/values-af/strings.xml b/java/res/values-af/strings.xml
index 59494aa..9c9f022 100644
--- a/java/res/values-af/strings.xml
+++ b/java/res/values-af/strings.xml
@@ -93,7 +93,8 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Oortjie"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Spasie"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Steminvoering"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Glimlag-gesiggie"</string>
+    <!-- no translation found for spoken_description_emoji (6934027701390427635) -->
+    <skip />
     <string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Soek"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Punt"</string>
diff --git a/java/res/values-am/strings.xml b/java/res/values-am/strings.xml
index 13fe7f8..3f9b03f 100644
--- a/java/res/values-am/strings.xml
+++ b/java/res/values-am/strings.xml
@@ -93,7 +93,8 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"ትር"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"ባዶ ቦታ"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"የድምፅ ግቤ ት"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"የፈገግታ ፊት"</string>
+    <!-- no translation found for spoken_description_emoji (6934027701390427635) -->
+    <skip />
     <string name="spoken_description_return" msgid="8178083177238315647">"ተመለስ"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"ፍለጋ"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"ነጥብ"</string>
diff --git a/java/res/values-ar/strings.xml b/java/res/values-ar/strings.xml
index f6ac117..9d8cc63 100644
--- a/java/res/values-ar/strings.xml
+++ b/java/res/values-ar/strings.xml
@@ -93,7 +93,7 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"علامة تبويب"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"مسافة"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"إدخال صوتي"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"وجه مبتسم"</string>
+    <string name="spoken_description_emoji" msgid="6934027701390427635">"الرموز التعبيرية"</string>
     <string name="spoken_description_return" msgid="8178083177238315647">"رجوع"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"بحث"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"نقطة"</string>
diff --git a/java/res/values-az-rAZ/strings.xml b/java/res/values-az-rAZ/strings.xml
index 2fe102a..99765a9 100644
--- a/java/res/values-az-rAZ/strings.xml
+++ b/java/res/values-az-rAZ/strings.xml
@@ -93,7 +93,8 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Tab"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Boşluq"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Səs daxiletməsi"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Smaylik"</string>
+    <!-- no translation found for spoken_description_emoji (6934027701390427635) -->
+    <skip />
     <string name="spoken_description_return" msgid="8178083177238315647">"Qayıt"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Axtar"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Nöqtə"</string>
diff --git a/java/res/values-bg/strings.xml b/java/res/values-bg/strings.xml
index 7e86abf..03e8b8c 100644
--- a/java/res/values-bg/strings.xml
+++ b/java/res/values-bg/strings.xml
@@ -93,7 +93,8 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Tab"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Интервал"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Гласово въвеждане"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Усмивка"</string>
+    <!-- no translation found for spoken_description_emoji (6934027701390427635) -->
+    <skip />
     <string name="spoken_description_return" msgid="8178083177238315647">"Return"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Търсене"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Точка"</string>
diff --git a/java/res/values-ca/strings.xml b/java/res/values-ca/strings.xml
index 5afaca0..0845486 100644
--- a/java/res/values-ca/strings.xml
+++ b/java/res/values-ca/strings.xml
@@ -93,7 +93,7 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Tabulador"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Espai"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Entrada de veu"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Cara somrient"</string>
+    <string name="spoken_description_emoji" msgid="6934027701390427635">"Emoji"</string>
     <string name="spoken_description_return" msgid="8178083177238315647">"Retorn"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Cerca"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Punt"</string>
diff --git a/java/res/values-cs/strings.xml b/java/res/values-cs/strings.xml
index e517043..b190857 100644
--- a/java/res/values-cs/strings.xml
+++ b/java/res/values-cs/strings.xml
@@ -93,7 +93,8 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Tabulátor"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Mezerník"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Hlasový vstup"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Smajlík"</string>
+    <!-- no translation found for spoken_description_emoji (6934027701390427635) -->
+    <skip />
     <string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"vyhledávací tlačítko"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Tečka"</string>
diff --git a/java/res/values-da/strings.xml b/java/res/values-da/strings.xml
index 4dccbbf..e87fffd 100644
--- a/java/res/values-da/strings.xml
+++ b/java/res/values-da/strings.xml
@@ -93,7 +93,8 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Tabulatortast"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Mellemrum"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Taleinput"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Smiley"</string>
+    <!-- no translation found for spoken_description_emoji (6934027701390427635) -->
+    <skip />
     <string name="spoken_description_return" msgid="8178083177238315647">"Tilbage"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Søg"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Punktum"</string>
diff --git a/java/res/values-de/strings.xml b/java/res/values-de/strings.xml
index a42f85d..54dca98 100644
--- a/java/res/values-de/strings.xml
+++ b/java/res/values-de/strings.xml
@@ -93,7 +93,7 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Tabulator"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Leerzeichen"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Spracheingabe"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Smiley"</string>
+    <string name="spoken_description_emoji" msgid="6934027701390427635">"Emoji"</string>
     <string name="spoken_description_return" msgid="8178083177238315647">"Eingabe"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Suchen"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Aufzählungspunkt"</string>
diff --git a/java/res/values-el/strings.xml b/java/res/values-el/strings.xml
index 4ea576c..3aee4ad 100644
--- a/java/res/values-el/strings.xml
+++ b/java/res/values-el/strings.xml
@@ -93,7 +93,7 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Πλήκτρο Tab"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Πλήκτρο διαστήματος"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Μικρόφωνο"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Smiley"</string>
+    <string name="spoken_description_emoji" msgid="6934027701390427635">"Emoticon"</string>
     <string name="spoken_description_return" msgid="8178083177238315647">"Πλήκτρο Return"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Αναζήτηση"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Κουκκίδα"</string>
diff --git a/java/res/values-en-rGB/strings.xml b/java/res/values-en-rGB/strings.xml
index a84b389..5d1ec87 100644
--- a/java/res/values-en-rGB/strings.xml
+++ b/java/res/values-en-rGB/strings.xml
@@ -93,7 +93,7 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Tab"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Space"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Voice input"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Smiley face"</string>
+    <string name="spoken_description_emoji" msgid="6934027701390427635">"Emoji"</string>
     <string name="spoken_description_return" msgid="8178083177238315647">"Return"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Search"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Dot"</string>
diff --git a/java/res/values-en-rIN/strings.xml b/java/res/values-en-rIN/strings.xml
index a84b389..5d1ec87 100644
--- a/java/res/values-en-rIN/strings.xml
+++ b/java/res/values-en-rIN/strings.xml
@@ -93,7 +93,7 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Tab"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Space"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Voice input"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Smiley face"</string>
+    <string name="spoken_description_emoji" msgid="6934027701390427635">"Emoji"</string>
     <string name="spoken_description_return" msgid="8178083177238315647">"Return"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Search"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Dot"</string>
diff --git a/java/res/values-es-rUS/strings.xml b/java/res/values-es-rUS/strings.xml
index 50583e8..2e8dc16 100644
--- a/java/res/values-es-rUS/strings.xml
+++ b/java/res/values-es-rUS/strings.xml
@@ -93,7 +93,8 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Pestaña"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Espacio"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Entrada de voz"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Carita sonriente"</string>
+    <!-- no translation found for spoken_description_emoji (6934027701390427635) -->
+    <skip />
     <string name="spoken_description_return" msgid="8178083177238315647">"Volver"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Buscar"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Punto"</string>
diff --git a/java/res/values-es/strings.xml b/java/res/values-es/strings.xml
index 3427751..81a62cd 100644
--- a/java/res/values-es/strings.xml
+++ b/java/res/values-es/strings.xml
@@ -93,7 +93,7 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Tabulador"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Barra espaciadora"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Entrada de voz"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Emoticono"</string>
+    <string name="spoken_description_emoji" msgid="6934027701390427635">"Emojis"</string>
     <string name="spoken_description_return" msgid="8178083177238315647">"Tecla Intro"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Buscar"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Punto"</string>
diff --git a/java/res/values-et-rEE/strings.xml b/java/res/values-et-rEE/strings.xml
index 26ab93e..bd7c6e6 100644
--- a/java/res/values-et-rEE/strings.xml
+++ b/java/res/values-et-rEE/strings.xml
@@ -93,7 +93,7 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Tabulaator"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Tühik"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Kõnesisend"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Naerunägu"</string>
+    <string name="spoken_description_emoji" msgid="6934027701390427635">"Emotikonid"</string>
     <string name="spoken_description_return" msgid="8178083177238315647">"Tagasi"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Otsing"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Punkt"</string>
diff --git a/java/res/values-fa/strings.xml b/java/res/values-fa/strings.xml
index 1ea69cb..14957eb 100644
--- a/java/res/values-fa/strings.xml
+++ b/java/res/values-fa/strings.xml
@@ -97,7 +97,8 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Tab"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"فاصله"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"ورودی صدا"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"صورت متبسم"</string>
+    <!-- no translation found for spoken_description_emoji (6934027701390427635) -->
+    <skip />
     <string name="spoken_description_return" msgid="8178083177238315647">"Return"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"جستجو"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"نقطه"</string>
diff --git a/java/res/values-fi/strings.xml b/java/res/values-fi/strings.xml
index bfa5d01..514e07c 100644
--- a/java/res/values-fi/strings.xml
+++ b/java/res/values-fi/strings.xml
@@ -93,7 +93,8 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Sarkain"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Välilyönti"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Äänisyöte"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Hymiö"</string>
+    <!-- no translation found for spoken_description_emoji (6934027701390427635) -->
+    <skip />
     <string name="spoken_description_return" msgid="8178083177238315647">"Takaisin"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Haku"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Piste"</string>
diff --git a/java/res/values-fr-rCA/strings.xml b/java/res/values-fr-rCA/strings.xml
index cb6daee..b0b4c01 100644
--- a/java/res/values-fr-rCA/strings.xml
+++ b/java/res/values-fr-rCA/strings.xml
@@ -93,7 +93,8 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Onglet"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Espace"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Saisie vocale"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Émoticône"</string>
+    <!-- no translation found for spoken_description_emoji (6934027701390427635) -->
+    <skip />
     <string name="spoken_description_return" msgid="8178083177238315647">"Renvoyer"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Rechercher"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Point"</string>
diff --git a/java/res/values-fr/strings.xml b/java/res/values-fr/strings.xml
index e3243c3..1afd4af 100644
--- a/java/res/values-fr/strings.xml
+++ b/java/res/values-fr/strings.xml
@@ -93,7 +93,8 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Tabulation"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Espace"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Saisie vocale"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Émoticône"</string>
+    <!-- no translation found for spoken_description_emoji (6934027701390427635) -->
+    <skip />
     <string name="spoken_description_return" msgid="8178083177238315647">"Entrée"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Rechercher"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Point"</string>
diff --git a/java/res/values-hi/strings.xml b/java/res/values-hi/strings.xml
index b752d61..28e7b9c 100644
--- a/java/res/values-hi/strings.xml
+++ b/java/res/values-hi/strings.xml
@@ -93,7 +93,7 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"टैब"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"स्पेस"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"ध्‍वनि इनपुट"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"मुस्कुराता चेहरा"</string>
+    <string name="spoken_description_emoji" msgid="6934027701390427635">"ईमोजी"</string>
     <string name="spoken_description_return" msgid="8178083177238315647">"रिटर्न"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"खोजें"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"बिंदु"</string>
diff --git a/java/res/values-hr/strings.xml b/java/res/values-hr/strings.xml
index 6d7e191..93435b7 100644
--- a/java/res/values-hr/strings.xml
+++ b/java/res/values-hr/strings.xml
@@ -93,7 +93,7 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Kartica"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Razmaknica"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Glasovni unos"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Smješko"</string>
+    <string name="spoken_description_emoji" msgid="6934027701390427635">"Emoji"</string>
     <string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Pretraživanje"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Točka"</string>
diff --git a/java/res/values-hu/strings.xml b/java/res/values-hu/strings.xml
index 88fd3d6..b0ccba6 100644
--- a/java/res/values-hu/strings.xml
+++ b/java/res/values-hu/strings.xml
@@ -93,7 +93,7 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Tab"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Szóköz"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Hangbevitel"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Mosolygós arc"</string>
+    <string name="spoken_description_emoji" msgid="6934027701390427635">"Hangulatjel"</string>
     <string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Keresés"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Pont"</string>
diff --git a/java/res/values-hy-rAM/strings.xml b/java/res/values-hy-rAM/strings.xml
index f24ea45..975b7b3 100644
--- a/java/res/values-hy-rAM/strings.xml
+++ b/java/res/values-hy-rAM/strings.xml
@@ -93,7 +93,8 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Tab"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Բացակ"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Ձայնային մուտքագրում"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Ժպիտ"</string>
+    <!-- no translation found for spoken_description_emoji (6934027701390427635) -->
+    <skip />
     <string name="spoken_description_return" msgid="8178083177238315647">"Վերադարձ"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Որոնել"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Կետ"</string>
diff --git a/java/res/values-in/strings.xml b/java/res/values-in/strings.xml
index 9964daa..48b4f9d 100644
--- a/java/res/values-in/strings.xml
+++ b/java/res/values-in/strings.xml
@@ -93,7 +93,7 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Tab"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Spasi"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Masukan suara"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Wajah tersenyum"</string>
+    <string name="spoken_description_emoji" msgid="6934027701390427635">"Emoji"</string>
     <string name="spoken_description_return" msgid="8178083177238315647">"Kembali"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Telusuri"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Titik"</string>
diff --git a/java/res/values-it/strings.xml b/java/res/values-it/strings.xml
index 78f3987..6cdbef7 100644
--- a/java/res/values-it/strings.xml
+++ b/java/res/values-it/strings.xml
@@ -93,7 +93,7 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Tabulazione"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Spazio"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Input vocale"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Smile"</string>
+    <string name="spoken_description_emoji" msgid="6934027701390427635">"Emoji"</string>
     <string name="spoken_description_return" msgid="8178083177238315647">"Invio"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Cerca"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Pallino"</string>
diff --git a/java/res/values-iw/strings.xml b/java/res/values-iw/strings.xml
index accf809..d9178b2 100644
--- a/java/res/values-iw/strings.xml
+++ b/java/res/values-iw/strings.xml
@@ -93,7 +93,7 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"כרטיסייה"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"רווח"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"קלט קולי"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"סמיילי"</string>
+    <string name="spoken_description_emoji" msgid="6934027701390427635">"אמוג\'י"</string>
     <string name="spoken_description_return" msgid="8178083177238315647">"חזרה"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"חפש"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"נקודה"</string>
diff --git a/java/res/values-ja/strings.xml b/java/res/values-ja/strings.xml
index add4449..76ba650 100644
--- a/java/res/values-ja/strings.xml
+++ b/java/res/values-ja/strings.xml
@@ -93,7 +93,8 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Tab"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Space"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"音声入力"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"顔文字"</string>
+    <!-- no translation found for spoken_description_emoji (6934027701390427635) -->
+    <skip />
     <string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"検索"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"中点"</string>
diff --git a/java/res/values-ka-rGE/strings.xml b/java/res/values-ka-rGE/strings.xml
index 8fe415d..dcb8061 100644
--- a/java/res/values-ka-rGE/strings.xml
+++ b/java/res/values-ka-rGE/strings.xml
@@ -93,7 +93,8 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Tab"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"შორისი"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"ხმოვანი შეყვანა"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"ღიმილი"</string>
+    <!-- no translation found for spoken_description_emoji (6934027701390427635) -->
+    <skip />
     <string name="spoken_description_return" msgid="8178083177238315647">"დაბრუნება"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"ძიება"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"წერტილი"</string>
diff --git a/java/res/values-km-rKH/strings.xml b/java/res/values-km-rKH/strings.xml
index 71242b6..5e519f5 100644
--- a/java/res/values-km-rKH/strings.xml
+++ b/java/res/values-km-rKH/strings.xml
@@ -93,7 +93,7 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Tab"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"ដកឃ្លា"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"បញ្ចូលសំឡេង"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"មុខ​ញញឹម"</string>
+    <string name="spoken_description_emoji" msgid="6934027701390427635">"សញ្ញា​អារម្មណ៍"</string>
     <string name="spoken_description_return" msgid="8178083177238315647">"Return"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"ស្វែងរក"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Dot"</string>
diff --git a/java/res/values-ko/strings.xml b/java/res/values-ko/strings.xml
index 5d21d2a..30907b6 100644
--- a/java/res/values-ko/strings.xml
+++ b/java/res/values-ko/strings.xml
@@ -93,7 +93,8 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"탭"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"스페이스"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"음성 입력"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"웃는 얼굴"</string>
+    <!-- no translation found for spoken_description_emoji (6934027701390427635) -->
+    <skip />
     <string name="spoken_description_return" msgid="8178083177238315647">"리턴 키"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"검색"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"점"</string>
diff --git a/java/res/values-lo-rLA/strings.xml b/java/res/values-lo-rLA/strings.xml
index dae75e3..445d695 100644
--- a/java/res/values-lo-rLA/strings.xml
+++ b/java/res/values-lo-rLA/strings.xml
@@ -93,7 +93,7 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"ແທັບ"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"ຍະຫວ່າງ"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"ການປ້ອນຂໍ້ມູນດ້ວຍສຽງ"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"ຮອຍຍິ້ມ"</string>
+    <string name="spoken_description_emoji" msgid="6934027701390427635">"ອີໂມຈິ"</string>
     <string name="spoken_description_return" msgid="8178083177238315647">"ກັບຄືນ"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"ຊອກຫາ"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"ຈ້ຳ"</string>
diff --git a/java/res/values-lt/strings.xml b/java/res/values-lt/strings.xml
index 331656f..49258fb 100644
--- a/java/res/values-lt/strings.xml
+++ b/java/res/values-lt/strings.xml
@@ -93,7 +93,7 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Skirtukas"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Tarpas"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Įvestis balsu"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Šypsenėlė"</string>
+    <string name="spoken_description_emoji" msgid="6934027701390427635">"Jaustukai"</string>
     <string name="spoken_description_return" msgid="8178083177238315647">"Grįžti"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Ieškoti"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Taškas"</string>
diff --git a/java/res/values-lv/strings.xml b/java/res/values-lv/strings.xml
index ec4a38b..e92d8e3 100644
--- a/java/res/values-lv/strings.xml
+++ b/java/res/values-lv/strings.xml
@@ -93,7 +93,7 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Tabulēšanas taustiņš"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Atstarpes taustiņš"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Balss ievade"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Smaidoša seja"</string>
+    <string name="spoken_description_emoji" msgid="6934027701390427635">"Emocijzīmes"</string>
     <string name="spoken_description_return" msgid="8178083177238315647">"Ievadīšanas taustiņš"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Meklēt"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Punkts"</string>
diff --git a/java/res/values-mn-rMN/strings.xml b/java/res/values-mn-rMN/strings.xml
index 6c97403..684c03f 100644
--- a/java/res/values-mn-rMN/strings.xml
+++ b/java/res/values-mn-rMN/strings.xml
@@ -93,7 +93,7 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Таб"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Хоосон зай"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Дуугаар оруулах"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Инээсэн царай"</string>
+    <string name="spoken_description_emoji" msgid="6934027701390427635">"Emoji"</string>
     <string name="spoken_description_return" msgid="8178083177238315647">"Буцах"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Хайх"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Цэг"</string>
diff --git a/java/res/values-ms-rMY/strings.xml b/java/res/values-ms-rMY/strings.xml
index eef792e..2cbba21 100644
--- a/java/res/values-ms-rMY/strings.xml
+++ b/java/res/values-ms-rMY/strings.xml
@@ -93,7 +93,8 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Tab"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Ruang"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Input suara"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Muka senyum"</string>
+    <!-- no translation found for spoken_description_emoji (6934027701390427635) -->
+    <skip />
     <string name="spoken_description_return" msgid="8178083177238315647">"Kembali"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Cari"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Titik"</string>
diff --git a/java/res/values-nb/strings.xml b/java/res/values-nb/strings.xml
index 6a4eaf4..57a274b 100644
--- a/java/res/values-nb/strings.xml
+++ b/java/res/values-nb/strings.xml
@@ -93,7 +93,7 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Tabulator"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Mellomrom"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Taleinndata"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Smilefjes"</string>
+    <string name="spoken_description_emoji" msgid="6934027701390427635">"Emoji-tegn"</string>
     <string name="spoken_description_return" msgid="8178083177238315647">"Return"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Søk"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Prikk"</string>
diff --git a/java/res/values-ne-rNP/strings.xml b/java/res/values-ne-rNP/strings.xml
index 65d15e3..04a2d09 100644
--- a/java/res/values-ne-rNP/strings.xml
+++ b/java/res/values-ne-rNP/strings.xml
@@ -93,7 +93,8 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"ट्याब"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"स्पेस"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"आवाज इनपुट"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"मुस्कुराएको अनुहार"</string>
+    <!-- no translation found for spoken_description_emoji (6934027701390427635) -->
+    <skip />
     <string name="spoken_description_return" msgid="8178083177238315647">"फर्कनुहोस्"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"खोज्नुहोस्"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"डट"</string>
diff --git a/java/res/values-nl/strings.xml b/java/res/values-nl/strings.xml
index 1968a45..a86c638 100644
--- a/java/res/values-nl/strings.xml
+++ b/java/res/values-nl/strings.xml
@@ -93,7 +93,7 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Tab"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Spatie"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Spraakinvoer"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Smiley-gezichtje"</string>
+    <string name="spoken_description_emoji" msgid="6934027701390427635">"Emoji"</string>
     <string name="spoken_description_return" msgid="8178083177238315647">"Return"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Zoeken"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Stip"</string>
diff --git a/java/res/values-pl/strings.xml b/java/res/values-pl/strings.xml
index a4a6931..607dc26 100644
--- a/java/res/values-pl/strings.xml
+++ b/java/res/values-pl/strings.xml
@@ -93,7 +93,7 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Tab"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Spacja"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Rozpoznawanie mowy"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Uśmiechnięta buźka"</string>
+    <string name="spoken_description_emoji" msgid="6934027701390427635">"Emotikony"</string>
     <string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Szukaj"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Punkt"</string>
diff --git a/java/res/values-pt-rPT/strings.xml b/java/res/values-pt-rPT/strings.xml
index 4311dcd..8b4131c 100644
--- a/java/res/values-pt-rPT/strings.xml
+++ b/java/res/values-pt-rPT/strings.xml
@@ -93,7 +93,8 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Tab"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Espaço"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Entrada de voz"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Cara sorridente"</string>
+    <!-- no translation found for spoken_description_emoji (6934027701390427635) -->
+    <skip />
     <string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Pesquisar"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Ponto"</string>
diff --git a/java/res/values-pt/strings.xml b/java/res/values-pt/strings.xml
index 5bbab46..a74d24a 100644
--- a/java/res/values-pt/strings.xml
+++ b/java/res/values-pt/strings.xml
@@ -93,7 +93,8 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Tab"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Espaço"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Entrada de voz"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Carinha sorridente"</string>
+    <!-- no translation found for spoken_description_emoji (6934027701390427635) -->
+    <skip />
     <string name="spoken_description_return" msgid="8178083177238315647">"Voltar"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Pesquisar"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Ponto"</string>
diff --git a/java/res/values-ro/strings.xml b/java/res/values-ro/strings.xml
index b9c8977..d40f442 100644
--- a/java/res/values-ro/strings.xml
+++ b/java/res/values-ro/strings.xml
@@ -93,7 +93,8 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Tab"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Spaţiu"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Intrare vocală"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Faţă zâmbitoare"</string>
+    <!-- no translation found for spoken_description_emoji (6934027701390427635) -->
+    <skip />
     <string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Căutaţi"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Punct"</string>
diff --git a/java/res/values-ru/strings.xml b/java/res/values-ru/strings.xml
index 2d23263..44cb0fa 100644
--- a/java/res/values-ru/strings.xml
+++ b/java/res/values-ru/strings.xml
@@ -93,7 +93,8 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Клавиша табуляции"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Пробел"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Голосовой ввод"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Смайлик"</string>
+    <!-- no translation found for spoken_description_emoji (6934027701390427635) -->
+    <skip />
     <string name="spoken_description_return" msgid="8178083177238315647">"Клавиша \"Ввод\""</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Поиск"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Точка"</string>
diff --git a/java/res/values-sk/strings.xml b/java/res/values-sk/strings.xml
index d88759d..68df7ae 100644
--- a/java/res/values-sk/strings.xml
+++ b/java/res/values-sk/strings.xml
@@ -93,7 +93,8 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Karta"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Medzerník"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Hlasový vstup"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Usmiata tvár"</string>
+    <!-- no translation found for spoken_description_emoji (6934027701390427635) -->
+    <skip />
     <string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Hľadať"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Bodka"</string>
diff --git a/java/res/values-sl/strings.xml b/java/res/values-sl/strings.xml
index 3dabb7e..16a3531 100644
--- a/java/res/values-sl/strings.xml
+++ b/java/res/values-sl/strings.xml
@@ -93,7 +93,7 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Tabulator"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Presledek"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Glasovni vnos"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Smeško"</string>
+    <string name="spoken_description_emoji" msgid="6934027701390427635">"Znaki »emoji«"</string>
     <string name="spoken_description_return" msgid="8178083177238315647">"Vračalka"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Iskanje"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Pika"</string>
diff --git a/java/res/values-sr/strings.xml b/java/res/values-sr/strings.xml
index af8760f..e1ead18 100644
--- a/java/res/values-sr/strings.xml
+++ b/java/res/values-sr/strings.xml
@@ -93,7 +93,8 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Tab"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Размак"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Гласовни унос"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Смајли"</string>
+    <!-- no translation found for spoken_description_emoji (6934027701390427635) -->
+    <skip />
     <string name="spoken_description_return" msgid="8178083177238315647">"Return"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Претражи"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Тачка"</string>
diff --git a/java/res/values-sv/strings.xml b/java/res/values-sv/strings.xml
index cda89b8..225e795 100644
--- a/java/res/values-sv/strings.xml
+++ b/java/res/values-sv/strings.xml
@@ -93,7 +93,7 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Tabb"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Blanksteg"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Röstinmatning"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Uttryckssymbol"</string>
+    <string name="spoken_description_emoji" msgid="6934027701390427635">"Emoji"</string>
     <string name="spoken_description_return" msgid="8178083177238315647">"Retur"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Sök"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Punkt"</string>
diff --git a/java/res/values-sw/strings.xml b/java/res/values-sw/strings.xml
index 587e95a..f931e13 100644
--- a/java/res/values-sw/strings.xml
+++ b/java/res/values-sw/strings.xml
@@ -93,7 +93,7 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Kichupo"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Nafasi"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Kuweka data kwa kutamka"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Uso wenye tabasamu"</string>
+    <string name="spoken_description_emoji" msgid="6934027701390427635">"Emoji"</string>
     <string name="spoken_description_return" msgid="8178083177238315647">"Rudi"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Tafuta"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Nukta"</string>
diff --git a/java/res/values-th/strings.xml b/java/res/values-th/strings.xml
index fe86ff3..fe118bd 100644
--- a/java/res/values-th/strings.xml
+++ b/java/res/values-th/strings.xml
@@ -93,7 +93,8 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"แท็บ"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Space"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"การป้อนข้อมูลด้วยเสียง"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"หน้ายิ้ม"</string>
+    <!-- no translation found for spoken_description_emoji (6934027701390427635) -->
+    <skip />
     <string name="spoken_description_return" msgid="8178083177238315647">"Return"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"ค้นหา"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"เครื่องหมายจุด"</string>
diff --git a/java/res/values-tl/strings.xml b/java/res/values-tl/strings.xml
index c0503a3..02bc8ba 100644
--- a/java/res/values-tl/strings.xml
+++ b/java/res/values-tl/strings.xml
@@ -93,7 +93,8 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Tab"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Puwang"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Input ng boses"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Smiley na mukha"</string>
+    <!-- no translation found for spoken_description_emoji (6934027701390427635) -->
+    <skip />
     <string name="spoken_description_return" msgid="8178083177238315647">"Bumalik"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Paghahanap"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Tuldok"</string>
diff --git a/java/res/values-tr/strings.xml b/java/res/values-tr/strings.xml
index b168e8b..ca6d664 100644
--- a/java/res/values-tr/strings.xml
+++ b/java/res/values-tr/strings.xml
@@ -93,7 +93,8 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Sekme"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Boşluk"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Ses girişi"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Gülen yüz"</string>
+    <!-- no translation found for spoken_description_emoji (6934027701390427635) -->
+    <skip />
     <string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Ara"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Nokta"</string>
diff --git a/java/res/values-uk/strings.xml b/java/res/values-uk/strings.xml
index ee9208c..d4c9ff3 100644
--- a/java/res/values-uk/strings.xml
+++ b/java/res/values-uk/strings.xml
@@ -93,7 +93,7 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Вкладка"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Пробіл"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Голосовий ввід"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Смайлик"</string>
+    <string name="spoken_description_emoji" msgid="6934027701390427635">"Смайли Emoji"</string>
     <string name="spoken_description_return" msgid="8178083177238315647">"Клавіша Return"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Пошук"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Крапка"</string>
diff --git a/java/res/values-vi/strings.xml b/java/res/values-vi/strings.xml
index 112bc47..9524442 100644
--- a/java/res/values-vi/strings.xml
+++ b/java/res/values-vi/strings.xml
@@ -93,7 +93,8 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Tab"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Dấu cách"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Nhập dữ liệu bằng giọng nói"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Mặt cười"</string>
+    <!-- no translation found for spoken_description_emoji (6934027701390427635) -->
+    <skip />
     <string name="spoken_description_return" msgid="8178083177238315647">"Quay lại"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Tìm kiếm"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Dấu chấm"</string>
diff --git a/java/res/values-zh-rCN/strings.xml b/java/res/values-zh-rCN/strings.xml
index c363624..603e84b 100644
--- a/java/res/values-zh-rCN/strings.xml
+++ b/java/res/values-zh-rCN/strings.xml
@@ -93,7 +93,8 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Tab"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"空格"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"语音输入"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"笑脸"</string>
+    <!-- no translation found for spoken_description_emoji (6934027701390427635) -->
+    <skip />
     <string name="spoken_description_return" msgid="8178083177238315647">"返回"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"搜索"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"点"</string>
diff --git a/java/res/values-zh-rHK/strings.xml b/java/res/values-zh-rHK/strings.xml
index 806f73f..0b64703 100644
--- a/java/res/values-zh-rHK/strings.xml
+++ b/java/res/values-zh-rHK/strings.xml
@@ -93,7 +93,7 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Tab 鍵"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"空白鍵"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"語音輸入"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"笑臉"</string>
+    <string name="spoken_description_emoji" msgid="6934027701390427635">"表情圖案"</string>
     <string name="spoken_description_return" msgid="8178083177238315647">"Return 鍵"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"搜尋"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"點"</string>
diff --git a/java/res/values-zh-rTW/strings.xml b/java/res/values-zh-rTW/strings.xml
index 1fdf7e8..2309c5c 100644
--- a/java/res/values-zh-rTW/strings.xml
+++ b/java/res/values-zh-rTW/strings.xml
@@ -93,7 +93,8 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Tab 鍵"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"空白鍵"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"語音輸入"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"笑臉"</string>
+    <!-- no translation found for spoken_description_emoji (6934027701390427635) -->
+    <skip />
     <string name="spoken_description_return" msgid="8178083177238315647">"返回"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"搜尋"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"點"</string>
diff --git a/java/res/values-zu/strings.xml b/java/res/values-zu/strings.xml
index f4c0d92..4917e6c 100644
--- a/java/res/values-zu/strings.xml
+++ b/java/res/values-zu/strings.xml
@@ -93,7 +93,7 @@
     <string name="spoken_description_tab" msgid="2667716002663482248">"Tab"</string>
     <string name="spoken_description_space" msgid="2582521050049860859">"Isikhala"</string>
     <string name="spoken_description_mic" msgid="615536748882611950">"Okungenayo kwezwi"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Ubuso-obumomothekayo"</string>
+    <string name="spoken_description_emoji" msgid="6934027701390427635">"I-Emoji"</string>
     <string name="spoken_description_return" msgid="8178083177238315647">"Buyisela"</string>
     <string name="spoken_description_search" msgid="1247236163755920808">"Sesha"</string>
     <string name="spoken_description_dot" msgid="40711082435231673">"Icashazi"</string>
diff --git a/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java b/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
index 7fff46a..7e21667 100644
--- a/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
+++ b/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
@@ -632,7 +632,9 @@
     }
 
     public void setHardwareAcceleratedDrawingEnabled(final boolean enabled) {
-        // TODO:
+        if (!enabled) return;
+        // TODO: Should use LAYER_TYPE_SOFTWARE when hardware acceleration is off?
+        setLayerType(LAYER_TYPE_HARDWARE, null);
     }
 
     private static void setupAlphabetKey(final TextView alphabetKey, final String label,
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index 2dfde94..37a4bf8 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -205,7 +205,7 @@
         final boolean subtypeChanged = (oldKeyboard == null)
                 || !keyboard.mId.mLocale.equals(oldKeyboard.mId.mLocale);
         final boolean needsToDisplayLanguage = mSubtypeSwitcher.needsToDisplayLanguage(
-                keyboard.mId.mLocale);
+                keyboard.mId.mSubtype);
         keyboardView.startDisplayLanguageOnSpacebar(subtypeChanged, needsToDisplayLanguage,
                 RichInputMethodManager.getInstance().hasMultipleEnabledIMEsOrSubtypes(true));
     }
diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index 3590c48..8246c92 100644
--- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -930,11 +930,6 @@
             return middleText;
         }
 
-        final String shortText = SubtypeLocaleUtils.getShortDisplayName(subtype);
-        if (fitsTextIntoWidth(width, shortText, paint)) {
-            return shortText;
-        }
-
         return "";
     }
 
diff --git a/java/src/com/android/inputmethod/keyboard/internal/NeedsToDisplayLanguage.java b/java/src/com/android/inputmethod/keyboard/internal/NeedsToDisplayLanguage.java
new file mode 100644
index 0000000..e548de5
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/NeedsToDisplayLanguage.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.internal;
+
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
+
+/**
+ * This class determines that the language name on the spacebar should be displayed or not.
+ */
+public final class NeedsToDisplayLanguage {
+    private int mEnabledSubtypeCount;
+    private boolean mIsSystemLanguageSameAsInputLanguage;
+
+    public boolean needsToDisplayLanguage(final InputMethodSubtype subtype) {
+        if (SubtypeLocaleUtils.isNoLanguage(subtype)) {
+            return true;
+        }
+        return mEnabledSubtypeCount >= 2 || !mIsSystemLanguageSameAsInputLanguage;
+    }
+
+    public void updateEnabledSubtypeCount(final int count) {
+        mEnabledSubtypeCount = count;
+    }
+
+    public void updateIsSystemLanguageSameAsInputLanguage(final boolean isSame) {
+        mIsSystemLanguageSameAsInputLanguage = isSame;
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index 0aa34e8..60ac1ba 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -28,9 +28,10 @@
 import com.android.inputmethod.latin.makedict.FormatSpec.DictionaryOptions;
 import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
 import com.android.inputmethod.latin.makedict.WordProperty;
-import com.android.inputmethod.latin.personalization.PersonalizationHelper;
 import com.android.inputmethod.latin.settings.NativeSuggestOptions;
+import com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
 import com.android.inputmethod.latin.utils.CollectionUtils;
+import com.android.inputmethod.latin.utils.FileUtils;
 import com.android.inputmethod.latin.utils.JniUtils;
 import com.android.inputmethod.latin.utils.LanguageModelParam;
 import com.android.inputmethod.latin.utils.StringUtils;
@@ -81,6 +82,8 @@
     public static final int FORMAT_WORD_PROPERTY_LEVEL_INDEX = 2;
     public static final int FORMAT_WORD_PROPERTY_COUNT_INDEX = 3;
 
+    public static final String DICT_FILE_NAME_SUFFIX_FOR_MIGRATION = ".migrate";
+
     private long mNativeDict;
     private final Locale mLocale;
     private final long mDictSize;
@@ -244,7 +247,7 @@
         // TODO: toLowerCase in the native code
         final int[] prevWordCodePointArray = (null == prevWord)
                 ? null : StringUtils.toCodePointArray(prevWord);
-        final int composerSize = composer.size();
+        final int composerSize = composer.sizeWithoutTrailingSingleQuotes();
 
         final boolean isGesture = composer.isBatchMode();
         if (composerSize <= 1 || !isGesture) {
@@ -458,6 +461,24 @@
         return needsToRunGCNative(mNativeDict, mindsBlockByGC);
     }
 
+    public boolean migrateTo(final int newFormatVersion) {
+        if (!isValidDictionary()) {
+            return false;
+        }
+        final String tmpDictFilePath = mDictFilePath + DICT_FILE_NAME_SUFFIX_FOR_MIGRATION;
+        // TODO: Implement migrateNative(tmpDictFilePath, newFormatVersion).
+        close();
+        final File dictFile = new File(mDictFilePath);
+        final File tmpDictFile = new File(tmpDictFilePath);
+        FileUtils.deleteRecursively(dictFile);
+        if (!BinaryDictionaryUtils.renameDict(tmpDictFile, dictFile)) {
+            return false;
+        }
+        loadDictionary(dictFile.getAbsolutePath(), 0 /* startOffset */,
+                dictFile.length(), mIsUpdatable);
+        return true;
+    }
+
     @UsedForTesting
     public int calculateProbability(final int unigramProbability, final int bigramProbability) {
         if (!isValidDictionary()) return NOT_A_PROBABILITY;
diff --git a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
index c2941e4..4e17f83 100644
--- a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
@@ -262,11 +262,6 @@
     }
 
     @Override
-    protected boolean needsToReloadAfterCreation() {
-        return true;
-    }
-
-    @Override
     protected boolean haveContentsChanged() {
         final long startTime = SystemClock.uptimeMillis();
         final int contactCount = getContactCount();
diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
index 7847738..92b5354 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
@@ -137,6 +137,11 @@
         return formatVersion == FormatSpec.VERSION4;
     }
 
+    private boolean needsToMigrateDictionary(final int formatVersion) {
+        // TODO: Check version.
+        return false;
+    }
+
     public boolean isValidDictionaryLocked() {
         return mBinaryDictionary.isValidDictionary();
     }
@@ -269,6 +274,9 @@
     }
 
     private void runGCAfterAllPrioritizedTasksIfRequiredLocked(final boolean mindsBlockByGC) {
+        if (mBinaryDictionary == null) {
+            return;
+        }
         // needsToRunGC() have to be called with lock.
         if (mBinaryDictionary.needsToRunGC(mindsBlockByGC)) {
             if (setProcessingLargeTaskIfNot()) {
@@ -357,6 +365,9 @@
             public void run() {
                 final boolean locked = setProcessingLargeTaskIfNot();
                 try {
+                    if (mBinaryDictionary == null) {
+                        return;
+                    }
                     mBinaryDictionary.addMultipleDictionaryEntries(
                             languageModelParams.toArray(
                                     new LanguageModelParam[languageModelParams.size()]));
@@ -471,15 +482,13 @@
         if (oldBinaryDictionary != null) {
             oldBinaryDictionary.close();
         }
+        if (mBinaryDictionary.isValidDictionary()
+                && needsToMigrateDictionary(mBinaryDictionary.getFormatVersion())) {
+            mBinaryDictionary.migrateTo(DICTIONARY_FORMAT_VERSION);
+        }
     }
 
     /**
-     * Abstract method for checking if it is required to reload the dictionary before writing
-     * a binary dictionary.
-     */
-    abstract protected boolean needsToReloadAfterCreation();
-
-    /**
      * Create a new binary dictionary and load initial contents.
      */
     private void createNewDictionaryLocked() {
@@ -496,6 +505,9 @@
     }
 
     private void flushDictionaryLocked() {
+        if (mBinaryDictionary == null) {
+            return;
+        }
         if (mBinaryDictionary.needsToRunGC(false /* mindsBlockByGC */)) {
             mBinaryDictionary.flushWithGC();
         } else {
diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
index 935dd96..d430122 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -34,6 +34,7 @@
 import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils;
 import com.android.inputmethod.keyboard.KeyboardSwitcher;
+import com.android.inputmethod.keyboard.internal.NeedsToDisplayLanguage;
 import com.android.inputmethod.latin.utils.LocaleUtils;
 import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
 
@@ -89,23 +90,6 @@
                     false /* isAuxiliary */, false /* overridesImplicitlyEnabledSubtype */,
                     SUBTYPE_ID_OF_DUMMY_EMOJI_SUBTYPE);
 
-    static final class NeedsToDisplayLanguage {
-        private int mEnabledSubtypeCount;
-        private boolean mIsSystemLanguageSameAsInputLanguage;
-
-        public boolean getValue() {
-            return mEnabledSubtypeCount >= 2 || !mIsSystemLanguageSameAsInputLanguage;
-        }
-
-        public void updateEnabledSubtypeCount(final int count) {
-            mEnabledSubtypeCount = count;
-        }
-
-        public void updateIsSystemLanguageSameAsInputLanguage(final boolean isSame) {
-            mIsSystemLanguageSameAsInputLanguage = isSame;
-        }
-    }
-
     public static SubtypeSwitcher getInstance() {
         return sInstance;
     }
@@ -265,14 +249,8 @@
     // Subtype Switching functions //
     //////////////////////////////////
 
-    public boolean needsToDisplayLanguage(final Locale keyboardLocale) {
-        if (keyboardLocale.toString().equals(SubtypeLocaleUtils.NO_LANGUAGE)) {
-            return true;
-        }
-        if (!keyboardLocale.equals(getCurrentSubtypeLocale())) {
-            return false;
-        }
-        return mNeedsToDisplayLanguage.getValue();
+    public boolean needsToDisplayLanguage(final InputMethodSubtype subtype) {
+        return mNeedsToDisplayLanguage.needsToDisplayLanguage(subtype);
     }
 
     public boolean isSystemLocaleSameAsLocaleOfAllEnabledSubtypesOfEnabledImes() {
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 6985d9a..db0a8a8 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -101,19 +101,6 @@
                 : typedWord;
         LatinImeLogger.onAddSuggestedWord(typedWord, Dictionary.TYPE_USER_TYPED);
 
-        final WordComposer wordComposerForLookup;
-        if (trailingSingleQuotesCount > 0) {
-            wordComposerForLookup = new WordComposer(wordComposer);
-            for (int i = trailingSingleQuotesCount - 1; i >= 0; --i) {
-                // TODO: do not create a fake event for this. Ideally the word composer should know
-                // how to give out the word without trailing quotes and we can remove this entirely
-                wordComposerForLookup.deleteLast(Event.createSoftwareKeypressEvent(
-                        Event.NOT_A_CODE_POINT, Constants.CODE_DELETE,
-                        Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE));
-            }
-        } else {
-            wordComposerForLookup = wordComposer;
-        }
         final ArrayList<SuggestedWordInfo> rawSuggestions;
         if (ProductionFlag.INCLUDE_RAW_SUGGESTIONS) {
             rawSuggestions = CollectionUtils.newArrayList();
@@ -121,7 +108,7 @@
             rawSuggestions = null;
         }
         final SuggestionResults suggestionResults = mDictionaryFacilitator.getSuggestionResults(
-                wordComposerForLookup, prevWordForBigram, proximityInfo, blockOffensiveWords,
+                wordComposer, prevWordForBigram, proximityInfo, blockOffensiveWords,
                 additionalFeaturesOptions, SESSION_TYPING, rawSuggestions);
 
         final boolean isFirstCharCapitalized = wordComposer.isFirstCharCapitalized();
diff --git a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
index 8078ab5..8838e27 100644
--- a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
@@ -269,9 +269,4 @@
     protected boolean haveContentsChanged() {
         return true;
     }
-
-    @Override
-    protected boolean needsToReloadAfterCreation() {
-        return true;
-    }
 }
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index a955f37..8a321e2 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -104,25 +104,6 @@
         refreshSize();
     }
 
-    public WordComposer(final WordComposer source) {
-        mCombinerChain = source.mCombinerChain;
-        mPrimaryKeyCodes = Arrays.copyOf(source.mPrimaryKeyCodes, source.mPrimaryKeyCodes.length);
-        mEvents = new ArrayList<Event>(source.mEvents);
-        mTypedWord = new StringBuilder(source.mTypedWord);
-        mInputPointers.copy(source.mInputPointers);
-        mCapsCount = source.mCapsCount;
-        mDigitsCount = source.mDigitsCount;
-        mIsFirstCharCapitalized = source.mIsFirstCharCapitalized;
-        mCapitalizedMode = source.mCapitalizedMode;
-        mTrailingSingleQuotesCount = source.mTrailingSingleQuotesCount;
-        mIsResumed = source.mIsResumed;
-        mIsBatchMode = source.mIsBatchMode;
-        mCursorPositionWithinWord = source.mCursorPositionWithinWord;
-        mRejectedBatchModeSuggestion = source.mRejectedBatchModeSuggestion;
-        mPreviousWordForSuggestion = source.mPreviousWordForSuggestion;
-        refreshSize();
-    }
-
     /**
      * Clear out the keys registered so far.
      */
@@ -151,10 +132,22 @@
      * Number of keystrokes in the composing word.
      * @return the number of keystrokes
      */
-    public final int size() {
+    // This may be made public if need be, but right now it's not used anywhere
+    /* package for tests */ int size() {
         return mCodePointSize;
     }
 
+    public boolean isSingleLetter() {
+        return size() == 1;
+    }
+
+    // When the composition contains trailing quotes, we don't pass them to the suggestion engine.
+    // This is because "'tgis'" should be corrected to "'this'", but we can't afford to consider
+    // single quotes as separators because of their very common use as apostrophes.
+    public int sizeWithoutTrailingSingleQuotes() {
+        return size() - mTrailingSingleQuotesCount;
+    }
+
     public final boolean isComposingWord() {
         return size() > 0;
     }
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index 4c7dc64..ec6bd28 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -130,7 +130,11 @@
         // so we try using some heuristics to find out about these and fix them.
         mConnection.tryFixLyingCursorPosition();
         cancelDoubleSpacePeriodCountdown();
-        mInputLogicHandler = new InputLogicHandler(mLatinIME, this);
+        if (InputLogicHandler.NULL_HANDLER == mInputLogicHandler) {
+            mInputLogicHandler = new InputLogicHandler(mLatinIME, this);
+        } else {
+            mInputLogicHandler.reset();
+        }
     }
 
     /**
@@ -141,8 +145,7 @@
             mConnection.finishComposingText();
         }
         resetComposingState(true /* alsoResetLastComposedWord */);
-        mInputLogicHandler.destroy();
-        mInputLogicHandler = InputLogicHandler.NULL_HANDLER;
+        mInputLogicHandler.reset();
     }
 
     /**
@@ -513,14 +516,12 @@
                             mWordComposer);
                 }
             }
-            final int wordComposerSize = mWordComposer.size();
-            // Since isComposingWord() is true, the size is at least 1.
             if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
                 // If we are in the middle of a recorrection, we need to commit the recorrection
                 // first so that we can insert the batch input at the current cursor position.
                 resetEntireInputState(mConnection.getExpectedSelectionStart(),
                         mConnection.getExpectedSelectionEnd(), true /* clearSuggestionStrip */);
-            } else if (wordComposerSize <= 1) {
+            } else if (mWordComposer.isSingleLetter()) {
                 // We auto-correct the previous (typed, not gestured) string iff it's one character
                 // long. The reason for this is, even in the middle of gesture typing, you'll still
                 // tap one-letter words and you want them auto-corrected (typically, "i" in English
@@ -738,7 +739,7 @@
         if (isComposingWord) {
             mWordComposer.add(inputTransaction.mEvent);
             // If it's the first letter, make note of auto-caps state
-            if (mWordComposer.size() == 1) {
+            if (mWordComposer.isSingleLetter()) {
                 // We pass 1 to getPreviousWordForSuggestion because we were not composing a word
                 // yet, so the word we want is the 1st word before the cursor.
                 mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime(
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java
index db96de3..42f0d7c 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java
@@ -43,7 +43,7 @@
     // is initialized, though probably only the monkey can actually do this.
     public static final InputLogicHandler NULL_HANDLER = new InputLogicHandler() {
         @Override
-        public void destroy() {}
+        public void reset() {}
         @Override
         public boolean handleMessage(final Message msg) { return true; }
         @Override
@@ -75,8 +75,8 @@
         mInputLogic = inputLogic;
     }
 
-    public void destroy() {
-        mNonUIThreadHandler.getLooper().quit();
+    public void reset() {
+        mNonUIThreadHandler.removeCallbacksAndMessages(null);
     }
 
     /**
diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
index 07217e4..f255034 100644
--- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
+++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
@@ -18,9 +18,7 @@
 
 import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.latin.Constants;
-import com.android.inputmethod.latin.makedict.DictDecoder.DictionaryBufferFactory;
 
-import java.io.File;
 import java.util.Date;
 import java.util.HashMap;
 
@@ -363,42 +361,6 @@
         }
     }
 
-    /**
-     * Returns new dictionary decoder.
-     *
-     * @param dictFile the dictionary file.
-     * @param offset the offset in the file.
-     * @param length the length of the file, in bytes.
-     * @param bufferType The type of buffer, as one of USE_* in DictDecoder.
-     * @return new dictionary decoder if the dictionary file exists, otherwise null.
-     */
-    public static DictDecoder getDictDecoder(final File dictFile, final long offset,
-            final long length, final int bufferType) {
-        if (dictFile.isDirectory()) {
-            return new Ver4DictDecoder(dictFile, bufferType);
-        } else if (dictFile.isFile()) {
-            return new Ver2DictDecoder(dictFile, offset, length, bufferType);
-        }
-        return null;
-    }
-
-    @UsedForTesting
-    public static DictDecoder getDictDecoder(final File dictFile, final long offset,
-            final long length, final DictionaryBufferFactory factory) {
-        if (dictFile.isDirectory()) {
-            return new Ver4DictDecoder(dictFile, factory);
-        } else if (dictFile.isFile()) {
-            return new Ver2DictDecoder(dictFile, offset, length, factory);
-        }
-        return null;
-    }
-
-    @UsedForTesting
-    public static DictDecoder getDictDecoder(final File dictFile, final long offset,
-            final long length) {
-        return getDictDecoder(dictFile, offset, length, DictDecoder.USE_READONLY_BYTEBUFFER);
-    }
-
     private FormatSpec() {
         // This utility class is not publicly instantiable.
     }
diff --git a/java/src/com/android/inputmethod/latin/makedict/MakedictLog.java b/java/src/com/android/inputmethod/latin/makedict/MakedictLog.java
deleted file mode 100644
index cf07209..0000000
--- a/java/src/com/android/inputmethod/latin/makedict/MakedictLog.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2011 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 android.util.Log;
-
-/**
- * Wrapper to redirect log events to the right output medium.
- */
-public final class MakedictLog {
-    public static final boolean DBG = false;
-    private static final String TAG = MakedictLog.class.getSimpleName();
-
-    public static void d(String message) {
-        if (DBG) {
-            Log.d(TAG, message);
-        }
-    }
-
-    public static void i(String message) {
-        if (DBG) {
-            Log.i(TAG, message);
-        }
-    }
-
-    public static void w(String message) {
-        Log.w(TAG, message);
-    }
-
-    public static void e(String message) {
-        Log.e(TAG, message);
-    }
-}
diff --git a/java/src/com/android/inputmethod/latin/makedict/ProbabilityInfo.java b/java/src/com/android/inputmethod/latin/makedict/ProbabilityInfo.java
index 9dcd63f..5fcbb63 100644
--- a/java/src/com/android/inputmethod/latin/makedict/ProbabilityInfo.java
+++ b/java/src/com/android/inputmethod/latin/makedict/ProbabilityInfo.java
@@ -16,6 +16,7 @@
 
 package com.android.inputmethod.latin.makedict;
 
+import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.latin.BinaryDictionary;
 import com.android.inputmethod.latin.utils.CombinedFormatUtils;
 
@@ -30,6 +31,7 @@
     public final int mLevel;
     public final int mCount;
 
+    @UsedForTesting
     public static ProbabilityInfo max(final ProbabilityInfo probabilityInfo1,
             final ProbabilityInfo probabilityInfo2) {
         if (probabilityInfo1 == null) {
diff --git a/java/src/com/android/inputmethod/latin/makedict/WordProperty.java b/java/src/com/android/inputmethod/latin/makedict/WordProperty.java
index d94cec4..8533922 100644
--- a/java/src/com/android/inputmethod/latin/makedict/WordProperty.java
+++ b/java/src/com/android/inputmethod/latin/makedict/WordProperty.java
@@ -42,6 +42,7 @@
 
     private int mHashCode = 0;
 
+    @UsedForTesting
     public WordProperty(final String word, final ProbabilityInfo probabilityInfo,
             final ArrayList<WeightedString> shortcutTargets,
             final ArrayList<WeightedString> bigrams,
diff --git a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
index 074ec40..6f84e1f 100644
--- a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
+++ b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
@@ -91,11 +91,6 @@
         return false;
     }
 
-    @Override
-    protected boolean needsToReloadAfterCreation() {
-        return false;
-    }
-
     public void addMultipleDictionaryEntriesToDictionary(
             final ArrayList<LanguageModelParam> languageModelParams,
             final ExpandableBinaryDictionary.AddMultipleDictionaryEntriesCallback callback) {
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
index a07e8eb..6a52481 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
@@ -268,6 +268,7 @@
             // if it doesn't. See documentation for binarySearch.
             final int insertIndex = positionIndex >= 0 ? positionIndex : -positionIndex - 1;
 
+            // Weak <- insertIndex == 0, ..., insertIndex == mLength -> Strong
             if (insertIndex == 0 && mLength >= mMaxLength) {
                 // In the future, we may want to keep track of the best suggestion score even if
                 // we are asked for 0 suggestions. In this case, we can use the following
@@ -285,11 +286,6 @@
                 // }
                 return true;
             }
-            if (insertIndex >= mMaxLength) {
-                // We found a suggestion, but its score is too weak to be kept considering
-                // the suggestion limit.
-                return true;
-            }
 
             final String wordString = new String(word, wordOffset, wordLength);
             if (mLength < mMaxLength) {
@@ -297,12 +293,13 @@
                 ++mLength;
                 System.arraycopy(mScores, insertIndex, mScores, insertIndex + 1, copyLen);
                 mSuggestions.add(insertIndex, wordString);
+                mScores[insertIndex] = score;
             } else {
-                System.arraycopy(mScores, 1, mScores, 0, insertIndex);
+                System.arraycopy(mScores, 1, mScores, 0, insertIndex - 1);
                 mSuggestions.add(insertIndex, wordString);
                 mSuggestions.remove(0);
+                mScores[insertIndex - 1] = score;
             }
-            mScores[insertIndex] = score;
 
             return true;
         }
diff --git a/java/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtils.java b/java/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtils.java
index 6388300..b4658b5 100644
--- a/java/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtils.java
@@ -26,6 +26,8 @@
 import java.io.IOException;
 import java.util.Locale;
 import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 public final class BinaryDictionaryUtils {
     private static final String TAG = BinaryDictionaryUtils.class.getSimpleName();
@@ -64,6 +66,31 @@
         return header;
     }
 
+    public static boolean renameDict(final File dictFile, final File newDictFile) {
+        if (dictFile.isFile()) {
+            return dictFile.renameTo(newDictFile);
+        } else if (dictFile.isDirectory()) {
+            final String dictName = dictFile.getName();
+            final String newDictName = newDictFile.getName();
+            if (newDictFile.exists()) {
+                return false;
+            }
+            for (final File file : dictFile.listFiles()) {
+                if (!file.isFile()) {
+                    continue;
+                }
+                final String fileName = file.getName();
+                final String newFileName = fileName.replaceFirst(
+                        Pattern.quote(dictName), Matcher.quoteReplacement(newDictName));
+                if (!file.renameTo(new File(dictFile, newFileName))) {
+                    return false;
+                }
+            }
+            return dictFile.renameTo(newDictFile);
+        }
+        return false;
+    }
+
     public static boolean createEmptyDictFile(final String filePath, final long dictVersion,
             final Locale locale, final Map<String, String> attributeMap) {
         final String[] keyArray = new String[attributeMap.size()];
diff --git a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java
index e531d4b..315913e 100644
--- a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java
@@ -23,12 +23,10 @@
 import android.text.TextUtils;
 import android.util.Log;
 
-import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.latin.AssetFileAddress;
 import com.android.inputmethod.latin.BinaryDictionaryGetter;
 import com.android.inputmethod.latin.Constants;
 import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.makedict.BinaryDictIOUtils;
 import com.android.inputmethod.latin.makedict.DictionaryHeader;
 import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
 import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
diff --git a/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java b/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java
index acd16a9..5ce977d 100644
--- a/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java
+++ b/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java
@@ -110,6 +110,9 @@
             final LanguageModelParam languageModelParam =
                     detectWhetherVaildWordOrNotAndGetLanguageModelParam(
                             prevWord, tempWord, timestamp, dictionaryFacilitator);
+            if (languageModelParam == null) {
+                continue;
+            }
             languageModelParams.add(languageModelParam);
             prevWord = languageModelParam.mTargetWord;
         }
@@ -120,6 +123,9 @@
             final String prevWord, final String targetWord, final int timestamp,
             final DictionaryFacilitatorForSuggest dictionaryFacilitator) {
         final Locale locale = dictionaryFacilitator.getLocale();
+        if (locale == null) {
+            return null;
+        }
         if (!dictionaryFacilitator.isValidWord(targetWord, true /* ignoreCase */)) {
             // OOV word.
             return createAndGetLanguageModelParamOfWord(prevWord, targetWord, timestamp,
diff --git a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java
index b3871bf..4f556f9 100644
--- a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java
@@ -201,11 +201,11 @@
     //  fr_CH swiss   F  Français (Suisse)
     //  de    qwertz  F  Deutsch
     //  de_CH swiss   T  Deutsch (Schweiz)
-    //  zz    qwerty  F  No language (QWERTY)    in system locale
+    //  zz    qwerty  F  Alphabet (QWERTY)       in system locale
     //  fr    qwertz  T  Français (QWERTZ)
     //  de    qwerty  T  Deutsch (QWERTY)
     //  en_US azerty  T  English (US) (AZERTY)   exception
-    //  zz    azerty  T  No language (AZERTY)    in system locale
+    //  zz    azerty  T  Alphabet (AZERTY)       in system locale
 
     private static String getReplacementString(final InputMethodSubtype subtype,
             final Locale displayLocale) {
@@ -294,21 +294,21 @@
 
     // InputMethodSubtype's display name for spacebar text in its locale.
     //        isAdditionalSubtype (T=true, F=false)
-    // locale layout  | Short  Middle      Full
-    // ------ ------- - ---- --------- ----------------------
-    //  en_US qwerty  F  En  English   English (US)           exception
-    //  en_GB qwerty  F  En  English   English (UK)           exception
-    //  es_US spanish F  Es  Español   Español (EE.UU.)       exception
-    //  fr    azerty  F  Fr  Français  Français
-    //  fr_CA qwerty  F  Fr  Français  Français (Canada)
-    //  fr_CH swiss   F  Fr  Français  Français (Suisse)
-    //  de    qwertz  F  De  Deutsch   Deutsch
-    //  de_CH swiss   T  De  Deutsch   Deutsch (Schweiz)
-    //  zz    qwerty  F      QWERTY    QWERTY
-    //  fr    qwertz  T  Fr  Français  Français
-    //  de    qwerty  T  De  Deutsch   Deutsch
-    //  en_US azerty  T  En  English   English (US)
-    //  zz    azerty  T      AZERTY    AZERTY
+    // locale layout  |  Middle      Full
+    // ------ ------- - --------- ----------------------
+    //  en_US qwerty  F  English   English (US)           exception
+    //  en_GB qwerty  F  English   English (UK)           exception
+    //  es_US spanish F  Español   Español (EE.UU.)       exception
+    //  fr    azerty  F  Français  Français
+    //  fr_CA qwerty  F  Français  Français (Canada)
+    //  fr_CH swiss   F  Français  Français (Suisse)
+    //  de    qwertz  F  Deutsch   Deutsch
+    //  de_CH swiss   T  Deutsch   Deutsch (Schweiz)
+    //  zz    qwerty  F  QWERTY    QWERTY
+    //  fr    qwertz  T  Français  Français
+    //  de    qwerty  T  Deutsch   Deutsch
+    //  en_US azerty  T  English   English (US)
+    //  zz    azerty  T  AZERTY    AZERTY
 
     // Get InputMethodSubtype's full display name in its locale.
     public static String getFullDisplayName(final InputMethodSubtype subtype) {
@@ -327,15 +327,6 @@
         return getSubtypeLocaleDisplayName(locale.getLanguage());
     }
 
-    // Get InputMethodSubtype's short display name in its locale.
-    public static String getShortDisplayName(final InputMethodSubtype subtype) {
-        if (isNoLanguage(subtype)) {
-            return "";
-        }
-        final Locale locale = getSubtypeLocale(subtype);
-        return StringUtils.capitalizeFirstCodePoint(locale.getLanguage(), locale);
-    }
-
     // TODO: Get this information from the framework instead of maintaining here by ourselves.
     // Sorted list of known Right-To-Left language codes.
     private static final String[] SORTED_RTL_LANGUAGES = {
diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
index ac0b4ab..154ea98 100644
--- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
+++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
@@ -199,47 +199,30 @@
         ASSERT(false);
         return;
     }
-    int outputCodePoints[outputCodePointsLength];
-    int scores[scoresLength];
-    const jsize spaceIndicesLength = env->GetArrayLength(outSpaceIndicesArray);
-    int spaceIndices[spaceIndicesLength];
-    const jsize outputTypesLength = env->GetArrayLength(outTypesArray);
-    int outputTypes[outputTypesLength];
     const jsize outputAutoCommitFirstWordConfidenceLength =
             env->GetArrayLength(outAutoCommitFirstWordConfidenceArray);
-    // We only use the first result, as obviously we will only ever autocommit the first one
     ASSERT(outputAutoCommitFirstWordConfidenceLength == 1);
-    int outputAutoCommitFirstWordConfidence[outputAutoCommitFirstWordConfidenceLength];
-    memset(outputCodePoints, 0, sizeof(outputCodePoints));
-    memset(scores, 0, sizeof(scores));
-    memset(spaceIndices, 0, sizeof(spaceIndices));
-    memset(outputTypes, 0, sizeof(outputTypes));
-    memset(outputAutoCommitFirstWordConfidence, 0, sizeof(outputAutoCommitFirstWordConfidence));
-
-    if (givenSuggestOptions.isGesture() || inputSize > 0) {
-        // TODO: Use SuggestionResults to return suggestions.
-        count = dictionary->getSuggestions(pInfo, traverseSession, xCoordinates, yCoordinates,
-                times, pointerIds, inputCodePoints, inputSize, prevWordCodePoints,
-                prevWordCodePointsLength, &givenSuggestOptions, outputCodePoints,
-                scores, spaceIndices, outputTypes, outputAutoCommitFirstWordConfidence);
-    } else {
-        SuggestionResults suggestionResults(MAX_RESULTS);
-        dictionary->getPredictions(prevWordCodePoints, prevWordCodePointsLength,
-                &suggestionResults);
-        suggestionResults.outputSuggestions(env, outSuggestionCount, outCodePointsArray,
-                outScoresArray, outSpaceIndicesArray, outTypesArray,
-                outAutoCommitFirstWordConfidenceArray);
+    if (outputAutoCommitFirstWordConfidenceLength != 1) {
+        // We only use the first result, as obviously we will only ever autocommit the first one
+        AKLOGE("Invalid outputAutoCommitFirstWordConfidenceLength: %d",
+                outputAutoCommitFirstWordConfidenceLength);
+        ASSERT(false);
         return;
     }
 
-    // Copy back the output values
-    env->SetIntArrayRegion(outSuggestionCount, 0, 1 /* len */, &count);
-    env->SetIntArrayRegion(outCodePointsArray, 0, outputCodePointsLength, outputCodePoints);
-    env->SetIntArrayRegion(outScoresArray, 0, scoresLength, scores);
-    env->SetIntArrayRegion(outSpaceIndicesArray, 0, spaceIndicesLength, spaceIndices);
-    env->SetIntArrayRegion(outTypesArray, 0, outputTypesLength, outputTypes);
-    env->SetIntArrayRegion(outAutoCommitFirstWordConfidenceArray, 0,
-            outputAutoCommitFirstWordConfidenceLength, outputAutoCommitFirstWordConfidence);
+    SuggestionResults suggestionResults(MAX_RESULTS);
+    if (givenSuggestOptions.isGesture() || inputSize > 0) {
+        // TODO: Use SuggestionResults to return suggestions.
+        dictionary->getSuggestions(pInfo, traverseSession, xCoordinates, yCoordinates,
+                times, pointerIds, inputCodePoints, inputSize, prevWordCodePoints,
+                prevWordCodePointsLength, &givenSuggestOptions, &suggestionResults);
+    } else {
+        dictionary->getPredictions(prevWordCodePoints, prevWordCodePointsLength,
+                &suggestionResults);
+    }
+    suggestionResults.outputSuggestions(env, outSuggestionCount, outCodePointsArray,
+            outScoresArray, outSpaceIndicesArray, outTypesArray,
+            outAutoCommitFirstWordConfidenceArray);
 }
 
 static jint latinime_BinaryDictionary_getProbability(JNIEnv *env, jclass clazz, jlong dict,
diff --git a/native/jni/src/defines.h b/native/jni/src/defines.h
index 4e6ff95..3651cd5 100644
--- a/native/jni/src/defines.h
+++ b/native/jni/src/defines.h
@@ -103,7 +103,8 @@
 #define AKLOGI(fmt, ...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, fmt, ##__VA_ARGS__)
 #endif // defined(HOST_TOOL)
 
-#define DUMP_RESULT(words, frequencies) do { dumpResult(words, frequencies); } while (0)
+#define DUMP_SUGGESTION(words, frequencies, index, score) \
+        do { dumpWordInfo(words, frequencies, index, score); } while (0)
 #define DUMP_WORD(word, length) do { dumpWord(word, length); } while (0)
 #define INTS_TO_CHARS(input, length, output, outlength) do { \
         intArrayToCharArray(input, length, output, outlength); } while (0)
@@ -165,7 +166,7 @@
 #else // defined(FLAG_DO_PROFILE) || defined(FLAG_DBG)
 #define AKLOGE(fmt, ...)
 #define AKLOGI(fmt, ...)
-#define DUMP_RESULT(words, frequencies)
+#define DUMP_SUGGESTION(words, frequencies, index, score)
 #define DUMP_WORD(word, length)
 #undef DO_ASSERT_TEST
 #define ASSERT(success)
diff --git a/native/jni/src/suggest/core/dicnode/dic_node.h b/native/jni/src/suggest/core/dicnode/dic_node.h
index 865aab6..258aa9c 100644
--- a/native/jni/src/suggest/core/dicnode/dic_node.h
+++ b/native/jni/src/suggest/core/dicnode/dic_node.h
@@ -83,14 +83,6 @@
 #if DEBUG_DICT
     DicNodeProfiler mProfiler;
 #endif
-    //////////////////
-    // Memory utils //
-    //////////////////
-    AK_FORCE_INLINE static void managedDelete(DicNode *node) {
-        node->remove();
-    }
-    // end
-    /////////////////
 
     AK_FORCE_INLINE DicNode()
             :
@@ -158,7 +150,7 @@
         PROF_NODE_COPY(&dicNode->mProfiler, mProfiler);
     }
 
-    AK_FORCE_INLINE void remove() {
+    AK_FORCE_INLINE void finalize() {
         mIsUsed = false;
         if (mReleaseListener) {
             mReleaseListener->onReleased(this);
@@ -218,10 +210,6 @@
         return CharUtils::isAsciiUpper(c);
     }
 
-    bool isFirstWord() const {
-        return mDicNodeProperties.getPrevWordTerminalPtNodePos() == NOT_A_DICT_POS;
-    }
-
     bool isCompletion(const int inputSize) const {
         return mDicNodeState.mDicNodeStateInput.getInputIndex(0) >= inputSize;
     }
@@ -292,7 +280,9 @@
     // the one that corresponds to the last word of the suggestion, and all the previous words
     // are concatenated together in mDicNodeStateOutput.
     int getTotalNodeSpaceCount() const {
-        if (isFirstWord()) return 0;
+        if (!hasMultipleWords()) {
+            return 0;
+        }
         return CharUtils::getSpaceCount(mDicNodeState.mDicNodeStateOutput.getCodePointBuf(),
                 mDicNodeState.mDicNodeStateOutput.getPrevWordsLength());
     }
@@ -480,17 +470,7 @@
         mReleaseListener = releaseListener;
     }
 
-    AK_FORCE_INLINE bool compare(const DicNode *right) {
-        if (!isUsed() && !right->isUsed()) {
-            // Compare pointer values here for stable comparison
-            return this > right;
-        }
-        if (!isUsed()) {
-            return true;
-        }
-        if (!right->isUsed()) {
-            return false;
-        }
+    AK_FORCE_INLINE bool compare(const DicNode *right) const {
         // Promote exact matches to prevent them from being pruned.
         const bool leftExactMatch = ErrorTypeUtils::isExactMatch(getContainedErrorTypes());
         const bool rightExactMatch = ErrorTypeUtils::isExactMatch(right->getContainedErrorTypes());
diff --git a/native/jni/src/suggest/core/dicnode/dic_node_priority_queue.h b/native/jni/src/suggest/core/dicnode/dic_node_priority_queue.h
index 1f02731..213b1b9 100644
--- a/native/jni/src/suggest/core/dicnode/dic_node_priority_queue.h
+++ b/native/jni/src/suggest/core/dicnode/dic_node_priority_queue.h
@@ -68,15 +68,15 @@
         }
         setMaxSize(maxSize);
         for (int i = 0; i < mCapacity + 1; ++i) {
-            mDicNodesBuf[i].remove();
+            mDicNodesBuf[i].finalize();
             mDicNodesBuf[i].setReleaseListener(this);
-            mUnusedNodeIndices[i] = i == mCapacity ? NOT_A_NODE_ID : static_cast<int>(i) + 1;
+            mUnusedNodeIndices[i] = (i == mCapacity) ? NOT_A_NODE_ID : (i + 1);
         }
         mNextUnusedNodeId = 0;
     }
 
     // Copy
-    AK_FORCE_INLINE DicNode *copyPush(DicNode *dicNode) {
+    AK_FORCE_INLINE DicNode *copyPush(const DicNode *const dicNode) {
         return copyPush(dicNode, mMaxSize);
     }
 
@@ -89,11 +89,11 @@
         if (dest) {
             DicNodeUtils::initByCopy(node, dest);
         }
-        node->remove();
+        node->finalize();
         mDicNodesQueue.pop();
     }
 
-    void onReleased(DicNode *dicNode) {
+    void onReleased(const DicNode *dicNode) {
         const int index = static_cast<int>(dicNode - &mDicNodesBuf[0]);
         if (mUnusedNodeIndices[index] != NOT_A_NODE_ID) {
             // it's already released
@@ -118,7 +118,8 @@
     DISALLOW_IMPLICIT_CONSTRUCTORS(DicNodePriorityQueue);
     static const int NOT_A_NODE_ID = -1;
 
-    AK_FORCE_INLINE static bool compareDicNode(DicNode *left, DicNode *right) {
+    AK_FORCE_INLINE static bool compareDicNode(const DicNode *const left,
+            const DicNode *const right) {
         return left->compare(right);
     }
 
@@ -141,10 +142,10 @@
     }
 
     AK_FORCE_INLINE void pop() {
-        copyPop(0);
+        copyPop(nullptr);
     }
 
-    AK_FORCE_INLINE bool betterThanWorstDicNode(DicNode *dicNode) const {
+    AK_FORCE_INLINE bool betterThanWorstDicNode(const DicNode *const dicNode) const {
         DicNode *worstNode = mDicNodesQueue.top();
         if (!worstNode) {
             return true;
@@ -154,7 +155,7 @@
 
     AK_FORCE_INLINE DicNode *searchEmptyDicNode() {
         if (mCapacity == 0) {
-            return 0;
+            return nullptr;
         }
         if (mNextUnusedNodeId == NOT_A_NODE_ID) {
             AKLOGI("No unused node found.");
@@ -163,7 +164,7 @@
                         i, mDicNodesBuf[i].isUsed(), mUnusedNodeIndices[i]);
             }
             ASSERT(false);
-            return 0;
+            return nullptr;
         }
         DicNode *dicNode = &mDicNodesBuf[mNextUnusedNodeId];
         markNodeAsUsed(dicNode);
@@ -179,7 +180,7 @@
 
     AK_FORCE_INLINE DicNode *pushPoolNodeWithMaxSize(DicNode *dicNode, const int maxSize) {
         if (!dicNode) {
-            return 0;
+            return nullptr;
         }
         if (!isFull(maxSize)) {
             mDicNodesQueue.push(dicNode);
@@ -190,16 +191,16 @@
             mDicNodesQueue.push(dicNode);
             return dicNode;
         }
-        dicNode->remove();
-        return 0;
+        dicNode->finalize();
+        return nullptr;
     }
 
     // Copy
-    AK_FORCE_INLINE DicNode *copyPush(DicNode *dicNode, const int maxSize) {
+    AK_FORCE_INLINE DicNode *copyPush(const DicNode *const dicNode, const int maxSize) {
         return pushPoolNodeWithMaxSize(newDicNode(dicNode), maxSize);
     }
 
-    AK_FORCE_INLINE DicNode *newDicNode(DicNode *dicNode) {
+    AK_FORCE_INLINE DicNode *newDicNode(const DicNode *const dicNode) {
         DicNode *newNode = searchEmptyDicNode();
         if (newNode) {
             DicNodeUtils::initByCopy(dicNode, newNode);
diff --git a/native/jni/src/suggest/core/dicnode/dic_node_release_listener.h b/native/jni/src/suggest/core/dicnode/dic_node_release_listener.h
index 2ca4f21..c3f4329 100644
--- a/native/jni/src/suggest/core/dicnode/dic_node_release_listener.h
+++ b/native/jni/src/suggest/core/dicnode/dic_node_release_listener.h
@@ -27,7 +27,7 @@
  public:
     DicNodeReleaseListener() {}
     virtual ~DicNodeReleaseListener() {}
-    virtual void onReleased(DicNode *dicNode) = 0;
+    virtual void onReleased(const DicNode *dicNode) = 0;
  private:
     DISALLOW_COPY_AND_ASSIGN(DicNodeReleaseListener);
 };
diff --git a/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp b/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp
index 48752f2..2d02a7d 100644
--- a/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp
+++ b/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp
@@ -16,8 +16,6 @@
 
 #include "suggest/core/dicnode/dic_node_utils.h"
 
-#include <cstring>
-
 #include "suggest/core/dicnode/dic_node.h"
 #include "suggest/core/dicnode/dic_node_vector.h"
 #include "suggest/core/dictionary/multi_bigram_map.h"
@@ -103,34 +101,4 @@
             NOT_A_PROBABILITY);
 }
 
-////////////////
-// Char utils //
-////////////////
-
-// TODO: Move to char_utils?
-/* static */ int DicNodeUtils::appendTwoWords(const int *const src0, const int16_t length0,
-        const int *const src1, const int16_t length1, int *const dest) {
-    int actualLength0 = 0;
-    for (int i = 0; i < length0; ++i) {
-        if (src0[i] == 0) {
-            break;
-        }
-        actualLength0 = i + 1;
-    }
-    actualLength0 = std::min(actualLength0, MAX_WORD_LENGTH);
-    memmove(dest, src0, actualLength0 * sizeof(dest[0]));
-    if (!src1 || length1 == 0) {
-        return actualLength0;
-    }
-    int actualLength1 = 0;
-    for (int i = 0; i < length1; ++i) {
-        if (src1[i] == 0) {
-            break;
-        }
-        actualLength1 = i + 1;
-    }
-    actualLength1 = std::min(actualLength1, MAX_WORD_LENGTH - actualLength0);
-    memmove(&dest[actualLength0], src1, actualLength1 * sizeof(dest[0]));
-    return actualLength0 + actualLength1;
-}
 } // namespace latinime
diff --git a/native/jni/src/suggest/core/dicnode/dic_node_utils.h b/native/jni/src/suggest/core/dicnode/dic_node_utils.h
index 3f1514a..4c0f1f1 100644
--- a/native/jni/src/suggest/core/dicnode/dic_node_utils.h
+++ b/native/jni/src/suggest/core/dicnode/dic_node_utils.h
@@ -17,8 +17,6 @@
 #ifndef LATINIME_DIC_NODE_UTILS_H
 #define LATINIME_DIC_NODE_UTILS_H
 
-#include <stdint.h>
-
 #include "defines.h"
 
 namespace latinime {
@@ -30,8 +28,6 @@
 
 class DicNodeUtils {
  public:
-    static int appendTwoWords(const int *src0, const int16_t length0, const int *src1,
-            const int16_t length1, int *const dest);
     static void initAsRoot(
             const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy,
             const int prevWordPtNodePos, DicNode *const newRootDicNode);
diff --git a/native/jni/src/suggest/core/dicnode/dic_nodes_cache.h b/native/jni/src/suggest/core/dicnode/dic_nodes_cache.h
index d4769e7..6b8dc8c 100644
--- a/native/jni/src/suggest/core/dicnode/dic_nodes_cache.h
+++ b/native/jni/src/suggest/core/dicnode/dic_nodes_cache.h
@@ -100,14 +100,7 @@
     }
 
     AK_FORCE_INLINE void copyPushNextActive(DicNode *dicNode) {
-        DicNode *pushedDicNode = mNextActiveDicNodes->copyPush(dicNode);
-        if (!pushedDicNode) {
-            if (dicNode->isCached()) {
-                dicNode->remove();
-            }
-            // We simply drop any dic node that was not cached, ignoring the slim chance
-            // that one of its children represents what the user really wanted.
-        }
+        mNextActiveDicNodes->copyPush(dicNode);
     }
 
     void popTerminal(DicNode *dest) {
diff --git a/native/jni/src/suggest/core/dictionary/dictionary.cpp b/native/jni/src/suggest/core/dictionary/dictionary.cpp
index 07b07f7..ae4646d 100644
--- a/native/jni/src/suggest/core/dictionary/dictionary.cpp
+++ b/native/jni/src/suggest/core/dictionary/dictionary.cpp
@@ -22,6 +22,7 @@
 
 #include "defines.h"
 #include "suggest/core/policy/dictionary_header_structure_policy.h"
+#include "suggest/core/result/suggestion_results.h"
 #include "suggest/core/session/dic_traverse_session.h"
 #include "suggest/core/suggest.h"
 #include "suggest/core/suggest_options.h"
@@ -43,34 +44,25 @@
     logDictionaryInfo(env);
 }
 
-int Dictionary::getSuggestions(ProximityInfo *proximityInfo, DicTraverseSession *traverseSession,
+void Dictionary::getSuggestions(ProximityInfo *proximityInfo, DicTraverseSession *traverseSession,
         int *xcoordinates, int *ycoordinates, int *times, int *pointerIds, int *inputCodePoints,
         int inputSize, int *prevWordCodePoints, int prevWordLength,
-        const SuggestOptions *const suggestOptions, int *outWords, int *outputScores,
-        int *spaceIndices, int *outputTypes, int *outputAutoCommitFirstWordConfidence) const {
+        const SuggestOptions *const suggestOptions,
+        SuggestionResults *const outSuggestionResults) const {
     TimeKeeper::setCurrentTime();
-    int result = 0;
+    DicTraverseSession::initSessionInstance(
+            traverseSession, this, prevWordCodePoints, prevWordLength, suggestOptions);
     if (suggestOptions->isGesture()) {
-        DicTraverseSession::initSessionInstance(
-                traverseSession, this, prevWordCodePoints, prevWordLength, suggestOptions);
-        result = mGestureSuggest->getSuggestions(proximityInfo, traverseSession, xcoordinates,
-                ycoordinates, times, pointerIds, inputCodePoints, inputSize, outWords,
-                outputScores, spaceIndices, outputTypes, outputAutoCommitFirstWordConfidence);
-        if (DEBUG_DICT) {
-            DUMP_RESULT(outWords, outputScores);
-        }
-        return result;
-    } else {
-        DicTraverseSession::initSessionInstance(
-                traverseSession, this, prevWordCodePoints, prevWordLength, suggestOptions);
-        result = mTypingSuggest->getSuggestions(proximityInfo, traverseSession, xcoordinates,
+        mGestureSuggest->getSuggestions(proximityInfo, traverseSession, xcoordinates,
                 ycoordinates, times, pointerIds, inputCodePoints, inputSize,
-                outWords, outputScores, spaceIndices, outputTypes,
-                outputAutoCommitFirstWordConfidence);
-        if (DEBUG_DICT) {
-            DUMP_RESULT(outWords, outputScores);
-        }
-        return result;
+                outSuggestionResults);
+    } else {
+        mTypingSuggest->getSuggestions(proximityInfo, traverseSession, xcoordinates,
+                ycoordinates, times, pointerIds, inputCodePoints, inputSize,
+                outSuggestionResults);
+    }
+    if (DEBUG_DICT) {
+        outSuggestionResults->dumpSuggestions();
     }
 }
 
diff --git a/native/jni/src/suggest/core/dictionary/dictionary.h b/native/jni/src/suggest/core/dictionary/dictionary.h
index 4d482e7..df5fc9b 100644
--- a/native/jni/src/suggest/core/dictionary/dictionary.h
+++ b/native/jni/src/suggest/core/dictionary/dictionary.h
@@ -62,11 +62,11 @@
     Dictionary(JNIEnv *env, DictionaryStructureWithBufferPolicy::StructurePolicyPtr
             dictionaryStructureWithBufferPolicy);
 
-    int getSuggestions(ProximityInfo *proximityInfo, DicTraverseSession *traverseSession,
+    void getSuggestions(ProximityInfo *proximityInfo, DicTraverseSession *traverseSession,
             int *xcoordinates, int *ycoordinates, int *times, int *pointerIds, int *inputCodePoints,
             int inputSize, int *prevWordCodePoints, int prevWordLength,
-            const SuggestOptions *const suggestOptions, int *outWords, int *outputScores,
-            int *spaceIndices, int *outputTypes, int *outputAutoCommitFirstWordConfidence) const;
+            const SuggestOptions *const suggestOptions,
+            SuggestionResults *const outSuggestionResults) const;
 
     void getPredictions(const int *word, int length,
             SuggestionResults *const outSuggestionResults) const;
diff --git a/native/jni/src/suggest/core/policy/scoring.h b/native/jni/src/suggest/core/policy/scoring.h
index 0251475..292194bf 100644
--- a/native/jni/src/suggest/core/policy/scoring.h
+++ b/native/jni/src/suggest/core/policy/scoring.h
@@ -23,6 +23,7 @@
 
 class DicNode;
 class DicTraverseSession;
+class SuggestionResults;
 
 // This class basically tweaks suggestions and distances apart from CompoundDistance
 class Scoring {
@@ -30,11 +31,8 @@
     virtual int calculateFinalScore(const float compoundDistance, const int inputSize,
             const ErrorTypeUtils::ErrorType containedErrorTypes, const bool forceCommit,
             const bool boostExactMatches) const = 0;
-    virtual bool getMostProbableString(const DicTraverseSession *const traverseSession,
-            const int terminalSize, const float languageWeight, int *const outputCodePoints,
-            int *const type, int *const freq) const = 0;
-    virtual void safetyNetForMostProbableString(const int scoreCount,
-            const int maxScore, int *const outputCodePoints, int *const scores) const = 0;
+    virtual void getMostProbableString(const DicTraverseSession *const traverseSession,
+            const float languageWeight, SuggestionResults *const outSuggestionResults) const = 0;
     virtual float getAdjustedLanguageWeight(DicTraverseSession *const traverseSession,
             DicNode *const terminals, const int size) const = 0;
     virtual float getDoubleLetterDemotionDistanceCost(
diff --git a/native/jni/src/suggest/core/result/suggestion_results.cpp b/native/jni/src/suggest/core/result/suggestion_results.cpp
index 2be757d..da1c6bc 100644
--- a/native/jni/src/suggest/core/result/suggestion_results.cpp
+++ b/native/jni/src/suggest/core/result/suggestion_results.cpp
@@ -54,13 +54,23 @@
 
 void SuggestionResults::addPrediction(const int *const codePoints, const int codePointCount,
         const int probability) {
-    if (codePointCount <= 0 || codePointCount > MAX_WORD_LENGTH
-            || probability == NOT_A_PROBABILITY) {
+    if (probability == NOT_A_PROBABILITY) {
         // Invalid word.
         return;
     }
-    // Use probability as a score of the word.
-    const int score = probability;
+    addSuggestion(codePoints, codePointCount, probability, Dictionary::KIND_PREDICTION,
+            NOT_AN_INDEX, NOT_A_FIRST_WORD_CONFIDENCE);
+}
+
+void SuggestionResults::addSuggestion(const int *const codePoints, const int codePointCount,
+        const int score, const int type, const int indexToPartialCommit,
+        const int autocimmitFirstWordConfindence) {
+    if (codePointCount <= 0 || codePointCount > MAX_WORD_LENGTH) {
+        // Invalid word.
+        AKLOGE("Invalid word is added to the suggestion results. codePointCount: %d",
+                codePointCount);
+        return;
+    }
     if (getSuggestionCount() >= mMaxSuggestionCount) {
         const SuggestedWord &mWorstSuggestion = mSuggestedWords.top();
         if (score > mWorstSuggestion.getScore() || (score == mWorstSuggestion.getScore()
@@ -70,8 +80,31 @@
             return;
         }
     }
-    mSuggestedWords.push(SuggestedWord(codePoints, codePointCount, score,
-            Dictionary::KIND_PREDICTION, NOT_AN_INDEX, NOT_A_FIRST_WORD_CONFIDENCE));
+    mSuggestedWords.push(SuggestedWord(codePoints, codePointCount, score, type,
+            indexToPartialCommit, autocimmitFirstWordConfindence));
+}
+
+void SuggestionResults::getSortedScores(int *const outScores) const {
+    auto copyOfSuggestedWords = mSuggestedWords;
+    while (!copyOfSuggestedWords.empty()) {
+        const SuggestedWord &suggestedWord = copyOfSuggestedWords.top();
+        outScores[copyOfSuggestedWords.size() - 1] = suggestedWord.getScore();
+        copyOfSuggestedWords.pop();
+    }
+}
+
+void SuggestionResults::dumpSuggestions() const {
+    std::vector<SuggestedWord> suggestedWords;
+    auto copyOfSuggestedWords = mSuggestedWords;
+    while (!copyOfSuggestedWords.empty()) {
+        suggestedWords.push_back(copyOfSuggestedWords.top());
+        copyOfSuggestedWords.pop();
+    }
+    int index = 0;
+    for (auto it = suggestedWords.rbegin(); it != suggestedWords.rend(); ++it) {
+        DUMP_SUGGESTION(it->getCodePoint(), it->getCodePointCount(), index, it->getScore());
+        index++;
+    }
 }
 
 } // namespace latinime
diff --git a/native/jni/src/suggest/core/result/suggestion_results.h b/native/jni/src/suggest/core/result/suggestion_results.h
index 0b841ca..020bab4 100644
--- a/native/jni/src/suggest/core/result/suggestion_results.h
+++ b/native/jni/src/suggest/core/result/suggestion_results.h
@@ -35,8 +35,12 @@
     void outputSuggestions(JNIEnv *env, jintArray outSuggestionCount, jintArray outCodePointsArray,
             jintArray outScoresArray, jintArray outSpaceIndicesArray, jintArray outTypesArray,
             jintArray outAutoCommitFirstWordConfidenceArray);
-
     void addPrediction(const int *const codePoints, const int codePointCount, const int score);
+    void addSuggestion(const int *const codePoints, const int codePointCount,
+            const int score, const int type, const int indexToPartialCommit,
+            const int autocimmitFirstWordConfindence);
+    void getSortedScores(int *const outScores) const;
+    void dumpSuggestions() const;
 
     int getSuggestionCount() const {
         return mSuggestedWords.size();
diff --git a/native/jni/src/suggest/core/result/suggestions_output_utils.cpp b/native/jni/src/suggest/core/result/suggestions_output_utils.cpp
index 19912f2..83140f1 100644
--- a/native/jni/src/suggest/core/result/suggestions_output_utils.cpp
+++ b/native/jni/src/suggest/core/result/suggestions_output_utils.cpp
@@ -17,163 +17,125 @@
 #include "suggest/core/result/suggestions_output_utils.h"
 
 #include <algorithm>
+#include <vector>
 
 #include "suggest/core/dicnode/dic_node.h"
 #include "suggest/core/dicnode/dic_node_utils.h"
 #include "suggest/core/dictionary/binary_dictionary_shortcut_iterator.h"
-#include "suggest/core/dictionary/dictionary.h"
 #include "suggest/core/dictionary/error_type_utils.h"
 #include "suggest/core/policy/scoring.h"
+#include "suggest/core/result/suggestion_results.h"
 #include "suggest/core/session/dic_traverse_session.h"
 
 namespace latinime {
 
 const int SuggestionsOutputUtils::MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT = 16;
 
-// TODO: Split this method.
-/* static */ int SuggestionsOutputUtils::outputSuggestions(
+/* static */ void SuggestionsOutputUtils::outputSuggestions(
         const Scoring *const scoringPolicy, DicTraverseSession *traverseSession,
-        int *outputScores, int *outputCodePoints, int *outputIndicesToPartialCommit,
-        int *outputTypes, int *outputAutoCommitFirstWordConfidence) {
+        SuggestionResults *const outSuggestionResults) {
 #if DEBUG_EVALUATE_MOST_PROBABLE_STRING
     const int terminalSize = 0;
 #else
-    const int terminalSize = std::min(MAX_RESULTS,
-            static_cast<int>(traverseSession->getDicTraverseCache()->terminalSize()));
+    const int terminalSize = traverseSession->getDicTraverseCache()->terminalSize();
 #endif
-    DicNode terminals[MAX_RESULTS]; // Avoiding non-POD variable length array
-
+    std::vector<DicNode> terminals(terminalSize);
     for (int index = terminalSize - 1; index >= 0; --index) {
         traverseSession->getDicTraverseCache()->popTerminal(&terminals[index]);
     }
 
     const float languageWeight = scoringPolicy->getAdjustedLanguageWeight(
-            traverseSession, terminals, terminalSize);
-
-    int outputWordIndex = 0;
-    // Insert most probable word at index == 0 as long as there is one terminal at least
-    const bool hasMostProbableString =
-            scoringPolicy->getMostProbableString(traverseSession, terminalSize, languageWeight,
-                    &outputCodePoints[0], &outputTypes[0], &outputScores[0]);
-    if (hasMostProbableString) {
-        outputIndicesToPartialCommit[outputWordIndex] = NOT_AN_INDEX;
-        ++outputWordIndex;
-    }
-
-    int maxScore = S_INT_MIN;
+            traverseSession, terminals.data(), terminalSize);
     // Force autocorrection for obvious long multi-word suggestions when the top suggestion is
     // a long multiple words suggestion.
     // TODO: Implement a smarter auto-commit method for handling multi-word suggestions.
-    // traverseSession->isPartiallyCommited() always returns false because we never auto partial
-    // commit for now.
-    const bool forceCommitMultiWords = (terminalSize > 0) ?
-            scoringPolicy->autoCorrectsToMultiWordSuggestionIfTop()
-                    && (traverseSession->isPartiallyCommited()
-                            || (traverseSession->getInputSize()
-                                    >= MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT
-                                            && terminals[0].hasMultipleWords())) : false;
+    const bool forceCommitMultiWords = scoringPolicy->autoCorrectsToMultiWordSuggestionIfTop()
+            && (traverseSession->getInputSize() >= MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT
+                    && !terminals.empty() && terminals.front().hasMultipleWords());
     // TODO: have partial commit work even with multiple pointers.
     const bool outputSecondWordFirstLetterInputIndex =
             traverseSession->isOnlyOnePointerUsed(0 /* pointerId */);
-    if (terminalSize > 0) {
-        // If we have no suggestions, don't write this
-        outputAutoCommitFirstWordConfidence[0] =
-                computeFirstWordConfidence(&terminals[0]);
-    }
     const bool boostExactMatches = traverseSession->getDictionaryStructurePolicy()->
             getHeaderStructurePolicy()->shouldBoostExactMatches();
+
     // Output suggestion results here
-    for (int terminalIndex = 0; terminalIndex < terminalSize && outputWordIndex < MAX_RESULTS;
-            ++terminalIndex) {
-        DicNode *terminalDicNode = &terminals[terminalIndex];
-        if (DEBUG_GEO_FULL) {
-            terminalDicNode->dump("OUT:");
-        }
-        const float doubleLetterCost =
-                scoringPolicy->getDoubleLetterDemotionDistanceCost(terminalDicNode);
-        const float compoundDistance = terminalDicNode->getCompoundDistance(languageWeight)
-                + doubleLetterCost;
-        const bool isPossiblyOffensiveWord =
-                traverseSession->getDictionaryStructurePolicy()->getProbability(
-                        terminalDicNode->getProbability(), NOT_A_PROBABILITY) <= 0;
-        const bool isExactMatch =
-                ErrorTypeUtils::isExactMatch(terminalDicNode->getContainedErrorTypes());
-        const bool isFirstCharUppercase = terminalDicNode->isFirstCharUppercase();
-        // Heuristic: We exclude probability=0 first-char-uppercase words from exact match.
-        // (e.g. "AMD" and "and")
-        const bool isSafeExactMatch = isExactMatch
-                && !(isPossiblyOffensiveWord && isFirstCharUppercase);
-        const int outputTypeFlags =
-                (isPossiblyOffensiveWord ? Dictionary::KIND_FLAG_POSSIBLY_OFFENSIVE : 0)
-                | ((isSafeExactMatch && boostExactMatches) ? Dictionary::KIND_FLAG_EXACT_MATCH : 0);
+    for (auto &terminalDicNode : terminals) {
+        outputSuggestionsOfDicNode(scoringPolicy, traverseSession, &terminalDicNode,
+                languageWeight, boostExactMatches, forceCommitMultiWords,
+                outputSecondWordFirstLetterInputIndex, outSuggestionResults);
+    }
+    scoringPolicy->getMostProbableString(traverseSession, languageWeight, outSuggestionResults);
+}
 
-        // Entries that are blacklisted or do not represent a word should not be output.
-        const bool isValidWord = !terminalDicNode->isBlacklistedOrNotAWord();
+/* static */ void SuggestionsOutputUtils::outputSuggestionsOfDicNode(
+        const Scoring *const scoringPolicy, DicTraverseSession *traverseSession,
+        const DicNode *const terminalDicNode, const float languageWeight,
+        const bool boostExactMatches, const bool forceCommitMultiWords,
+        const bool outputSecondWordFirstLetterInputIndex,
+        SuggestionResults *const outSuggestionResults) {
+    if (DEBUG_GEO_FULL) {
+        terminalDicNode->dump("OUT:");
+    }
+    const float doubleLetterCost =
+            scoringPolicy->getDoubleLetterDemotionDistanceCost(terminalDicNode);
+    const float compoundDistance = terminalDicNode->getCompoundDistance(languageWeight)
+            + doubleLetterCost;
+    const bool isPossiblyOffensiveWord =
+            traverseSession->getDictionaryStructurePolicy()->getProbability(
+                    terminalDicNode->getProbability(), NOT_A_PROBABILITY) <= 0;
+    const bool isExactMatch =
+            ErrorTypeUtils::isExactMatch(terminalDicNode->getContainedErrorTypes());
+    const bool isFirstCharUppercase = terminalDicNode->isFirstCharUppercase();
+    // Heuristic: We exclude probability=0 first-char-uppercase words from exact match.
+    // (e.g. "AMD" and "and")
+    const bool isSafeExactMatch = isExactMatch
+            && !(isPossiblyOffensiveWord && isFirstCharUppercase);
+    const int outputTypeFlags =
+            (isPossiblyOffensiveWord ? Dictionary::KIND_FLAG_POSSIBLY_OFFENSIVE : 0)
+            | ((isSafeExactMatch && boostExactMatches) ? Dictionary::KIND_FLAG_EXACT_MATCH : 0);
 
-        // Increase output score of top typing suggestion to ensure autocorrection.
-        // TODO: Better integration with java side autocorrection logic.
-        const int finalScore = scoringPolicy->calculateFinalScore(
-                compoundDistance, traverseSession->getInputSize(),
-                terminalDicNode->getContainedErrorTypes(),
-                (forceCommitMultiWords && terminalDicNode->hasMultipleWords())
-                         || (isValidWord && scoringPolicy->doesAutoCorrectValidWord()),
-                boostExactMatches);
-        if (maxScore < finalScore && isValidWord) {
-            maxScore = finalScore;
-        }
+    // Entries that are blacklisted or do not represent a word should not be output.
+    const bool isValidWord = !terminalDicNode->isBlacklistedOrNotAWord();
 
-        // Don't output invalid words. However, we still need to submit their shortcuts if any.
-        if (isValidWord) {
-            outputTypes[outputWordIndex] = Dictionary::KIND_CORRECTION | outputTypeFlags;
-            outputScores[outputWordIndex] = finalScore;
-            if (outputSecondWordFirstLetterInputIndex) {
-                outputIndicesToPartialCommit[outputWordIndex] =
-                        terminalDicNode->getSecondWordFirstInputIndex(
-                                traverseSession->getProximityInfoState(0));
-            } else {
-                outputIndicesToPartialCommit[outputWordIndex] = NOT_AN_INDEX;
-            }
-            // Populate the outputChars array with the suggested word.
-            const int startIndex = outputWordIndex * MAX_WORD_LENGTH;
-            terminalDicNode->outputResult(&outputCodePoints[startIndex]);
-            ++outputWordIndex;
-        }
+    // Increase output score of top typing suggestion to ensure autocorrection.
+    // TODO: Better integration with java side autocorrection logic.
+    const int finalScore = scoringPolicy->calculateFinalScore(
+            compoundDistance, traverseSession->getInputSize(),
+            terminalDicNode->getContainedErrorTypes(),
+            (forceCommitMultiWords && terminalDicNode->hasMultipleWords())
+                     || (isValidWord && scoringPolicy->doesAutoCorrectValidWord()),
+            boostExactMatches);
 
-        if (!terminalDicNode->hasMultipleWords()) {
-            BinaryDictionaryShortcutIterator shortcutIt(
-                    traverseSession->getDictionaryStructurePolicy()->getShortcutsStructurePolicy(),
-                    traverseSession->getDictionaryStructurePolicy()
-                            ->getShortcutPositionOfPtNode(terminalDicNode->getPtNodePos()));
-            // Shortcut is not supported for multiple words suggestions.
-            // TODO: Check shortcuts during traversal for multiple words suggestions.
-            const bool sameAsTyped = scoringPolicy->sameAsTyped(traverseSession, terminalDicNode);
-            const int shortcutBaseScore = scoringPolicy->doesAutoCorrectValidWord() ?
-                     scoringPolicy->calculateFinalScore(compoundDistance,
-                             traverseSession->getInputSize(),
-                             terminalDicNode->getContainedErrorTypes(),
-                             true /* forceCommit */, boostExactMatches) : finalScore;
-            const int updatedOutputWordIndex = outputShortcuts(&shortcutIt,
-                    outputWordIndex, shortcutBaseScore, outputCodePoints, outputScores, outputTypes,
-                    sameAsTyped);
-            const int secondWordFirstInputIndex = terminalDicNode->getSecondWordFirstInputIndex(
-                    traverseSession->getProximityInfoState(0));
-            for (int i = outputWordIndex; i < updatedOutputWordIndex; ++i) {
-                if (outputSecondWordFirstLetterInputIndex) {
-                    outputIndicesToPartialCommit[i] = secondWordFirstInputIndex;
-                } else {
-                    outputIndicesToPartialCommit[i] = NOT_AN_INDEX;
-                }
-            }
-            outputWordIndex = updatedOutputWordIndex;
-        }
-        DicNode::managedDelete(terminalDicNode);
+    // Don't output invalid words. However, we still need to submit their shortcuts if any.
+    if (isValidWord) {
+        int codePoints[MAX_WORD_LENGTH];
+        terminalDicNode->outputResult(codePoints);
+        const int indexToPartialCommit = outputSecondWordFirstLetterInputIndex ?
+                terminalDicNode->getSecondWordFirstInputIndex(
+                        traverseSession->getProximityInfoState(0)) :
+                NOT_AN_INDEX;
+        outSuggestionResults->addSuggestion(codePoints,
+                terminalDicNode->getTotalNodeCodePointCount(),
+                finalScore, Dictionary::KIND_CORRECTION | outputTypeFlags,
+                indexToPartialCommit, computeFirstWordConfidence(terminalDicNode));
     }
 
-    if (hasMostProbableString) {
-        scoringPolicy->safetyNetForMostProbableString(outputWordIndex, maxScore,
-                &outputCodePoints[0], outputScores);
+    // Output shortcuts.
+    // Shortcut is not supported for multiple words suggestions.
+    // TODO: Check shortcuts during traversal for multiple words suggestions.
+    if (!terminalDicNode->hasMultipleWords()) {
+        BinaryDictionaryShortcutIterator shortcutIt(
+                traverseSession->getDictionaryStructurePolicy()->getShortcutsStructurePolicy(),
+                traverseSession->getDictionaryStructurePolicy()
+                        ->getShortcutPositionOfPtNode(terminalDicNode->getPtNodePos()));
+        const bool sameAsTyped = scoringPolicy->sameAsTyped(traverseSession, terminalDicNode);
+        const int shortcutBaseScore = scoringPolicy->doesAutoCorrectValidWord() ?
+                 scoringPolicy->calculateFinalScore(compoundDistance,
+                         traverseSession->getInputSize(),
+                         terminalDicNode->getContainedErrorTypes(),
+                         true /* forceCommit */, boostExactMatches) : finalScore;
+        outputShortcuts(&shortcutIt, shortcutBaseScore, sameAsTyped, outSuggestionResults);
     }
-    return outputWordIndex;
 }
 
 /* static */ int SuggestionsOutputUtils::computeFirstWordConfidence(
@@ -228,12 +190,11 @@
     return distanceContribution + lengthContribution + spaceContribution;
 }
 
-/* static */ int SuggestionsOutputUtils::outputShortcuts(
-        BinaryDictionaryShortcutIterator *const shortcutIt,
-        int outputWordIndex, const int finalScore, int *const outputCodePoints,
-        int *const outputScores, int *const outputTypes, const bool sameAsTyped) {
+/* static */ void SuggestionsOutputUtils::outputShortcuts(
+        BinaryDictionaryShortcutIterator *const shortcutIt, const int finalScore,
+        const bool sameAsTyped, SuggestionResults *const outSuggestionResults) {
     int shortcutTarget[MAX_WORD_LENGTH];
-    while (shortcutIt->hasNextShortcutTarget() && outputWordIndex < MAX_RESULTS) {
+    while (shortcutIt->hasNextShortcutTarget()) {
         bool isWhilelist;
         int shortcutTargetStringLength;
         shortcutIt->nextShortcutTarget(MAX_WORD_LENGTH, shortcutTarget,
@@ -250,14 +211,9 @@
             shortcutScore = std::max(S_INT_MIN + 1, shortcutScore) - 1;
             kind = Dictionary::KIND_SHORTCUT;
         }
-        outputTypes[outputWordIndex] = kind;
-        outputScores[outputWordIndex] = shortcutScore;
-        outputScores[outputWordIndex] = std::max(S_INT_MIN + 1, shortcutScore) - 1;
-        const int startIndex2 = outputWordIndex * MAX_WORD_LENGTH;
-        DicNodeUtils::appendTwoWords(0, 0, shortcutTarget, shortcutTargetStringLength,
-                &outputCodePoints[startIndex2]);
-        ++outputWordIndex;
+        outSuggestionResults->addSuggestion(shortcutTarget, shortcutTargetStringLength,
+                std::max(S_INT_MIN + 1, shortcutScore) - 1, kind, NOT_AN_INDEX,
+                NOT_A_FIRST_WORD_CONFIDENCE);
     }
-    return outputWordIndex;
 }
 } // namespace latinime
diff --git a/native/jni/src/suggest/core/result/suggestions_output_utils.h b/native/jni/src/suggest/core/result/suggestions_output_utils.h
index d456a54..73cdb95 100644
--- a/native/jni/src/suggest/core/result/suggestions_output_utils.h
+++ b/native/jni/src/suggest/core/result/suggestions_output_utils.h
@@ -25,16 +25,15 @@
 class DicNode;
 class DicTraverseSession;
 class Scoring;
+class SuggestionResults;
 
 class SuggestionsOutputUtils {
  public:
     /**
      * Outputs the final list of suggestions (i.e., terminal nodes).
      */
-    static int outputSuggestions(const Scoring *const scoringPolicy,
-            DicTraverseSession *traverseSession, int *outputScores, int *outputCodePoints,
-            int *outputIndicesToPartialCommit, int *outputTypes,
-            int *outputAutoCommitFirstWordConfidence);
+    static void outputSuggestions(const Scoring *const scoringPolicy,
+            DicTraverseSession *traverseSession, SuggestionResults *const outSuggestionResults);
 
  private:
     DISALLOW_IMPLICIT_CONSTRUCTORS(SuggestionsOutputUtils);
@@ -42,11 +41,15 @@
     // Inputs longer than this will autocorrect if the suggestion is multi-word
     static const int MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT;
 
+    static void outputSuggestionsOfDicNode(const Scoring *const scoringPolicy,
+            DicTraverseSession *traverseSession, const DicNode *const terminalDicNode,
+            const float languageWeight, const bool boostExactMatches,
+            const bool forceCommitMultiWords, const bool outputSecondWordFirstLetterInputIndex,
+            SuggestionResults *const outSuggestionResults);
+    static void outputShortcuts(BinaryDictionaryShortcutIterator *const shortcutIt,
+            const int finalScore, const bool sameAsTyped,
+            SuggestionResults *const outSuggestionResults);
     static int computeFirstWordConfidence(const DicNode *const terminalDicNode);
-
-    static int outputShortcuts(BinaryDictionaryShortcutIterator *const shortcutIt,
-            int outputWordIndex, const int finalScore, int *const outputCodePoints,
-            int *const outputScores, int *const outputTypes, const bool sameAsTyped);
 };
 } // namespace latinime
 #endif // LATINIME_SUGGESTIONS_OUTPUT_UTILS
diff --git a/native/jni/src/suggest/core/session/dic_traverse_session.cpp b/native/jni/src/suggest/core/session/dic_traverse_session.cpp
index 5070491..77b634e 100644
--- a/native/jni/src/suggest/core/session/dic_traverse_session.cpp
+++ b/native/jni/src/suggest/core/session/dic_traverse_session.cpp
@@ -68,7 +68,6 @@
     mDicNodesCache.reset(thresholdForNextActiveDicNodes /* nextActiveSize */,
             maxWords /* terminalSize */);
     mMultiBigramMap.clear();
-    mPartiallyCommited = false;
 }
 
 void DicTraverseSession::initializeProximityInfoStates(const int *const inputCodePoints,
diff --git a/native/jni/src/suggest/core/session/dic_traverse_session.h b/native/jni/src/suggest/core/session/dic_traverse_session.h
index b718fb5..9e5d902 100644
--- a/native/jni/src/suggest/core/session/dic_traverse_session.h
+++ b/native/jni/src/suggest/core/session/dic_traverse_session.h
@@ -61,7 +61,7 @@
     AK_FORCE_INLINE DicTraverseSession(JNIEnv *env, jstring localeStr, bool usesLargeCache)
             : mPrevWordPtNodePos(NOT_A_DICT_POS), mProximityInfo(nullptr),
               mDictionary(nullptr), mSuggestOptions(nullptr), mDicNodesCache(usesLargeCache),
-              mMultiBigramMap(), mInputSize(0), mPartiallyCommited(false), mMaxPointerCount(1),
+              mMultiBigramMap(), mInputSize(0), mMaxPointerCount(1),
               mMultiWordCostMultiplier(1.0f) {
         // NOTE: mProximityInfoStates is an array of instances.
         // No need to initialize it explicitly here.
@@ -95,8 +95,6 @@
         return &mProximityInfoStates[id];
     }
     int getInputSize() const { return mInputSize; }
-    void setPartiallyCommited() { mPartiallyCommited = true; }
-    bool isPartiallyCommited() const { return mPartiallyCommited; }
 
     bool isOnlyOnePointerUsed(int *pointerId) const {
         // Not in the dictionary word
@@ -188,7 +186,6 @@
     ProximityInfoState mProximityInfoStates[MAX_POINTER_COUNT_G];
 
     int mInputSize;
-    bool mPartiallyCommited;
     int mMaxPointerCount;
 
     /////////////////////////////////
diff --git a/native/jni/src/suggest/core/suggest.cpp b/native/jni/src/suggest/core/suggest.cpp
index f6de571..303182c 100644
--- a/native/jni/src/suggest/core/suggest.cpp
+++ b/native/jni/src/suggest/core/suggest.cpp
@@ -42,10 +42,9 @@
  * automatically activated for sequential calls that share the same starting input.
  * TODO: Stop detecting continuous suggestion. Start using traverseSession instead.
  */
-int Suggest::getSuggestions(ProximityInfo *pInfo, void *traverseSession,
+void Suggest::getSuggestions(ProximityInfo *pInfo, void *traverseSession,
         int *inputXs, int *inputYs, int *times, int *pointerIds, int *inputCodePoints,
-        int inputSize, int *outWords, int *outputScores, int *outputIndices,
-        int *outputTypes, int *outputAutoCommitFirstWordConfidence) const {
+        int inputSize, SuggestionResults *const outSuggestionResults) const {
     PROF_OPEN;
     PROF_START(0);
     const float maxSpatialDistance = TRAVERSAL->getMaxSpatialDistance();
@@ -66,11 +65,9 @@
     }
     PROF_END(1);
     PROF_START(2);
-    const int size = SuggestionsOutputUtils::outputSuggestions(SCORING, tSession, outputScores,
-            outWords, outputIndices, outputTypes, outputAutoCommitFirstWordConfidence);
+    SuggestionsOutputUtils::outputSuggestions(SCORING, tSession, outSuggestionResults);
     PROF_END(2);
     PROF_CLOSE;
-    return size;
 }
 
 /**
@@ -268,7 +265,6 @@
             traverseSession->getDicTraverseCache()->copyPushNextActive(dicNode);
         }
     }
-    DicNode::managedDelete(dicNode);
 }
 
 void Suggest::processDicNodeAsMatch(DicTraverseSession *traverseSession,
@@ -391,7 +387,6 @@
                 processExpandedDicNode(traverseSession, childDicNode2);
             }
         }
-        DicNode::managedDelete(childDicNodes1[i]);
     }
 }
 
diff --git a/native/jni/src/suggest/core/suggest.h b/native/jni/src/suggest/core/suggest.h
index 33ea0b6..13ad621 100644
--- a/native/jni/src/suggest/core/suggest.h
+++ b/native/jni/src/suggest/core/suggest.h
@@ -36,6 +36,7 @@
 class DicTraverseSession;
 class ProximityInfo;
 class Scoring;
+class SuggestionResults;
 class Traversal;
 class Weighting;
 
@@ -46,10 +47,9 @@
               SCORING(suggestPolicy ? suggestPolicy->getScoring() : nullptr),
               WEIGHTING(suggestPolicy ? suggestPolicy->getWeighting() : nullptr) {}
     AK_FORCE_INLINE virtual ~Suggest() {}
-    int getSuggestions(ProximityInfo *pInfo, void *traverseSession, int *inputXs, int *inputYs,
-            int *times, int *pointerIds, int *inputCodePoints, int inputSize, int *outWords,
-            int *outputScores, int *outputIndices, int *outputTypes,
-            int *outputAutoCommitFirstWordConfidence) const;
+    void getSuggestions(ProximityInfo *pInfo, void *traverseSession, int *inputXs, int *inputYs,
+            int *times, int *pointerIds, int *inputCodePoints, int inputSize,
+            SuggestionResults *const outSuggestionResults) const;
 
  private:
     DISALLOW_IMPLICIT_CONSTRUCTORS(Suggest);
diff --git a/native/jni/src/suggest/core/suggest_interface.h b/native/jni/src/suggest/core/suggest_interface.h
index f10db83..c3ffea9 100644
--- a/native/jni/src/suggest/core/suggest_interface.h
+++ b/native/jni/src/suggest/core/suggest_interface.h
@@ -22,13 +22,13 @@
 namespace latinime {
 
 class ProximityInfo;
+class SuggestionResults;
 
 class SuggestInterface {
  public:
-    virtual int getSuggestions(ProximityInfo *pInfo, void *traverseSession, int *inputXs,
+    virtual void getSuggestions(ProximityInfo *pInfo, void *traverseSession, int *inputXs,
             int *inputYs, int *times, int *pointerIds, int *inputCodePoints, int inputSize,
-            int *outWords, int *outputScores, int *outputIndices, int *outputTypes,
-            int *outputAutoCommitFirstWordConfidence) const = 0;
+            SuggestionResults *const suggestionResults) const = 0;
     SuggestInterface() {}
     virtual ~SuggestInterface() {}
  private:
diff --git a/native/jni/src/suggest/policyimpl/typing/typing_scoring.h b/native/jni/src/suggest/policyimpl/typing/typing_scoring.h
index 8982800..66ea624 100644
--- a/native/jni/src/suggest/policyimpl/typing/typing_scoring.h
+++ b/native/jni/src/suggest/policyimpl/typing/typing_scoring.h
@@ -32,15 +32,8 @@
  public:
     static const TypingScoring *getInstance() { return &sInstance; }
 
-    AK_FORCE_INLINE bool getMostProbableString(const DicTraverseSession *const traverseSession,
-            const int terminalSize, const float languageWeight, int *const outputCodePoints,
-            int *const type, int *const freq) const {
-        return false;
-    }
-
-    AK_FORCE_INLINE void safetyNetForMostProbableString(const int scoreCount, const int maxScore,
-            int *const outputCodePoints, int *const scores) const {
-    }
+    AK_FORCE_INLINE void getMostProbableString(const DicTraverseSession *const traverseSession,
+            const float languageWeight, SuggestionResults *const outSuggestionResults) const {}
 
     AK_FORCE_INLINE float getAdjustedLanguageWeight(DicTraverseSession *const traverseSession,
             DicNode *const terminals, const int size) const {
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/NeedsToDisplayLanguageTests.java b/tests/src/com/android/inputmethod/keyboard/internal/NeedsToDisplayLanguageTests.java
new file mode 100644
index 0000000..e03bce1
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/internal/NeedsToDisplayLanguageTests.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.internal;
+
+import android.content.Context;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.inputmethod.latin.RichInputMethodManager;
+import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
+
+import java.util.Locale;
+
+@SmallTest
+public class NeedsToDisplayLanguageTests extends AndroidTestCase {
+    private final NeedsToDisplayLanguage mNeedsToDisplayLanguage = new NeedsToDisplayLanguage();
+
+    private RichInputMethodManager mRichImm;
+
+    InputMethodSubtype EN_US;
+    InputMethodSubtype FR;
+    InputMethodSubtype ZZ;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        final Context context = getContext();
+        RichInputMethodManager.init(context);
+        mRichImm = RichInputMethodManager.getInstance();
+        SubtypeLocaleUtils.init(context);
+
+        EN_US = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+                Locale.US.toString(), "qwerty");
+        FR = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+                Locale.FRENCH.toString(), "azerty");
+        ZZ = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+                SubtypeLocaleUtils.NO_LANGUAGE, "qwerty");
+    }
+
+    public void testOneSubtype() {
+        mNeedsToDisplayLanguage.updateEnabledSubtypeCount(1);
+
+        mNeedsToDisplayLanguage.updateIsSystemLanguageSameAsInputLanguage(true /* isSame */);
+        assertFalse("one same English (US)", mNeedsToDisplayLanguage.needsToDisplayLanguage(EN_US));
+        assertTrue("one same NoLanguage", mNeedsToDisplayLanguage.needsToDisplayLanguage(ZZ));
+
+        mNeedsToDisplayLanguage.updateIsSystemLanguageSameAsInputLanguage(false /* isSame */);
+        assertTrue("one diff English (US)", mNeedsToDisplayLanguage.needsToDisplayLanguage(EN_US));
+        assertTrue("one diff NoLanguage", mNeedsToDisplayLanguage.needsToDisplayLanguage(ZZ));
+    }
+
+    public void testTwoSubtype() {
+        mNeedsToDisplayLanguage.updateEnabledSubtypeCount(2);
+
+        mNeedsToDisplayLanguage.updateIsSystemLanguageSameAsInputLanguage(true /* isSame */);
+        assertTrue("two same English (US)", mNeedsToDisplayLanguage.needsToDisplayLanguage(EN_US));
+        assertTrue("two same French", mNeedsToDisplayLanguage.needsToDisplayLanguage(FR));
+        assertTrue("two same NoLanguage", mNeedsToDisplayLanguage.needsToDisplayLanguage(ZZ));
+
+        mNeedsToDisplayLanguage.updateIsSystemLanguageSameAsInputLanguage(false /* isSame */);
+        assertTrue("two diff English (US)", mNeedsToDisplayLanguage.needsToDisplayLanguage(EN_US));
+        assertTrue("two diff French", mNeedsToDisplayLanguage.needsToDisplayLanguage(ZZ));
+        assertTrue("two diff NoLanguage", mNeedsToDisplayLanguage.needsToDisplayLanguage(FR));
+    }
+}
diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
index 918f090..ae2205b 100644
--- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
+++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
@@ -20,6 +20,7 @@
 import android.test.suitebuilder.annotation.LargeTest;
 import android.util.Pair;
 
+import com.android.inputmethod.latin.makedict.BinaryDictIOUtils;
 import com.android.inputmethod.latin.makedict.CodePointUtils;
 import com.android.inputmethod.latin.makedict.DictDecoder;
 import com.android.inputmethod.latin.makedict.DictionaryHeader;
@@ -151,7 +152,8 @@
         binaryDictionary.flushWithGC();
         binaryDictionary.close();
 
-        final DictDecoder dictDecoder = FormatSpec.getDictDecoder(dictFile, 0, dictFile.length());
+        final DictDecoder dictDecoder =
+                BinaryDictIOUtils.getDictDecoder(dictFile, 0, dictFile.length());
         try {
             final FusionDictionary dict =
                     dictDecoder.readDictionaryBinary(false /* deleteDictIfBroken */);
diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTests.java b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
index d4e6ad8..b366452 100644
--- a/tests/src/com/android/inputmethod/latin/InputLogicTests.java
+++ b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
@@ -474,4 +474,43 @@
                 WORD_TO_TYPE.length() * TIMES_TO_TYPE - TIMES_TO_BACKSPACE,
                 mEditText.getText().length());
     }
+
+    public void testManySingleQuotes() {
+        final String WORD_TO_AUTOCORRECT = "i";
+        final String WORD_AUTOCORRECTED = "I";
+        final String QUOTES = "''''''''''''''''''''";
+        final String WORD_TO_TYPE = WORD_TO_AUTOCORRECT + QUOTES + " ";
+        final String EXPECTED_RESULT = WORD_AUTOCORRECTED + QUOTES + " ";
+        type(WORD_TO_TYPE);
+        assertEquals("auto-correct with many trailing single quotes", EXPECTED_RESULT,
+                mEditText.getText().toString());
+    }
+
+    public void testManySingleQuotesOneByOne() {
+        final String WORD_TO_AUTOCORRECT = "i";
+        final String WORD_AUTOCORRECTED = "I";
+        final String QUOTES = "''''''''''''''''''''";
+        final String WORD_TO_TYPE = WORD_TO_AUTOCORRECT + QUOTES + " ";
+        final String EXPECTED_RESULT = WORD_AUTOCORRECTED + QUOTES + " ";
+
+        for (int i = 0; i < WORD_TO_TYPE.length(); ++i) {
+            type(WORD_TO_TYPE.substring(i, i+1));
+            sleep(DELAY_TO_WAIT_FOR_PREDICTIONS);
+            runMessages();
+        }
+        assertEquals("type many trailing single quotes one by one", EXPECTED_RESULT,
+                mEditText.getText().toString());
+    }
+
+    public void testTypingSingleQuotesOneByOne() {
+        final String WORD_TO_TYPE = "it's ";
+        final String EXPECTED_RESULT = WORD_TO_TYPE;
+        for (int i = 0; i < WORD_TO_TYPE.length(); ++i) {
+            type(WORD_TO_TYPE.substring(i, i+1));
+            sleep(DELAY_TO_WAIT_FOR_PREDICTIONS);
+            runMessages();
+        }
+        assertEquals("type words letter by letter", EXPECTED_RESULT,
+                mEditText.getText().toString());
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java b/tests/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java
similarity index 100%
rename from java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java
rename to tests/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
index 4bf6174..f29fc21 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
@@ -251,7 +251,7 @@
 
         FusionDictionary dict = null;
         try {
-            final DictDecoder dictDecoder = FormatSpec.getDictDecoder(file, 0, file.length(),
+            final DictDecoder dictDecoder = BinaryDictIOUtils.getDictDecoder(file, 0, file.length(),
                     bufferType);
             now = System.currentTimeMillis();
             dict = dictDecoder.readDictionaryBinary(false /* deleteDictIfBroken */);
@@ -414,7 +414,7 @@
 
         long now = -1, diff = -1;
         try {
-            final DictDecoder dictDecoder = FormatSpec.getDictDecoder(file, 0, file.length(),
+            final DictDecoder dictDecoder = BinaryDictIOUtils.getDictDecoder(file, 0, file.length(),
                     bufferType);
             now = System.currentTimeMillis();
             dictDecoder.readUnigramsAndBigramsBinary(resultWords, resultFreqs, resultBigrams);
@@ -539,7 +539,7 @@
         addBigrams(dict, words, bigrams);
         timeWritingDictToFile(file, dict, formatOptions);
 
-        final DictDecoder dictDecoder = FormatSpec.getDictDecoder(file, 0, file.length(),
+        final DictDecoder dictDecoder = BinaryDictIOUtils.getDictDecoder(file, 0, file.length(),
                 DictDecoder.USE_BYTEARRAY);
         try {
             dictDecoder.openDictBuffer();
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java
similarity index 98%
rename from java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java
rename to tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java
index b534bcb..6f8b07a 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java
@@ -28,7 +28,6 @@
  *
  * All the methods in this class are static.
  *
- * TODO: Remove calls from classes except Ver3DictDecoder
  * TODO: Move this file to makedict/internal.
  * TODO: Rename this class to DictDecoderUtils.
  */
@@ -356,7 +355,7 @@
      * @return true if it's a binary dictionary, false otherwise
      */
     public static boolean isBinaryDictionary(final File file) {
-        final DictDecoder dictDecoder = FormatSpec.getDictDecoder(file, 0, file.length());
+        final DictDecoder dictDecoder = BinaryDictIOUtils.getDictDecoder(file, 0, file.length());
         if (dictDecoder == null) {
             return false;
         }
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java
similarity index 100%
rename from java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java
rename to tests/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
similarity index 88%
rename from java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
rename to tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
index a180f1c..42a50be 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
@@ -18,7 +18,9 @@
 
 import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.makedict.DictDecoder.DictionaryBufferFactory;
 
+import java.io.File;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.util.ArrayList;
@@ -32,6 +34,38 @@
         // This utility class is not publicly instantiable.
     }
 
+    /**
+     * Returns new dictionary decoder.
+     *
+     * @param dictFile the dictionary file.
+     * @param bufferType The type of buffer, as one of USE_* in DictDecoder.
+     * @return new dictionary decoder if the dictionary file exists, otherwise null.
+     */
+    public static DictDecoder getDictDecoder(final File dictFile, final long offset,
+            final long length, final int bufferType) {
+        if (dictFile.isDirectory()) {
+            return new Ver4DictDecoder(dictFile, bufferType);
+        } else if (dictFile.isFile()) {
+            return new Ver2DictDecoder(dictFile, offset, length, bufferType);
+        }
+        return null;
+    }
+
+    public static DictDecoder getDictDecoder(final File dictFile, final long offset,
+            final long length, final DictionaryBufferFactory factory) {
+        if (dictFile.isDirectory()) {
+            return new Ver4DictDecoder(dictFile, factory);
+        } else if (dictFile.isFile()) {
+            return new Ver2DictDecoder(dictFile, offset, length, factory);
+        }
+        return null;
+    }
+
+    public static DictDecoder getDictDecoder(final File dictFile, final long offset,
+            final long length) {
+        return getDictDecoder(dictFile, offset, length, DictDecoder.USE_READONLY_BYTEBUFFER);
+    }
+
     private static final class Position {
         public static final int NOT_READ_PTNODE_COUNT = -1;
 
diff --git a/java/src/com/android/inputmethod/latin/makedict/DictDecoder.java b/tests/src/com/android/inputmethod/latin/makedict/DictDecoder.java
similarity index 100%
rename from java/src/com/android/inputmethod/latin/makedict/DictDecoder.java
rename to tests/src/com/android/inputmethod/latin/makedict/DictDecoder.java
diff --git a/java/src/com/android/inputmethod/latin/makedict/DictEncoder.java b/tests/src/com/android/inputmethod/latin/makedict/DictEncoder.java
similarity index 100%
rename from java/src/com/android/inputmethod/latin/makedict/DictEncoder.java
rename to tests/src/com/android/inputmethod/latin/makedict/DictEncoder.java
diff --git a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java b/tests/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
similarity index 100%
rename from java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
rename to tests/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
diff --git a/tools/dicttool/src/com/android/inputmethod/latin/makedict/MakedictLog.java b/tests/src/com/android/inputmethod/latin/makedict/MakedictLog.java
similarity index 100%
rename from tools/dicttool/src/com/android/inputmethod/latin/makedict/MakedictLog.java
rename to tests/src/com/android/inputmethod/latin/makedict/MakedictLog.java
diff --git a/java/src/com/android/inputmethod/latin/makedict/PendingAttribute.java b/tests/src/com/android/inputmethod/latin/makedict/PendingAttribute.java
similarity index 100%
rename from java/src/com/android/inputmethod/latin/makedict/PendingAttribute.java
rename to tests/src/com/android/inputmethod/latin/makedict/PendingAttribute.java
diff --git a/java/src/com/android/inputmethod/latin/makedict/PtNodeInfo.java b/tests/src/com/android/inputmethod/latin/makedict/PtNodeInfo.java
similarity index 100%
rename from java/src/com/android/inputmethod/latin/makedict/PtNodeInfo.java
rename to tests/src/com/android/inputmethod/latin/makedict/PtNodeInfo.java
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java
similarity index 100%
rename from java/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java
rename to tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java
similarity index 100%
rename from java/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java
rename to tests/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java
similarity index 100%
rename from java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java
rename to tests/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
similarity index 100%
rename from java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
rename to tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
diff --git a/tests/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtilsTests.java
new file mode 100644
index 0000000..d866391
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtilsTests.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2014 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.utils;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import com.android.inputmethod.latin.BinaryDictionary;
+import com.android.inputmethod.latin.makedict.DictionaryHeader;
+import com.android.inputmethod.latin.makedict.FormatSpec;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+@LargeTest
+public class BinaryDictionaryUtilsTests extends AndroidTestCase {
+    private static final String TEST_DICT_FILE_EXTENSION = ".testDict";
+    private static final String TEST_LOCALE = "test";
+
+    private File createEmptyDictionaryAndGetFile(final String dictId,
+            final int formatVersion) throws IOException {
+        if (formatVersion == FormatSpec.VERSION4) {
+            return createEmptyVer4DictionaryAndGetFile(dictId);
+        } else {
+            throw new IOException("Dictionary format version " + formatVersion
+                    + " is not supported.");
+        }
+    }
+
+    private File createEmptyVer4DictionaryAndGetFile(final String dictId) throws IOException {
+        final File file = getDictFile(dictId);
+        FileUtils.deleteRecursively(file);
+        Map<String, String> attributeMap = new HashMap<String, String>();
+        attributeMap.put(DictionaryHeader.DICTIONARY_ID_KEY, dictId);
+        attributeMap.put(DictionaryHeader.DICTIONARY_VERSION_KEY,
+                String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())));
+        attributeMap.put(DictionaryHeader.USES_FORGETTING_CURVE_KEY,
+                DictionaryHeader.ATTRIBUTE_VALUE_TRUE);
+        attributeMap.put(DictionaryHeader.HAS_HISTORICAL_INFO_KEY,
+                DictionaryHeader.ATTRIBUTE_VALUE_TRUE);
+        if (BinaryDictionaryUtils.createEmptyDictFile(file.getAbsolutePath(), FormatSpec.VERSION4,
+                LocaleUtils.constructLocaleFromString(TEST_LOCALE), attributeMap)) {
+            return file;
+        } else {
+            throw new IOException("Empty dictionary " + file.getAbsolutePath()
+                    + " cannot be created.");
+        }
+    }
+
+    private File getDictFile(final String dictId) {
+        return new File(getContext().getCacheDir(), dictId + TEST_DICT_FILE_EXTENSION);
+    }
+
+    public void testRenameDictionary() {
+        final int formatVersion = FormatSpec.VERSION4;
+        File dictFile0 = null;
+        try {
+            dictFile0 = createEmptyDictionaryAndGetFile("MoveFromDictionary", formatVersion);
+        } catch (IOException e) {
+            fail("IOException while writing an initial dictionary : " + e);
+        }
+        final File dictFile1 = getDictFile("MoveToDictionary");
+        FileUtils.deleteRecursively(dictFile1);
+        assertTrue(BinaryDictionaryUtils.renameDict(dictFile0, dictFile1));
+        assertFalse(dictFile0.exists());
+        assertTrue(dictFile1.exists());
+        BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile1.getAbsolutePath(),
+                0 /* offset */, dictFile1.length(), true /* useFullEditDistance */,
+                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+        assertTrue(binaryDictionary.isValidDictionary());
+        assertTrue(binaryDictionary.getFormatVersion() == formatVersion);
+        binaryDictionary.close();
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/utils/ByteArrayDictBuffer.java b/tests/src/com/android/inputmethod/latin/utils/ByteArrayDictBuffer.java
similarity index 100%
rename from java/src/com/android/inputmethod/latin/utils/ByteArrayDictBuffer.java
rename to tests/src/com/android/inputmethod/latin/utils/ByteArrayDictBuffer.java
diff --git a/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java
index 25f57eb..eb8a61a 100644
--- a/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java
@@ -213,13 +213,13 @@
     //  fr_CH swiss   F  Français (Suisse)
     //  de    qwertz  F  Allemand
     //  de_CH swiss   F  Allemand (Suisse)
-    //  zz    qwerty  F  Aucune langue (QWERTY)
+    //  zz    qwerty  F  Alphabet latin (QWERTY)
     //  fr    qwertz  T  Français (QWERTZ)
     //  de    qwerty  T  Allemand (QWERTY)
     //  en_US azerty  T  Anglais (États-Unis) (AZERTY)   exception
     //  en_UK dvorak  T  Anglais (Royaume-Uni) (Dvorak)   exception
     //  es_US colemak T  Espagnol (États-Unis) (Colemak)  exception
-    //  zz    pc      T  Alphabet (PC)
+    //  zz    pc      T  Alphabet latin (PC)
 
     public void testPredefinedSubtypesInFrenchSystemLocale() {
         final RunInLocale<Void> tests = new RunInLocale<Void>() {
@@ -303,39 +303,23 @@
         }
     }
 
-    public void testAllShortDisplayNameForSpacebar() {
-        for (final InputMethodSubtype subtype : mSubtypesList) {
-            final String subtypeName = SubtypeLocaleUtils
-                    .getSubtypeDisplayNameInSystemLocale(subtype);
-            final Locale locale = SubtypeLocaleUtils.getSubtypeLocale(subtype);
-            final String spacebarText = SubtypeLocaleUtils.getShortDisplayName(subtype);
-            final String languageCode = StringUtils.capitalizeFirstCodePoint(
-                    locale.getLanguage(), locale);
-            if (SubtypeLocaleUtils.isNoLanguage(subtype)) {
-                assertEquals(subtypeName, "", spacebarText);
-            } else {
-                assertEquals(subtypeName, languageCode, spacebarText);
-            }
-        }
-    }
-
     // InputMethodSubtype's display name for spacebar text in its locale.
     //        isAdditionalSubtype (T=true, F=false)
-    // locale layout  | Short  Middle      Full
-    // ------ ------- - ---- --------- ----------------------
-    //  en_US qwerty  F  En  English   English (US)           exception
-    //  en_GB qwerty  F  En  English   English (UK)           exception
-    //  es_US spanish F  Es  Español   Español (EE.UU.)       exception
-    //  fr    azerty  F  Fr  Français  Français
-    //  fr_CA qwerty  F  Fr  Français  Français (Canada)
-    //  fr_CH swiss   F  Fr  Français  Français (Suisse)
-    //  de    qwertz  F  De  Deutsch   Deutsch
-    //  de_CH swiss   F  De  Deutsch   Deutsch (Schweiz)
-    //  zz    qwerty  F      QWERTY    QWERTY
-    //  fr    qwertz  T  Fr  Français  Français
-    //  de    qwerty  T  De  Deutsch   Deutsch
-    //  en_US azerty  T  En  English   English (US)
-    //  zz    azerty  T      AZERTY    AZERTY
+    // locale layout  |  Middle    Full
+    // ------ ------- - --------- ----------------------
+    //  en_US qwerty  F  English   English (US)           exception
+    //  en_GB qwerty  F  English   English (UK)           exception
+    //  es_US spanish F  Español   Español (EE.UU.)       exception
+    //  fr    azerty  F  Français  Français
+    //  fr_CA qwerty  F  Français  Français (Canada)
+    //  fr_CH swiss   F  Français  Français (Suisse)
+    //  de    qwertz  F  Deutsch   Deutsch
+    //  de_CH swiss   F  Deutsch   Deutsch (Schweiz)
+    //  zz    qwerty  F  QWERTY    QWERTY
+    //  fr    qwertz  T  Français  Français
+    //  de    qwerty  T  Deutsch   Deutsch
+    //  en_US azerty  T  English   English (US)
+    //  zz    azerty  T  AZERTY    AZERTY
 
     private final RunInLocale<Void> testsPredefinedSubtypesForSpacebar = new RunInLocale<Void>() {
         @Override
@@ -363,16 +347,6 @@
             assertEquals("de   ", "Deutsch",  SubtypeLocaleUtils.getMiddleDisplayName(DE));
             assertEquals("de_CH", "Deutsch",  SubtypeLocaleUtils.getMiddleDisplayName(DE_CH));
             assertEquals("zz   ", "QWERTY",   SubtypeLocaleUtils.getMiddleDisplayName(ZZ));
-
-            assertEquals("en_US", "En", SubtypeLocaleUtils.getShortDisplayName(EN_US));
-            assertEquals("en_GB", "En", SubtypeLocaleUtils.getShortDisplayName(EN_GB));
-            assertEquals("es_US", "Es", SubtypeLocaleUtils.getShortDisplayName(ES_US));
-            assertEquals("fr   ", "Fr", SubtypeLocaleUtils.getShortDisplayName(FR));
-            assertEquals("fr_CA", "Fr", SubtypeLocaleUtils.getShortDisplayName(FR_CA));
-            assertEquals("fr_CH", "Fr", SubtypeLocaleUtils.getShortDisplayName(FR_CH));
-            assertEquals("de   ", "De", SubtypeLocaleUtils.getShortDisplayName(DE));
-            assertEquals("de_CH", "De", SubtypeLocaleUtils.getShortDisplayName(DE_CH));
-            assertEquals("zz   ", "",   SubtypeLocaleUtils.getShortDisplayName(ZZ));
             return null;
         }
     };
@@ -397,12 +371,6 @@
                     SubtypeLocaleUtils.getMiddleDisplayName(EN_US_AZERTY));
             assertEquals("zz azerty",    "AZERTY",
                     SubtypeLocaleUtils.getMiddleDisplayName(ZZ_AZERTY));
-
-            assertEquals("fr qwertz",    "Fr", SubtypeLocaleUtils.getShortDisplayName(FR_QWERTZ));
-            assertEquals("de qwerty",    "De", SubtypeLocaleUtils.getShortDisplayName(DE_QWERTY));
-            assertEquals("en_US azerty", "En",
-                    SubtypeLocaleUtils.getShortDisplayName(EN_US_AZERTY));
-            assertEquals("zz azerty",    "",   SubtypeLocaleUtils.getShortDisplayName(ZZ_AZERTY));
             return null;
         }
     };
diff --git a/tools/dicttool/Android.mk b/tools/dicttool/Android.mk
index 0e9c14e..f49dee7 100644
--- a/tools/dicttool/Android.mk
+++ b/tools/dicttool/Android.mk
@@ -30,11 +30,12 @@
 LATINIME_BASE_SOURCE_DIRECTORY := $(LATINIME_LOCAL_DIR)/java/src/com/android/inputmethod
 LATINIME_ANNOTATIONS_SOURCE_DIRECTORY := $(LATINIME_BASE_SOURCE_DIRECTORY)/annotations
 MAKEDICT_CORE_SOURCE_DIRECTORY := $(LATINIME_BASE_SOURCE_DIRECTORY)/latin/makedict
+LATINIME_TESTS_SOURCE_DIRECTORY := $(LATINIME_LOCAL_DIR)/tests/src/com/android/inputmethod/latin
 
 # Dependencies for Dicttool. Most of these files are needed by BinaryDictionary.java. Note that
 # a significant part of the dependencies are mocked in the compat/ directory, with empty or
 # nearly-empty implementations, for parts that we don't use in Dicttool.
-LATINIME_SRCS_FOR_DICTTOOL := \
+LATINIME_SRC_FILES_FOR_DICTTOOL := \
         event/Combiner.java \
         event/Event.java \
         latin/BinaryDictionary.java \
@@ -47,7 +48,6 @@
         latin/WordComposer.java \
         latin/settings/NativeSuggestOptions.java \
         latin/utils/BinaryDictionaryUtils.java \
-        latin/utils/ByteArrayDictBuffer.java \
         latin/utils/CollectionUtils.java \
         latin/utils/CombinedFormatUtils.java \
         latin/utils/CoordinateUtils.java \
@@ -56,8 +56,13 @@
         latin/utils/LocaleUtils.java \
         latin/utils/ResizableIntArray.java \
         latin/utils/StringUtils.java
-USED_TARGETED_SRCS := $(addprefix $(LATINIME_BASE_SOURCE_DIRECTORY)/, \
-        $(LATINIME_SRCS_FOR_DICTTOOL))
+
+LATINIME_TEST_SRC_FILES_FOR_DICTTOOL := \
+        utils/ByteArrayDictBuffer.java
+
+USED_TARGETED_SRCS := \
+        $(addprefix $(LATINIME_BASE_SOURCE_DIRECTORY)/, $(LATINIME_SRC_FILES_FOR_DICTTOOL)) \
+        $(addprefix $(LATINIME_TESTS_SOURCE_DIRECTORY)/, $(LATINIME_TEST_SRC_FILES_FOR_DICTTOOL))
 
 DICTTOOL_ONDEVICE_TESTS_DIRECTORY := \
         $(LATINIME_LOCAL_DIR)/tests/src/com/android/inputmethod/latin/makedict/
@@ -70,12 +75,11 @@
 
 LOCAL_SRC_FILES := $(LOCAL_TOOL_SRC_FILES) \
         $(filter-out $(addprefix %/, $(notdir $(LOCAL_TOOL_SRC_FILES))), $(LOCAL_MAIN_SRC_FILES)) \
-        $(LOCAL_ANNOTATIONS_SRC_FILES) \
+        $(call all-java-files-under, $(DICTTOOL_COMPAT_TESTS_DIRECTORY)) \
+        $(LOCAL_ANNOTATIONS_SRC_FILES) $(USED_TARGETED_SRCS) \
         $(LATINIME_BASE_SOURCE_DIRECTORY)/latin/Constants.java \
         $(call all-java-files-under, tests) \
-        $(call all-java-files-under, $(DICTTOOL_ONDEVICE_TESTS_DIRECTORY)) \
-        $(call all-java-files-under, $(DICTTOOL_COMPAT_TESTS_DIRECTORY)) \
-        $(USED_TARGETED_SRCS)
+        $(call all-java-files-under, $(DICTTOOL_ONDEVICE_TESTS_DIRECTORY))
 
 LOCAL_JAVA_LIBRARIES := junit
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LATINIME_HOST_NATIVE_LIBNAME)
diff --git a/tools/dicttool/NativeLib.mk b/tools/dicttool/NativeLib.mk
index 95f767d..0d3507b 100644
--- a/tools/dicttool/NativeLib.mk
+++ b/tools/dicttool/NativeLib.mk
@@ -37,9 +37,9 @@
 LOCAL_CFLAGS += -DHOST_TOOL -fPIC -Wno-deprecated
 LOCAL_NO_DEFAULT_COMPILER_FLAGS := true
 
+LOCAL_CLANG := true
 # For C++11
-# TODO: Change this to -std=c++11
-LOCAL_CFLAGS += -std=gnu++0x
+LOCAL_CFLAGS += -std=c++11
 
 LATINIME_NATIVE_JNI_DIR := $(LATINIME_DIR_RELATIVE_TO_DICTTOOL)/native/jni
 LATINIME_NATIVE_SRC_DIR := $(LATINIME_DIR_RELATIVE_TO_DICTTOOL)/native/jni/src
diff --git a/tools/dicttool/compat/com/android/inputmethod/latin/define/JniLibName.java b/tools/dicttool/compat/com/android/inputmethod/latin/define/JniLibName.java
index c68bdaa..d6d5e2d 100644
--- a/tools/dicttool/compat/com/android/inputmethod/latin/define/JniLibName.java
+++ b/tools/dicttool/compat/com/android/inputmethod/latin/define/JniLibName.java
@@ -21,5 +21,5 @@
         // This class is not publicly instantiable.
     }
 
-    public static final String JNI_LIB_NAME = "latinime-dicttool-host";
+    public static final String JNI_LIB_NAME = "latinime-aosp-dicttool-host";
 }
diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtils.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtils.java
index e31ac2a..f9771c8 100644
--- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtils.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtils.java
@@ -17,6 +17,7 @@
 package com.android.inputmethod.latin.dicttool;
 
 import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils;
+import com.android.inputmethod.latin.makedict.BinaryDictIOUtils;
 import com.android.inputmethod.latin.makedict.DictDecoder;
 import com.android.inputmethod.latin.makedict.FormatSpec;
 import com.android.inputmethod.latin.makedict.FusionDictionary;
@@ -191,8 +192,9 @@
                     return CombinedInputOutput.readDictionaryCombined(
                             new BufferedInputStream(new FileInputStream(decodedSpec.mFile)));
                 } else {
-                    final DictDecoder dictDecoder = FormatSpec.getDictDecoder(decodedSpec.mFile,
-                            0, decodedSpec.mFile.length(), DictDecoder.USE_BYTEARRAY);
+                    final DictDecoder dictDecoder = BinaryDictIOUtils.getDictDecoder(
+                            decodedSpec.mFile, 0, decodedSpec.mFile.length(),
+                            DictDecoder.USE_BYTEARRAY);
                     if (report) {
                         System.out.println("Format : Binary dictionary format");
                         System.out.println("Packaging : " + decodedSpec.describeChain());
diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/DictionaryMaker.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/DictionaryMaker.java
index 68d7850..8e8ab19 100644
--- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/DictionaryMaker.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/DictionaryMaker.java
@@ -17,6 +17,7 @@
 package com.android.inputmethod.latin.dicttool;
 
 import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils;
+import com.android.inputmethod.latin.makedict.BinaryDictIOUtils;
 import com.android.inputmethod.latin.makedict.DictDecoder;
 import com.android.inputmethod.latin.makedict.DictEncoder;
 import com.android.inputmethod.latin.makedict.FormatSpec;
@@ -264,7 +265,7 @@
     private static FusionDictionary readBinaryFile(final String binaryFilename)
             throws FileNotFoundException, IOException, UnsupportedFormatException {
         final File file = new File(binaryFilename);
-        final DictDecoder dictDecoder = FormatSpec.getDictDecoder(file, 0, file.length());
+        final DictDecoder dictDecoder = BinaryDictIOUtils.getDictDecoder(file, 0, file.length());
         return dictDecoder.readDictionaryBinary(false /* deleteDictIfBroken */);
     }
 
diff --git a/tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java b/tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java
index a3095da..4f1273b 100644
--- a/tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java
+++ b/tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java
@@ -16,6 +16,8 @@
 
 package com.android.inputmethod.latin.dicttool;
 
+import com.android.inputmethod.latin.Dictionary;
+import com.android.inputmethod.latin.makedict.BinaryDictIOUtils;
 import com.android.inputmethod.latin.makedict.DictDecoder;
 import com.android.inputmethod.latin.makedict.DictEncoder;
 import com.android.inputmethod.latin.makedict.DictionaryHeader;
@@ -77,7 +79,7 @@
             assertEquals("Wrong decode spec", BinaryDictOffdeviceUtils.COMPRESSION, step);
         }
         assertEquals("Wrong decode spec", 3, decodeSpec.mDecoderSpec.size());
-        final DictDecoder dictDecoder = FormatSpec.getDictDecoder(decodeSpec.mFile, 0,
+        final DictDecoder dictDecoder = BinaryDictIOUtils.getDictDecoder(decodeSpec.mFile, 0,
                 decodeSpec.mFile.length());
         final FusionDictionary resultDict =
                 dictDecoder.readDictionaryBinary(false /* deleteDictIfBroken */);