merge in ub-latinimegoogle-edamame-mr1-release history after reset to dce0394e415850225b8b6c90befccf3958a6364f
diff --git a/dictionaries/de_wordlist.combined.gz b/dictionaries/de_wordlist.combined.gz
index 400718d..803211c 100644
--- a/dictionaries/de_wordlist.combined.gz
+++ b/dictionaries/de_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/en_GB_wordlist.combined.gz b/dictionaries/en_GB_wordlist.combined.gz
index bf637e9..1fa9b85 100644
--- a/dictionaries/en_GB_wordlist.combined.gz
+++ b/dictionaries/en_GB_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/en_US_wordlist.combined.gz b/dictionaries/en_US_wordlist.combined.gz
index 9ea04b1..2e039ff 100644
--- a/dictionaries/en_US_wordlist.combined.gz
+++ b/dictionaries/en_US_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/en_wordlist.combined.gz b/dictionaries/en_wordlist.combined.gz
index 87a8633..e845346 100644
--- a/dictionaries/en_wordlist.combined.gz
+++ b/dictionaries/en_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/es_wordlist.combined.gz b/dictionaries/es_wordlist.combined.gz
index 8bb2102..3391e64 100644
--- a/dictionaries/es_wordlist.combined.gz
+++ b/dictionaries/es_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/ro_wordlist.combined.gz b/dictionaries/ro_wordlist.combined.gz
index 829abd9..5307855 100644
--- a/dictionaries/ro_wordlist.combined.gz
+++ b/dictionaries/ro_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/ru_wordlist.combined.gz b/dictionaries/ru_wordlist.combined.gz
index 7794f7b..401ad08 100644
--- a/dictionaries/ru_wordlist.combined.gz
+++ b/dictionaries/ru_wordlist.combined.gz
Binary files differ
diff --git a/java-overridable/src/com/android/inputmethod/latin/define/ProductionFlags.java b/java-overridable/src/com/android/inputmethod/latin/define/ProductionFlags.java
index 9d7258d..5738ea2 100644
--- a/java-overridable/src/com/android/inputmethod/latin/define/ProductionFlags.java
+++ b/java-overridable/src/com/android/inputmethod/latin/define/ProductionFlags.java
@@ -24,14 +24,6 @@
     public static final boolean IS_HARDWARE_KEYBOARD_SUPPORTED = false;
 
     /**
-     * When true, enable {@link InputMethodService#onUpdateCursorAnchorInfo} callback via
-     * {@link InputConnection#requestUpdateCursorAnchorInfo}. This flag has no effect in API
-     * Level 20 and prior. In general, this callback provides detailed positional information,
-     * even though an explicit support is required by the editor.
-     */
-    public static final boolean ENABLE_CURSOR_ANCHOR_INFO_CALLBACK = true;
-
-    /**
      * Include all suggestions from all dictionaries in {@link SuggestedWords#mRawSuggestions}.
      */
     public static final boolean INCLUDE_RAW_SUGGESTIONS = false;
diff --git a/java-overridable/src/com/android/inputmethod/latin/touchinputconsumer/GestureConsumer.java b/java-overridable/src/com/android/inputmethod/latin/touchinputconsumer/GestureConsumer.java
new file mode 100644
index 0000000..672d6d1
--- /dev/null
+++ b/java-overridable/src/com/android/inputmethod/latin/touchinputconsumer/GestureConsumer.java
@@ -0,0 +1,66 @@
+/*
+ * 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.touchinputconsumer;
+
+import android.view.inputmethod.EditorInfo;
+import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.latin.InputPointers;
+import com.android.inputmethod.latin.SuggestedWords;
+import com.android.inputmethod.latin.inputlogic.PrivateCommandPerformer;
+
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Stub for GestureConsumer.
+ * <br>
+ * The methods of this class should only be called from a single thread, e.g.,
+ * the UI Thread.
+ */
+public class GestureConsumer {
+    public static final GestureConsumer NULL_GESTURE_CONSUMER =
+            new GestureConsumer();
+
+    public static GestureConsumer newInstance(
+            final EditorInfo editorInfo, final PrivateCommandPerformer commandPerformer,
+            final List<Locale> locales, final Keyboard keyboard) {
+        return GestureConsumer.NULL_GESTURE_CONSUMER;
+    }
+
+    private GestureConsumer() {
+    }
+
+    public boolean willConsume() {
+        return false;
+    }
+
+    public void onInit(final List<Locale> locales, final Keyboard keyboard) {
+    }
+
+    public void onGestureStarted(final List<Locale> locales, final Keyboard keyboard) {
+    }
+
+    public void onGestureCanceled() {
+    }
+
+    public void onGestureCompleted(final InputPointers inputPointers) {
+    }
+
+    public void onImeSuggestionsProcessed(final SuggestedWords suggestedWords,
+            final int composingStart, final int composingLength) {
+    }
+}
diff --git a/java-overridable/src/com/android/inputmethod/latin/utils/StatsUtils.java b/java-overridable/src/com/android/inputmethod/latin/utils/StatsUtils.java
index ad34dc2..fcaeca8 100644
--- a/java-overridable/src/com/android/inputmethod/latin/utils/StatsUtils.java
+++ b/java-overridable/src/com/android/inputmethod/latin/utils/StatsUtils.java
@@ -79,4 +79,7 @@
     public static void onWordCommitSuggestionPickedManually(
             final String commitWord, final boolean isBatchMode) {
     }
+
+    public static void onLoadSettings(SettingsValues settingsValues) {
+    }
 }
diff --git a/java/AndroidManifest.xml b/java/AndroidManifest.xml
index 25fa0f9..ee1cef6 100644
--- a/java/AndroidManifest.xml
+++ b/java/AndroidManifest.xml
@@ -89,8 +89,7 @@
 
         <activity android:name=".settings.SettingsActivity"
                 android:theme="@style/platformSettingsTheme"
-                android:label="@string/english_ime_settings"
-                android:uiOptions="splitActionBarWhenNarrow">
+                android:label="@string/english_ime_settings">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
             </intent-filter>
diff --git a/java/res/raw/main_de.dict b/java/res/raw/main_de.dict
index 3cbf710..45b2883 100644
--- a/java/res/raw/main_de.dict
+++ b/java/res/raw/main_de.dict
Binary files differ
diff --git a/java/res/raw/main_en.dict b/java/res/raw/main_en.dict
index d631d6f..5bbb857 100644
--- a/java/res/raw/main_en.dict
+++ b/java/res/raw/main_en.dict
Binary files differ
diff --git a/java/res/raw/main_es.dict b/java/res/raw/main_es.dict
index 8c42fda..fae1318 100644
--- a/java/res/raw/main_es.dict
+++ b/java/res/raw/main_es.dict
Binary files differ
diff --git a/java/res/raw/main_ru.dict b/java/res/raw/main_ru.dict
index 9fdaf31..76b5f80 100644
--- a/java/res/raw/main_ru.dict
+++ b/java/res/raw/main_ru.dict
Binary files differ
diff --git a/java/res/raw/setup_welcome_image.png b/java/res/raw/setup_welcome_image.png
index 98e7313..b726181 100644
--- a/java/res/raw/setup_welcome_image.png
+++ b/java/res/raw/setup_welcome_image.png
Binary files differ
diff --git a/java/res/raw/setup_welcome_video.mp4 b/java/res/raw/setup_welcome_video.mp4
index 224bf25..09be565 100644
--- a/java/res/raw/setup_welcome_video.mp4
+++ b/java/res/raw/setup_welcome_video.mp4
Binary files differ
diff --git a/java/res/values-af/strings-talkback-descriptions.xml b/java/res/values-af/strings-talkback-descriptions.xml
index 3b6ba0a..e2a7206 100644
--- a/java/res/values-af/strings-talkback-descriptions.xml
+++ b/java/res/values-af/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"Hoofletter-I, kol bo"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Onbekende simbool"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Onbekende emosiekoon"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Verveelde gesig"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Verleë gesig"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Gesig met sonbrille"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Verbaasde gesig"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Soenende gesig"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Fronsende gesig"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Alternatiewe karakters is beskikbaar"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Alternatiewe karakters is toegemaak"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Alternatiewe voorstelle is beskikbaar"</string>
diff --git a/java/res/values-af/strings.xml b/java/res/values-af/strings.xml
index 3b53547..5f3a067 100644
--- a/java/res/values-af/strings.xml
+++ b/java/res/values-af/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Klank met sleuteldruk"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Opspring met sleuteldruk"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"Voorkeure"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"Rekeninge en privaatheid"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"Voorkoms en uitlegte"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"Veeltalige opsies"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"Gebaarinvoer"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"Raak hier om te stoor"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Woordeboek beskikbaar"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Sleutelbordtema"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"Wissel rekeninge"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"Geen rekeninge gekies nie"</string>
+    <string name="account_selected" msgid="2846876462199625974">"Gebruik tans <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"OK"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"Kanselleer"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"Meld af"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"Kies \'n rekening om te gebruik"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"Engels (VK)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"Engels (VS)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"Spaans (VS)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"Serwies (Latyns)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"Engels (VK) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"Engels (VS) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"Spaans (VS) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serwies (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Tradisioneel)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Cyrillies)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Latyns)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Geen taal nie (alfabet)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabet (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabet (QWERTZ)"</string>
diff --git a/java/res/values-am/strings-talkback-descriptions.xml b/java/res/values-am/strings-talkback-descriptions.xml
index 84c3cb0..d916d51 100644
--- a/java/res/values-am/strings-talkback-descriptions.xml
+++ b/java/res/values-am/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"አቢይ ሆሄ አይ፣ ነጥብ ከላይ"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"ያልታወቀ ምልክት"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"ያልታወቀ ስሜት ገላጭ ምስል"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"የደበረው ፊት"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"ያፈረ ፊት"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"የጸሐይ መነጽር የሚለብስ ፊት"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"የደነቀው ፊት"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"የሚስም ፊት"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"የሚኮሳተር ፊት"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"ተለዋጭ ቁምፊዎች ይገኛሉ"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"ተለዋጭ ቁምፊዎች ተሰናብተዋል"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"ተለዋጭ የአስተያየት ጥቆማዎች ይገኛሉ"</string>
diff --git a/java/res/values-am/strings.xml b/java/res/values-am/strings.xml
index 1575f45..4e9c4ca 100644
--- a/java/res/values-am/strings.xml
+++ b/java/res/values-am/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"በቁልፍ መጫን ላይ የሚወጣ ድምፅ"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"ቁልፍ ጫን ላይ ብቅ ባይ"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"ምርጫዎች"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"መለያዎች እና ግላዊነት"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"መልክ እና አቀማመጦች"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"ባለብዙ ቋንቋ አማራጮች"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"በጣት ምልክት መተየብ"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"ለማስቀመጥ እዚህ ይንኩ"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"መዝገበ ቃላት አለ"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"የቁልፍ ሰሌዳ ገጽታ"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"መለያዎችን ቀይር"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"ምንም መለያዎች አልተመረጡም"</string>
+    <string name="account_selected" msgid="2846876462199625974">"በአሁኑ ጊዜ <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>ን በመጠቀም ላይ"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"እሺ"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"ይቅር"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"ዘግተህ ውጣ"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"የሚጠቀሙበትን መለያ ይምረጡ"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"እንግሊዘኛ (የታላቋ ብሪታንያ)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"እንግሊዘኛ (ዩ.ኤስ)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"ስፓኒሽኛ (ዩኤስ)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"ሂንግሊሽ"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"ሰርብያኛ (ላቲን)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"እንግሊዝኛ (ዩኬ) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"እንግሊዝኛ (አሜሪካ) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"ስፓኒሽ (አሜሪካ) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"ሂንግሊሽ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"ሰርቢያኛ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ተለምዷዊ)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ሳይሪሊክ)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ላቲን)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"ምንም ቋንቋ (ፊደላት)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"ፊደላት (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"ፊደላት (QWERTZ)"</string>
diff --git a/java/res/values-ar/strings-talkback-descriptions.xml b/java/res/values-ar/strings-talkback-descriptions.xml
index bd2d7c6..7051a73 100644
--- a/java/res/values-ar/strings-talkback-descriptions.xml
+++ b/java/res/values-ar/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"Capital I, dot above"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"رمز غير معروف"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"رمز تعبيري غير معروف"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"وجه يبدو عليه الملل"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"وجه يبدو عليه الإحراج"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"وجه يرتدي نظارات شمسية"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"وجه يبدو عليه الاندهاش"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"وجه يبعث قبلة"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"وجه عبوس"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"تتوفر الأحرف البديلة"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"تم تجاهل الأحرف البديلة"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"تتوفر الاقتراحات البديلة"</string>
diff --git a/java/res/values-ar/strings.xml b/java/res/values-ar/strings.xml
index 3192bf2..b5e9c50 100644
--- a/java/res/values-ar/strings.xml
+++ b/java/res/values-ar/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"صوت عند الضغط على مفتاح"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"انبثاق عند ضغط مفتاح"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"التفضيلات"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"الحسابات والخصوصية"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"المظهر والتنسيقات"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"خيارات اللغات المتعددة"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"الكتابة بالإشارة"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"المس هنا للحفظ"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"القاموس متاح"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"مظهر لوحة المفاتيح"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"تبديل الحسابات"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"لم يتم تحديد أي حساب"</string>
+    <string name="account_selected" msgid="2846876462199625974">"يتم حاليًا استخدام <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"موافق"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"إلغاء"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"الخروج"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"تحديد حساب لاستخدامه"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"الإنجليزية (المملكة المتحدة)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"الإنجليزية (الولايات المتحدة)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"الإسبانية (الأميركية)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"هنجليزية"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"الصربية (اللاتينية)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"الإنجليزية (المملكة المتحدة) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"الإنجليزية (الولايات المتحدة) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"الإسبانية (الولايات المتحدة) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"هنجليزية (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"الصربية (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (التقليدية)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (السريلية)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (اللاتينية)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"بدون لغة (أبجدية)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"‏الأبجدية (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"‏الأبجدية (QWERTZ)"</string>
diff --git a/java/res/values-az-rAZ/strings-talkback-descriptions.xml b/java/res/values-az-rAZ/strings-talkback-descriptions.xml
index 8f23627..aa3c4e3 100644
--- a/java/res/values-az-rAZ/strings-talkback-descriptions.xml
+++ b/java/res/values-az-rAZ/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"Böyük I, üstü nöqtəli"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Naməlum rəmz"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Naməlum emoji"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Darıxan sifət"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Çaşmış sifət"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Gün eynəyi taxan sifət"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Təəccüblü sifət"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Öpən sifət"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Mısmırıqlı sifət"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Alternativ simvollar əlçatandır"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Alternativ simvollar kənarlaşdırılıb"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Alternativ təkliflər əlçatandır"</string>
diff --git a/java/res/values-az-rAZ/strings.xml b/java/res/values-az-rAZ/strings.xml
index f32bd72..238ed0f 100644
--- a/java/res/values-az-rAZ/strings.xml
+++ b/java/res/values-az-rAZ/strings.xml
@@ -27,16 +27,14 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Klikləmə səsi"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Klikləmədə popup"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"Seçimlər"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"Hesablama və məxfilik"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"Görünüş və düzənlər"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"Çoxdilli seçimlər"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"Jest ilə yazma"</string>
     <string name="settings_screen_correction" msgid="1616818407747682955">"Mətn korreksiyası"</string>
     <string name="settings_screen_advanced" msgid="7472408607625972994">"Qabaqcıl"</string>
     <string name="settings_screen_theme" msgid="2137262503543943871">"Tema"</string>
-    <!-- no translation found for enable_split_keyboard (4177264923999493614) -->
-    <skip />
+    <string name="enable_split_keyboard" msgid="4177264923999493614">"Klaviaturanı ayırmağa icazə verin"</string>
     <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Digər daxiletmə metodlarına keçin"</string>
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Dil keçid düyməsi başqa daxiletmə metodlarını da əhatə edir"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"Dil keçidi düyməsi"</string>
@@ -86,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"Yadda saxlamaq üçün buraya toxunun"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Lüğət mövcuddur"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Klaviatura teması"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"Hesablar arasında keçid edin"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"Heç bir hesab seçilməyib"</string>
+    <string name="account_selected" msgid="2846876462199625974">"Hazırda istifadə olunur: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"Ok"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"Ləğv et"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"Çıxış"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"İşlətmək üçün hesab seçin"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"İngilis (BK)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"İngilis (ABŞ)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"İspan (ABŞ)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hingilis"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"Serb (Latın)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"İngilis (Britaniya) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"İngilis (Amerika) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"İspan (Amerika) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hingilis (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serb (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Ənənəvi)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Kiril)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Latın)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Dil yoxdur (Əlifba)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Əlifba (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Əlifba (QWERTZ)"</string>
diff --git a/java/res/values-bg/strings-talkback-descriptions.xml b/java/res/values-bg/strings-talkback-descriptions.xml
index c1271bf..e206d1b 100644
--- a/java/res/values-bg/strings-talkback-descriptions.xml
+++ b/java/res/values-bg/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"Главна буква I с точка отгоре"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Неизвестен символ"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Неизвестен емотикон"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Отегчено лице"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Засрамено лице"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Лице със слънчеви очила"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Изненадано лице"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Целуващо лице"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Намръщено лице"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Налице са алтернативни знаци"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Алтернативните знаци са отхвърлени"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Налице са алтернативни предложения"</string>
diff --git a/java/res/values-bg/strings.xml b/java/res/values-bg/strings.xml
index 68b774f..ed40074 100644
--- a/java/res/values-bg/strings.xml
+++ b/java/res/values-bg/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Звук при натискане на клавиш"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Изскачащ прозорец при натискане на клавиш"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"Предпочитания"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"Профили и поверителност"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"Облик и оформления"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"Опции за няколко езика"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"Въвеждане чрез жест"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"Докоснете тук, за да запазите"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Има достъп до речник"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Тема на клавиатурата"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"Превключване между профилите"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"Няма избрани профили"</string>
+    <string name="account_selected" msgid="2846876462199625974">"Понастоящем използвате <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"ОK"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"Отказ"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"Изход"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"Изберете профил, който да използвате"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"английски (Великобритания)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"английски (САЩ)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"испански (САЩ)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Хинглиш"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"Сръбска (латиница)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"английски (Великобр.) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"английски (САЩ) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"испански (САЩ) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Хинглиш (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Сръбска (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (традиционна клавиатура)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (кирилица)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (латиница)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Без език (латиница)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Латиница (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Латиница (QWERTZ)"</string>
diff --git a/java/res/values-ca/strings-talkback-descriptions.xml b/java/res/values-ca/strings-talkback-descriptions.xml
index 9b01c75..bc944d7 100644
--- a/java/res/values-ca/strings-talkback-descriptions.xml
+++ b/java/res/values-ca/strings-talkback-descriptions.xml
@@ -57,7 +57,7 @@
     <string name="announce_keyboard_mode" msgid="6698257917367823205">"Es mostra el teclat per a <xliff:g id="KEYBOARD_MODE">%s</xliff:g>."</string>
     <string name="keyboard_mode_date" msgid="6597407244976713364">"data"</string>
     <string name="keyboard_mode_date_time" msgid="3642804408726668808">"data i hora"</string>
-    <string name="keyboard_mode_email" msgid="1239682082047693644">"correu electrònic"</string>
+    <string name="keyboard_mode_email" msgid="1239682082047693644">"adreça electrònica"</string>
     <string name="keyboard_mode_im" msgid="3812086215529493501">"missatgeria"</string>
     <string name="keyboard_mode_number" msgid="5395042245837996809">"número"</string>
     <string name="keyboard_mode_phone" msgid="2486230278064523665">"telèfon"</string>
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"I majúscula, punt superior"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Símbol desconegut"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Emoji desconegut"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Cara d\'avorriment"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Cara de vergonya"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Cara amb ulleres de sol"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Cara de sorpresa"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Cara que fa un petó"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Cara que arrufa el nas"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Hi ha caràcters alternatius disponibles."</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Els caràcters alternatius s\'ignoren."</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Hi ha suggeriments alternatius disponibles."</string>
diff --git a/java/res/values-ca/strings.xml b/java/res/values-ca/strings.xml
index f5cfcf5..e39247f 100644
--- a/java/res/values-ca/strings.xml
+++ b/java/res/values-ca/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"So en prémer tecles"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Amplia en prémer tecles"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"Preferències"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"Comptes i privadesa"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"Aparença i dissenys"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"Opcions de diversos idiomes"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"Escriptura gestual"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"Toca aquí per desar."</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Diccionari disponible"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Tema del teclat"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"Canvi de compte"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"No hi ha cap compte seleccionat."</string>
+    <string name="account_selected" msgid="2846876462199625974">"Actualment es fa servir <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>."</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"D\'acord"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"Cancel·la"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"Tanca la sessió"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"Selecció d\'un compte per utilitzar"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"Anglès (Regne Unit)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"Anglès (EUA)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"Espanyol (EUA)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"Serbi (llatí)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"Anglès (Regne Unit) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"Anglès (EUA) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"Espanyol (EUA) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbi (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicional)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ciríl·lic)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (llatí)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Cap idioma (alfabet)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabet (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabet (QWERTZ)"</string>
diff --git a/java/res/values-cs/strings-talkback-descriptions.xml b/java/res/values-cs/strings-talkback-descriptions.xml
index 6c9c800..a52a645 100644
--- a/java/res/values-cs/strings-talkback-descriptions.xml
+++ b/java/res/values-cs/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"Velké I s tečkou nad"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Neznámý znak"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Neznámý smajlík emodži"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Znuděný obličej"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Rozpačitý obličej"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Obličej se slunečními brýlemi"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Překvapený obličej"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Líbající obličej"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Zamračený obličej"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Alternativní znaky jsou k dispozici"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Alternativní znaky jsou ignorovány"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Alternativní návrhy jsou k dispozici"</string>
diff --git a/java/res/values-cs/strings.xml b/java/res/values-cs/strings.xml
index 5972be4..f527557 100644
--- a/java/res/values-cs/strings.xml
+++ b/java/res/values-cs/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Zvuk při stisku klávesy"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Detail znaku při stisku klávesy"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"Předvolby"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"Účty a ochrana soukromí"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"Vzhled a rozvržení"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"Možnosti pro více jazyků"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"Psaní gesty"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"Klepnutím sem položku uložíte"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Slovník k dispozici"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Motiv klávesnice"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"Přepnout účty"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"Nebyly vybrány žádné účty"</string>
+    <string name="account_selected" msgid="2846876462199625974">"Aktuálně používáte účet <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"OK"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"Zrušit"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"Odhlásit se"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"Vyberte účet, který chcete použít"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"angličtina (Velká Británie)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"angličtina (USA)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"španělština (USA)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"srbština (latinka)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"angličtina (VB) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"angličtina (USA) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"španělština (USA) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"srbština (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradiční)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (cyrilice)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (latinka)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Žádný jazyk (latinka)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Latinka (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Latinka (QWERTZ)"</string>
diff --git a/java/res/values-da/strings-talkback-descriptions.xml b/java/res/values-da/strings-talkback-descriptions.xml
index 5f54004..0241092 100644
--- a/java/res/values-da/strings-talkback-descriptions.xml
+++ b/java/res/values-da/strings-talkback-descriptions.xml
@@ -38,7 +38,7 @@
     <string name="spoken_description_settings" msgid="7281251004003143204">"Indstillinger"</string>
     <string name="spoken_description_tab" msgid="8210782459446866716">"Fane"</string>
     <string name="spoken_description_space" msgid="5908716896642059145">"Mellemrum"</string>
-    <string name="spoken_description_mic" msgid="6153138783813452464">"Stemmeinput"</string>
+    <string name="spoken_description_mic" msgid="6153138783813452464">"Taleinput"</string>
     <string name="spoken_description_emoji" msgid="7990051553008088470">"Emoji"</string>
     <string name="spoken_description_return" msgid="3183692287397645708">"Return"</string>
     <string name="spoken_description_search" msgid="5099937658231911288">"Søgning"</string>
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"Stort I med prik over"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Ukendt symbol"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Ukendt emoji"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Træt ansigt"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Forlegent ansigt"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Ansigt med solbriller"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Overrasket ansigt"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Kyssende ansigt"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Surt ansigt"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Alternative tegn er tilgængelige"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Alternative tegn er deaktiveret"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Alternative forslag er tilgængelige"</string>
diff --git a/java/res/values-da/strings.xml b/java/res/values-da/strings.xml
index 7143af5..240250f 100644
--- a/java/res/values-da/strings.xml
+++ b/java/res/values-da/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Lyd ved tastetryk"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Pop op ved tastetryk"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"Præferencer"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"Konti og personlige oplysninger"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"Udseende og layouts"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"Indstillinger for flere sprog"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"Glidende indtastning"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"Klik her for at gemme"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Ordbog er tilgængelig"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Tastaturtema"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"Skift konto"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"Du har ikke valgt en konto"</string>
+    <string name="account_selected" msgid="2846876462199625974">"Du bruger i øjeblikket <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"OK"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"Annuller"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"Log ud"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"Vælg den konto, du vil bruge"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"Engelsk (Storbritannien)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"Engelsk (USA)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"Spansk (USA)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"Serbisk (latinsk)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"Engelsk (UK) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"Engelsk (USA) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"Spansk (USA) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbisk (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (traditionelt)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (kyrillisk)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (latin)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Intet sprog (Alfabet)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabet (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabet (QWERTZ)"</string>
diff --git a/java/res/values-de/strings-talkback-descriptions.xml b/java/res/values-de/strings-talkback-descriptions.xml
index 27e9ecc..3085afe 100644
--- a/java/res/values-de/strings-talkback-descriptions.xml
+++ b/java/res/values-de/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"Großbuchstabe I mit übergesetztem Punkt"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Unbekanntes Symbol"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Unbekanntes Emoji"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Gelangweiltes Gesicht"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Verlegenes Gesicht"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Gesicht mit Sonnenbrille"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Überraschtes Gesicht"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Küssendes Gesicht"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Finsteres Gesicht"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Es sind Alternativzeichen verfügbar."</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Alternativzeichen werden ausgeblendet."</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Es sind Alternativvorschläge verfügbar."</string>
diff --git a/java/res/values-de/strings.xml b/java/res/values-de/strings.xml
index b6b21af..b59c506 100644
--- a/java/res/values-de/strings.xml
+++ b/java/res/values-de/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Ton bei Tastendruck"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Pop-up bei Tastendruck"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"Einstellungen"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"Konten &amp; Datenschutz"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"Darstellung &amp; Layouts"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"Mehrsprachige Optionen"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"Bewegungseingabe"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"Tippen Sie hier zum Speichern."</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Wörterbuch verfügbar"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Tastaturdesign"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"Konto wechseln"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"Keine Konten ausgewählt"</string>
+    <string name="account_selected" msgid="2846876462199625974">"Momentan wird <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g> verwendet."</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"OK"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"Abbrechen"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"Abmelden"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"Zu verwendendes Konto auswählen"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"Englisch (UK)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"Englisch (USA)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"Spanisch (USA)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"Serbisch (Lateinisch)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"Englisch (GB) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"Englisch (USA) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"Spanisch (USA) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbisch (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (traditionell)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (kyrillisch)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (lateinisch)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Keine Sprache (lat. Alphabet)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Lat. Alphabet (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Lat. Alphabet (QWERTZ)"</string>
diff --git a/java/res/values-el/strings-talkback-descriptions.xml b/java/res/values-el/strings-talkback-descriptions.xml
index 10df02f..1300a95 100644
--- a/java/res/values-el/strings-talkback-descriptions.xml
+++ b/java/res/values-el/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"Κεφαλαίο I, κουκκίδα από πάνω"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Άγνωστο σύμβολο"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Άγνωστο emoticon"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Πρόσωπο που βαριέται"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Αμήχανο πρόσωπο"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Πρόσωπο που φορά γυαλιά ηλίου"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Έκπληκτο πρόσωπο"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Πρόσωπο που δίνει φιλί"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Συνοφρυωμένο πρόσωπο"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Διατίθενται εναλλακτικοί χαρακτήρες"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Παράβλεψη εναλλακτικών χαρακτήρων"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Διατίθενται εναλλακτικές προτάσεις"</string>
diff --git a/java/res/values-el/strings.xml b/java/res/values-el/strings.xml
index 4bebe3e..e519e43 100644
--- a/java/res/values-el/strings.xml
+++ b/java/res/values-el/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Ήχος κατά το πάτημα πλήκτρων"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Εμφάνιση με το πάτημα πλήκτρου"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"Προτιμήσεις"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"Λογαριασμοί και απόρρητο"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"Εμφάνιση και διάταξη"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"Επιλογές για πολλές γλώσσες"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"Πληκτρολόγηση με κίνηση"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"Αγγίξτε εδώ για αποθήκευση"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Λεξικό διαθέσιμο"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Θέμα πληκτρολογίου"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"Εναλλαγή λογαριασμών"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"Δεν έχει επιλεγεί κανένας λογαριασμός"</string>
+    <string name="account_selected" msgid="2846876462199625974">"Προς το παρόν, χρησιμοποιείτε τη διεύθυνση <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"ΟΚ"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"Ακύρωση"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"Έξοδος"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"Επιλέξτε λογαριασμό για χρήση"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"Αγγλικά (Η.Β.)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"Αγγλικά (Η.Π.Α)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"Ισπανικά (ΗΠΑ)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"Σερβικά (Λατινικά)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"Αγγλικά (Ηνωμένο Βασίλειο) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"Αγγλικά (ΗΠΑ) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"Ισπανικά (ΗΠΑ) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Σερβικά (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Παραδοσιακά)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Κυριλλικά)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Λατινικά)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Καμία γλώσσα (Αλφάβητο)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Αλφάβητο (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Αλφάβητο (QWERTZ)"</string>
diff --git a/java/res/values-en-rGB/strings-talkback-descriptions.xml b/java/res/values-en-rGB/strings-talkback-descriptions.xml
index e209b54..aa805c9 100644
--- a/java/res/values-en-rGB/strings-talkback-descriptions.xml
+++ b/java/res/values-en-rGB/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"Capital I, dot above"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Unknown symbol"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Unknown emoji"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Bored face"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Embarrassed face"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Face wearing sunglasses"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Surprised face"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Kissing face"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Frowning face"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Alternative characters are available"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Alternative characters are dismissed"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Alternative suggestions are available"</string>
diff --git a/java/res/values-en-rGB/strings.xml b/java/res/values-en-rGB/strings.xml
index 14ddafc..f31b071 100644
--- a/java/res/values-en-rGB/strings.xml
+++ b/java/res/values-en-rGB/strings.xml
@@ -95,13 +95,13 @@
     <string name="subtype_en_US" msgid="6160452336634534239">"English (US)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"Spanish (US)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"Serbian (Latin)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"English (UK) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"English (US) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"Spanish (US) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbian (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Traditional)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Cyrillic)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Latin)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"No language (Alphabet)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alphabet (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alphabet (QWERTZ)"</string>
diff --git a/java/res/values-en-rIN/strings-talkback-descriptions.xml b/java/res/values-en-rIN/strings-talkback-descriptions.xml
index e209b54..aa805c9 100644
--- a/java/res/values-en-rIN/strings-talkback-descriptions.xml
+++ b/java/res/values-en-rIN/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"Capital I, dot above"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Unknown symbol"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Unknown emoji"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Bored face"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Embarrassed face"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Face wearing sunglasses"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Surprised face"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Kissing face"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Frowning face"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Alternative characters are available"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Alternative characters are dismissed"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Alternative suggestions are available"</string>
diff --git a/java/res/values-en-rIN/strings.xml b/java/res/values-en-rIN/strings.xml
index 14ddafc..f31b071 100644
--- a/java/res/values-en-rIN/strings.xml
+++ b/java/res/values-en-rIN/strings.xml
@@ -95,13 +95,13 @@
     <string name="subtype_en_US" msgid="6160452336634534239">"English (US)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"Spanish (US)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"Serbian (Latin)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"English (UK) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"English (US) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"Spanish (US) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbian (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Traditional)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Cyrillic)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Latin)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"No language (Alphabet)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alphabet (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alphabet (QWERTZ)"</string>
diff --git a/java/res/values-es-rUS/strings-talkback-descriptions.xml b/java/res/values-es-rUS/strings-talkback-descriptions.xml
index 0bde67e..3860a99 100644
--- a/java/res/values-es-rUS/strings-talkback-descriptions.xml
+++ b/java/res/values-es-rUS/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"I mayúscula, con punto superior"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Símbolo desconocido"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Emoji desconocido"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Cara de aburrimiento"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Cara de vergüenza"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Cara con anteojos de sol"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Cara de sorpresa"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Cara dando un beso"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Cara con el ceño fruncido"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Hay caracteres alternativos disponibles."</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Los caracteres alternativos se descartan."</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Hay sugerencias alternativas disponibles."</string>
diff --git a/java/res/values-es-rUS/strings.xml b/java/res/values-es-rUS/strings.xml
index a7fb3ae..79b0f92 100644
--- a/java/res/values-es-rUS/strings.xml
+++ b/java/res/values-es-rUS/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Sonar al pulsar las teclas"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Aviso emergente al pulsar tecla"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"Preferencias"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"Cuentas y privacidad"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"Apariencia y diseños"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"Opciones multilingües"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"Escritura gestual"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"Tocar aquí para guardar."</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Diccionario disponible"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Tema del teclado"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"Cambiar cuentas"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"No hay cuentas seleccionadas."</string>
+    <string name="account_selected" msgid="2846876462199625974">"En uso actualmente: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"Aceptar"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"Cancelar"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"Salir"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"Seleccionar una cuenta para usar"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"Inglés (Reino Unido)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"Inglés (EE.UU.)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"Español (EE.UU.)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"Serbio (latino)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"Inglés, Reino Unido (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"Inglés, EE. UU. (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"Español, EE. UU. (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbio (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicional)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (cirílico)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Latinoamérica)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Ningún idioma (alfabeto)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabeto (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabeto (QWERTZ)"</string>
diff --git a/java/res/values-es/strings-talkback-descriptions.xml b/java/res/values-es/strings-talkback-descriptions.xml
index ef95901..0a55036 100644
--- a/java/res/values-es/strings-talkback-descriptions.xml
+++ b/java/res/values-es/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"I maýuscula, punto superior"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Símbolo desconocido"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Emoji desconocido"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Cara aburrida"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Cara avergonzada"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Cara con gafas de sol"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Cara sorprendida"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Cara dando un beso"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Cara con el ceño fruncido"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Caracteres alternativos disponibles"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Los caracteres alternativos se descartarán"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Sugerencias alternativas disponibles"</string>
diff --git a/java/res/values-es/strings.xml b/java/res/values-es/strings.xml
index 56079f1..d099e4f 100644
--- a/java/res/values-es/strings.xml
+++ b/java/res/values-es/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Sonar al pulsar tecla"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Ampliar al pulsar tecla"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"Preferencias"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"Cuentas y privacidad"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"Aspecto y diseño"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"Opciones multilingües"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"Escritura gestual"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"Toca aquí para guardar"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Hay un diccionario disponible"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Tema de teclado"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"Cambiar de cuenta"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"Ninguna cuenta seleccionada"</string>
+    <string name="account_selected" msgid="2846876462199625974">"<xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g> actualmente en uso"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"Aceptar"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"Cancelar"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"Cerrar sesión"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"Selecciona la cuenta que quieras usar"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"inglés (Reino Unido)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"inglés (EE.UU.)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"Español (EE.UU.)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"Serbio (latino)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"Inglés (Reino Unido) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"Inglés (EE.UU.) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"Español (EE.UU.) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbio (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicional)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (cirílico)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Latinoamérica)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Ningún idioma (alfabeto)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabeto (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabeto (QWERTZ)"</string>
@@ -168,7 +160,7 @@
     <string name="no_dictionaries_available" msgid="8039920716566132611">"No hay diccionarios"</string>
     <string name="check_for_updates_now" msgid="8087688440916388581">"Actualizar"</string>
     <string name="last_update" msgid="730467549913588780">"Última actualización"</string>
-    <string name="message_updating" msgid="4457761393932375219">"Buscando actualizaciones"</string>
+    <string name="message_updating" msgid="4457761393932375219">"Buscando actualizaciones..."</string>
     <string name="message_loading" msgid="5638680861387748936">"Cargando…"</string>
     <string name="main_dict_description" msgid="3072821352793492143">"Diccionario principal"</string>
     <string name="cancel" msgid="6830980399865683324">"Cancelar"</string>
diff --git a/java/res/values-et-rEE/strings-talkback-descriptions.xml b/java/res/values-et-rEE/strings-talkback-descriptions.xml
index b47df1b..b94eaba 100644
--- a/java/res/values-et-rEE/strings-talkback-descriptions.xml
+++ b/java/res/values-et-rEE/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"Suurtäht I, ülapunkt"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Tundmatu sümbol"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Tundmatu emoji"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Igavlev nägu"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Piinlikkust tundev nägu"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Päikseprillidega nägu"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Üllatunud nägu"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Suudlev nägu"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Kulmu kortsutav nägu"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Alternatiivsed tähemärgid on saadaval"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Alternatiivsed tähemärgid eemaldatakse"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Alternatiivsed soovitused on saadaval"</string>
diff --git a/java/res/values-et-rEE/strings.xml b/java/res/values-et-rEE/strings.xml
index bb9c78f..e7939e2 100644
--- a/java/res/values-et-rEE/strings.xml
+++ b/java/res/values-et-rEE/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Heli klahvivajutusel"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Klahvivajutusel kuva hüpik"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"Eelistused"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"Kontod ja privaatsus"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"Välimus ja paigutused"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"Mitmekeelsed valikud"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"Joonistusega sisestamine"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"Salvestamiseks puudutage siin"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Sõnastik saadaval"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Klaviatuuri teema"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"Konto vahetamine"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"Ühtegi kontot pole valitud"</string>
+    <string name="account_selected" msgid="2846876462199625974">"Praegu kasutatakse e-posti aadressi <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"OK"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"Tühista"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"Logi välja"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"Kasutatava konto valimine"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"Inglise (UK)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"Inglise (USA)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"hispaania (USA)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hindi-inglise"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"Serbia (ladina)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"Inglise (Ühendk.) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"Inglise (USA) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"Hispaania (USA) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hindi-inglise (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbia (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (traditsiooniline)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (kirillitsa)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ladina)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Keel puudub (tähestik)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Tähestik (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Tähestik (QWERTZ)"</string>
diff --git a/java/res/values-fa/strings-talkback-descriptions.xml b/java/res/values-fa/strings-talkback-descriptions.xml
index 988dd5c..16b37f1 100644
--- a/java/res/values-fa/strings-talkback-descriptions.xml
+++ b/java/res/values-fa/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"‏I بزرگ با نقطه بالایی"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"نماد نامشخص"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"‏شکلک emoji نامشخص"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"صورتک بی‌حوصله"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"صورتک شرمنده"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"صورتک با عینک آفتابی"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"صورتک شگفت‌زده"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"صورتک بوسه‌زن"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"صورتک اخمو"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"نویسه‌های جایگزین در دسترس هستند"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"از نویسه‌های جایگزین صرفنظر می‌شود"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"پیشنهادهای جایگزین در دسترس هستند"</string>
diff --git a/java/res/values-fa/strings.xml b/java/res/values-fa/strings.xml
index 70f481a..336c8ed 100644
--- a/java/res/values-fa/strings.xml
+++ b/java/res/values-fa/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"صدا با فشار کلید"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"بازشدن با فشار کلید"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"تنظیمات ترجیحی"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"حساب‌ها و حریم خصوصی"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"ظاهر و طرح‌بندی‌ها"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"گزینه‌های چندزبانی"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"ورودی اشاره‌ای"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"برای ذخیره اینجا را لمس کنید"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"دیکشنری موجود است"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"طرح زمینه صفحه‌کلید"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"جابجایی بین حساب‌ها"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"هیچ حسابی انتخاب نشده است"</string>
+    <string name="account_selected" msgid="2846876462199625974">"در حال حاضر در حال استفاده از <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"تأیید"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"لغو"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"خروج از سیستم"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"انتخاب یک حساب برای استفاده"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"انگلیسی (بریتانیا)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"انگلیسی (امریکا)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"اسپانیایی (آمریکا)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"هندی انگلیسی"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"صربی (لاتین)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"انگلیسی (بریتانیا) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"انگلیسی (آمریکا) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"اسپانیایی (آمریکا) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"هندی انگلیسی (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"صربی (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (سنتی)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (سیریلیک)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (لاتین)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"بدون زبان (حروف الفبا)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"‏حروف الفبا (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"‏حروف الفبا (QWERTZ)"</string>
diff --git a/java/res/values-fi/strings-talkback-descriptions.xml b/java/res/values-fi/strings-talkback-descriptions.xml
index bec46ca..3ac2904 100644
--- a/java/res/values-fi/strings-talkback-descriptions.xml
+++ b/java/res/values-fi/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"Suuraakkonen I ja yläpuolinen piste"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Tuntematon symboli"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Tuntematon emoji-merkki"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Kyllästynyt naama"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Nolostunut naama"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Naama aurinkolasit silmillä"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Yllättynyt naama"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Naama ja pusuhuulet"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Kurttuotsainen naama"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Vaihtoehtoisia merkkejä on saatavilla"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Vaihtoehtoiset merkit hylättiin"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Vaihtoehtoisia ehdotuksia on saatavilla"</string>
diff --git a/java/res/values-fi/strings.xml b/java/res/values-fi/strings.xml
index da70146..d1bb592 100644
--- a/java/res/values-fi/strings.xml
+++ b/java/res/values-fi/strings.xml
@@ -27,16 +27,14 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Toista ääni näppäimiä painettaessa"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Ponnahdusikkuna painalluksella"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"Asetukset"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"Tilit ja tietosuoja"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"Ulkoasu ja asettelut"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"Monikielisyysasetukset"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"Piirtokirjoitus"</string>
     <string name="settings_screen_correction" msgid="1616818407747682955">"Tekstin korjaus"</string>
     <string name="settings_screen_advanced" msgid="7472408607625972994">"Lisäasetukset"</string>
     <string name="settings_screen_theme" msgid="2137262503543943871">"Teema"</string>
-    <!-- no translation found for enable_split_keyboard (4177264923999493614) -->
-    <skip />
+    <string name="enable_split_keyboard" msgid="4177264923999493614">"Ota jaettu näppäimistö käyttöön"</string>
     <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Käytä toista syöttötapaa"</string>
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Kielenvaihtonäppäin kattaa myös muut syöttötavat"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"Kielenvaihtonäppäin"</string>
@@ -86,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"Tallenna koskettamalla tätä"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Sanakirja saatavilla"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Näppäimistöteema"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"Vaihda tiliä"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"Tilejä ei ole valittu"</string>
+    <string name="account_selected" msgid="2846876462199625974">"Tällä hetkellä käytetään tiliä <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"OK"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"Peruuta"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"Kirjaudu ulos"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"Valitse käytettävä tili"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"englanti (Iso-Britannia)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"englanti (Yhdysvallat)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"espanja (Yhdysvallat)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hindienglanti"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"serbialainen (latinal.)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"englanti (UK) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"englanti (US) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"espanja (US) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hindienglanti (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"serbialainen (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (perinteinen)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (kyrillinen)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (latinalainen)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Ei kieltä (aakkoset)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Aakkoset (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Aakkoset (QWERTZ)"</string>
diff --git a/java/res/values-fr-rCA/strings-talkback-descriptions.xml b/java/res/values-fr-rCA/strings-talkback-descriptions.xml
index df644c0..84dcae7 100644
--- a/java/res/values-fr-rCA/strings-talkback-descriptions.xml
+++ b/java/res/values-fr-rCA/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"I majuscule, point en chef"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Symbole inconnu"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Emoji inconnu"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Ennuyé"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Embarrassé"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Lunettes de soleil"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Surpris"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Bisou"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Sourcils froncés"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Des caractères supplémentaires sont proposés"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Les caractères supplémentaires sont ignorés"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Des suggestions supplémentaires sont proposées"</string>
diff --git a/java/res/values-fr-rCA/strings.xml b/java/res/values-fr-rCA/strings.xml
index 2773c3b..9879652 100644
--- a/java/res/values-fr-rCA/strings.xml
+++ b/java/res/values-fr-rCA/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Son à chaque touche"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Agrandir les caractères"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"Préférences"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"Comptes et confidentialité"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"Apparence et dispositions"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"Options multilingues"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"Entrée gestuelle"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"Touchez ici pour enregistrer le mot dans le dictionnaire"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Dictionnaire disponible"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Thème du clavier"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"Changer de compte"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"Aucun compte sélectionné"</string>
+    <string name="account_selected" msgid="2846876462199625974">"Vous utilisez actuellement le compte <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"OK"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"Annuler"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"Déconnexion"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"Sélectionnez le compte à utiliser"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"Anglais (britannique)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"Anglais (États-Unis)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"Espagnol (États-Unis)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"Serbe (latin)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"anglais (Royaume-Uni) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"anglais (États-Unis) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"espagnol (États-Unis) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbe (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (traditionnel)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (cyrillique)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (latin)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Aucune langue (alphabet)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alphabet latin (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alphabet latin (QWERTZ)"</string>
diff --git a/java/res/values-fr/strings-talkback-descriptions.xml b/java/res/values-fr/strings-talkback-descriptions.xml
index 7ae2056..1c60129 100644
--- a/java/res/values-fr/strings-talkback-descriptions.xml
+++ b/java/res/values-fr/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"I majuscule, point en chef"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Symbole inconnu."</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Emoji inconnu."</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Ennui"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Embarras"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Lunettes de soleil"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Surpris"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Bisou"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Sourcils froncés"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Des caractères supplémentaires sont disponibles."</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Les caractères supplémentaires sont ignorés."</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Des suggestions supplémentaires sont disponibles."</string>
diff --git a/java/res/values-fr/strings.xml b/java/res/values-fr/strings.xml
index fd945fb..a93c0ef 100644
--- a/java/res/values-fr/strings.xml
+++ b/java/res/values-fr/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Son à chaque touche"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Agrandir les caractères"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"Préférences"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"Comptes et confidentialité"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"Apparence et dispositions"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"Options multilingues"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"Saisie gestuelle"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"Appuyez ici pour enregistrer."</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Dictionnaire disponible"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Thème du clavier"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"Changer de compte"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"Aucun compte sélectionné"</string>
+    <string name="account_selected" msgid="2846876462199625974">"Vous utilisez actuellement le compte <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"OK"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"Annuler"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"Déconnexion"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"Sélectionner le compte à utiliser"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"Anglais (Royaume-Uni)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"Anglais (États-Unis)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"Espagnol (États-Unis)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hindi/Anglais"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"Serbe (latin)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"anglais (Royaume-Uni) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"anglais (États-Unis) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"espagnol (États-Unis) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hindi/Anglais (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbe (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (traditionnel)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (cyrillique)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (latin)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Aucune langue (latin)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alphabet latin (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alphabet latin (QWERTZ)"</string>
diff --git a/java/res/values-hi/strings-talkback-descriptions.xml b/java/res/values-hi/strings-talkback-descriptions.xml
index aba2592..b3d876f 100644
--- a/java/res/values-hi/strings-talkback-descriptions.xml
+++ b/java/res/values-hi/strings-talkback-descriptions.xml
@@ -21,8 +21,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="spoken_use_headphones" msgid="4313642710742229868">"जोर से बोली जाने वाली पासवर्ड कुंजियां सुनने के लिए हैडसेट प्‍लग करें."</string>
-    <string name="spoken_current_text_is" msgid="4240549866156675799">"वर्तमान पाठ %s है"</string>
-    <string name="spoken_no_text_entered" msgid="1711276837961785646">"कोई पाठ नहीं डाला गया"</string>
+    <string name="spoken_current_text_is" msgid="4240549866156675799">"वर्तमान लेख %s है"</string>
+    <string name="spoken_no_text_entered" msgid="1711276837961785646">"कोई लेख नहीं डाला गया"</string>
     <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g> <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> को सुधार कर <xliff:g id="CORRECTED_WORD">%3$s</xliff:g> करता है"</string>
     <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> स्वत: सुधार करता है"</string>
     <string name="spoken_description_unknown" msgid="5139930082759824442">"अज्ञात वर्ण"</string>
@@ -44,7 +44,7 @@
     <string name="spoken_description_search" msgid="5099937658231911288">"खोजें"</string>
     <string name="spoken_description_dot" msgid="5644176501632325560">"डॉट"</string>
     <string name="spoken_description_language_switch" msgid="6818666779313544553">"भाषा स्विच करें"</string>
-    <string name="spoken_description_action_next" msgid="431761808119616962">"अगला"</string>
+    <string name="spoken_description_action_next" msgid="431761808119616962">"आगे"</string>
     <string name="spoken_description_action_previous" msgid="2919072174697865110">"पिछला"</string>
     <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"शिफ़्ट सक्षम किया गया"</string>
     <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"कैप्स लॉक सक्षम किया गया"</string>
@@ -61,7 +61,7 @@
     <string name="keyboard_mode_im" msgid="3812086215529493501">"संदेश सेवा"</string>
     <string name="keyboard_mode_number" msgid="5395042245837996809">"संख्या"</string>
     <string name="keyboard_mode_phone" msgid="2486230278064523665">"फ़ोन"</string>
-    <string name="keyboard_mode_text" msgid="9138789594969187494">"पाठ"</string>
+    <string name="keyboard_mode_text" msgid="9138789594969187494">"लेख"</string>
     <string name="keyboard_mode_time" msgid="8558297845514402675">"समय"</string>
     <string name="keyboard_mode_url" msgid="8072011652949962550">"URL"</string>
     <string name="spoken_descrption_emoji_category_recents" msgid="4185344945205590692">"हाल ही के"</string>
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"बड़ा आई, बिंदु ऊपर"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"अज्ञात प्रतीक"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"अज्ञात इमोजी"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"उदास चेहरा"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"शर्मिंदा चेहरा"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"चश्मा पहना हुआ चेहरा"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"अचंभित चेहरा"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"चूमता हुआ चेहरा"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"क्रोधित चेहरा"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"वैकल्पिक वर्ण उपलब्ध हैं"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"वैकल्पिक वर्ण ख़ारिज कर दिए जाते हैं"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"वैकल्पिक सुझाव उपलब्ध हैं"</string>
diff --git a/java/res/values-hi/strings.xml b/java/res/values-hi/strings.xml
index 3453b44..5f1ca5c 100644
--- a/java/res/values-hi/strings.xml
+++ b/java/res/values-hi/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"कुंजी दबाने पर आवाज"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"कुंजी दबाने पर पॉपअप दिखाएं"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"प्राथमिकताएं"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"खाते और गोपनीयता"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"प्रकटन और लेआउट"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"बहुभाषीय  विकल्प"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"हावभाव लेखन"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"सहेजने के लिए यहां स्पर्श करें"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"शब्‍दकोश उपलब्‍ध है"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"कीबोर्ड थीम"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"खाते स्‍विच करें"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"कोई खाता नहीं चुना गया"</string>
+    <string name="account_selected" msgid="2846876462199625974">"वर्तमान में <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g> का उपयोग कर रहा है"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"ठीक"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"रहने दें"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"प्रस्थान करें"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"उपयोग करने के लिए कोई खाता चुनें"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"अंग्रेज़ी (यूके)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"अंग्रेज़ी (यूएस)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"स्पेनिश (यूएस)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"हिंग्लिश"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"सर्बियाई (लैटिन)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"अंग्रेज़ी (यूके) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"अंग्रेज़ी (यूएस) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"स्‍पेनिश (यूएस) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"हिंग्लिश (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"सर्बियाई (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (पारंपरिक)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (सिरिलिक)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (लैटिन)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"भाषा उपलब्ध नहीं है (लैटिन वर्णाक्षर)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"वर्णाक्षर (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"वर्णाक्षर (QWERTZ)"</string>
diff --git a/java/res/values-hr/strings-talkback-descriptions.xml b/java/res/values-hr/strings-talkback-descriptions.xml
index e86db07..2a7a579 100644
--- a/java/res/values-hr/strings-talkback-descriptions.xml
+++ b/java/res/values-hr/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"Veliko slovo I, s točkicom iznad"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Nepoznati simbol"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Nepoznati emoji"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Lice s izrazom dosade"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Lice s izrazom neugode"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Lice sa sunčanim naočalama"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Iznenađeno lice"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Lice koje ljubi"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Namršteno lice"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Dostupni su zamjenski znakovi"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Zamjenski su znakovi isključeni"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Dostupni su zamjenski prijedlozi"</string>
diff --git a/java/res/values-hr/strings.xml b/java/res/values-hr/strings.xml
index 45a78ab..34449c2 100644
--- a/java/res/values-hr/strings.xml
+++ b/java/res/values-hr/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Zvuk pri pritisku tipke"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Povećanja na pritisak tipke"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"Postavke"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"Računi i privatnost"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"Prikaz i izgled"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"Višejezične opcije"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"Pisanje kretnjama"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"Dodirnite ovdje za spremanje"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Rječnik je dostupan"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Tema tipkovnice"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"Prijelaz na drugi račun"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"Nije odabran nijedan račun"</string>
+    <string name="account_selected" msgid="2846876462199625974">"Trenutačno upotrebljavate <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"U redu"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"Odustani"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"Odjava"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"Odaberite račun za upotrebu"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"Engleski (UK)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"Engleski (SAD)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"španjolski (SAD)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"Srpski (latinica)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"engleska (UK) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"engleska (SAD) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"španjolska (SAD) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Srpski (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicionalni)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ćirilica)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (latinica)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Nema jezika (abeceda)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Abeceda (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Abeceda (QWERTZ)"</string>
diff --git a/java/res/values-hu/strings-talkback-descriptions.xml b/java/res/values-hu/strings-talkback-descriptions.xml
index ae0c047..554cc97 100644
--- a/java/res/values-hu/strings-talkback-descriptions.xml
+++ b/java/res/values-hu/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"Nagy I, felette ponttal"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Ismeretlen szimbólum"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Ismeretlen hangulatjel"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Unott arc"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Zavarban lévő arc"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Napszemüveges arc"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Meglepődött arc"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Csókot adó arc"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Homlokráncoló arc"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Az alternatív karakterek elérhetők"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Az alternatív karakterek billentyűzete bezárva"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Az alternatív javaslatok elérhetők"</string>
diff --git a/java/res/values-hu/strings.xml b/java/res/values-hu/strings.xml
index 5b082d4..72b012b 100644
--- a/java/res/values-hu/strings.xml
+++ b/java/res/values-hu/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Hangjelzés gombnyomásra"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Nagyobb billentyű gombnyomásra"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"Beállítások"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"Fiókok és adatvédelem"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"Megjelenés és elrendezés"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"Többnyelvű beállítások"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"Kézmozdulatokkal történő gépelés"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"A mentéshez érintse meg itt"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Van elérhető szótár"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Billentyűzettéma"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"Fiókváltás"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"Nem választott fiókot"</string>
+    <string name="account_selected" msgid="2846876462199625974">"Jelenleg használt fiók: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"OK"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"Mégse"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"Kijelentkezés"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"Válassza ki a használni kívánt fiókot"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"angol (brit)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"angol (amerikai)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"spanyol (USA)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish (hindi-angol)"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"Szerb (latin)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"angol (UK) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"angol (USA) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"spanyol (USA) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (hindi-angol, <xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Szerb (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (hagyományos)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (cirill)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (latin)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Nincs nyelv (ábécé)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Ábécé (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Ábécé (QWERTZ)"</string>
diff --git a/java/res/values-hy-rAM/strings-talkback-descriptions.xml b/java/res/values-hy-rAM/strings-talkback-descriptions.xml
index 44784bd..63128ea 100644
--- a/java/res/values-hy-rAM/strings-talkback-descriptions.xml
+++ b/java/res/values-hy-rAM/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"Մեծատառ I, վերևում կետիկ"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Անհայտ նշան"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Անհայտ զմայլիկ"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Ձանձրացող դեմք"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Շփոթված դեմք"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Արևային ակնոցներով դեմք"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Զարմացած դեմք"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Համբուրող դեմք"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Խոժոռված դեմք"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Լրացուցիչ տառանշաններով ստեղնաշարը հասանելի է"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Լրացուցիչ տառանշաններով ստեղնաշարը փակված է"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Լրացուցիչ առաջարկներով վահանակը հասանելի է"</string>
diff --git a/java/res/values-hy-rAM/strings.xml b/java/res/values-hy-rAM/strings.xml
index ac277bb..488bcc6 100644
--- a/java/res/values-hy-rAM/strings.xml
+++ b/java/res/values-hy-rAM/strings.xml
@@ -27,16 +27,14 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Ձայնը սեղմման ժամանակ"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Ելնող պատուհան՝ ստեղնի հպման դեպքում"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"Նախընտրանքներ"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"Հաշիվներ և գաղտնիություն"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"Արտաքին տեսք և դասավորություն"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"Բազմալեզու ընտրանքներ"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"Ժեստերով մուտքագրում"</string>
     <string name="settings_screen_correction" msgid="1616818407747682955">"Տեքստի ուղղում"</string>
     <string name="settings_screen_advanced" msgid="7472408607625972994">"Հավելյալ"</string>
     <string name="settings_screen_theme" msgid="2137262503543943871">"Թեմա"</string>
-    <!-- no translation found for enable_split_keyboard (4177264923999493614) -->
-    <skip />
+    <string name="enable_split_keyboard" msgid="4177264923999493614">"Միացնել բաժանված ստեղնաշարը"</string>
     <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Անցնել մուտքագրման այլ եղանակների"</string>
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Լեզվի փոխարկման բանալին ընդգրկում է այլ մուտքագրման եղանակներ ևս"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"Լեզվի փոխարկման ստեղն"</string>
@@ -86,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"Պահելու համար հպեք այստեղ"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Բառարանն առկա է"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Ստեղնաշարի թեման"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"Փոխարկել հաշիվները"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"Հաշիվ չի ընտրվել"</string>
+    <string name="account_selected" msgid="2846876462199625974">"Ներկայումս օգտագործում է՝ <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"Լավ"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"Չեղարկել"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"Դուրս գալ"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"Ընտրեք հաշիվ՝ օգտագործելու համար"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"Անգլերեն (ՄԹ)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"Անգլերեն (ԱՄՆ)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"Իսպաներեն (ԱՄՆ)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Հինգլիշ"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"Սերբերեն (Լատինական)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"Անգլերեն (ՄԹ) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"Անգլերեն (ԱՄՆ) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"Իսպաներեն (ԱՄՆ) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Հինգլիշ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Սերբերեն (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ավանդական)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (կյուրեղյան)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (լատինական)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Ոչ մի լեզվով (Այբուբեն)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Այբուբեն (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Այբուբեն (QWERTZ)"</string>
diff --git a/java/res/values-in/strings-talkback-descriptions.xml b/java/res/values-in/strings-talkback-descriptions.xml
index f7334d9..ef31573 100644
--- a/java/res/values-in/strings-talkback-descriptions.xml
+++ b/java/res/values-in/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"Huruf kapital I dengan aksen titik di bagian atas"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Simbol tak dikenal"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Emoji tak dikenal"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Wajah bosan"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Wajah malu"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Wajah mengenakan kacamata hitam"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Wajah terkejut"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Wajah mencium"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Wajah merengut"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Karakter alternatif tersedia"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Karakter alternatif ditolak"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Saran alternatif tersedia"</string>
diff --git a/java/res/values-in/strings.xml b/java/res/values-in/strings.xml
index 8a6d47e..3b92faf 100644
--- a/java/res/values-in/strings.xml
+++ b/java/res/values-in/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Berbunyi jika tombol ditekan"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Muncul saat tombol ditekan"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"Preferensi"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"Akun &amp; privasi"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"Tampilan &amp; tata letak"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"Opsi multibahasa"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"Ketikan Isyarat"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"Sentuh di sini untuk menyimpan"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Kamus yang tersedia"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Tema keyboard"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"Beralih akun"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"Tidak ada akun yang dipilih"</string>
+    <string name="account_selected" msgid="2846876462199625974">"Saat ini menggunakan <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"Oke"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"Batal"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"Keluar"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"Pilih akun yang akan digunakan"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"Inggris (Inggris)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"Inggris (AS)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"Spanyol (AS)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"Serbia (Latin)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>) Inggris (Inggris)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>) Inggris (AS)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>) Spanyol (AS)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbia (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Tradisional)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Sirilik)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Latin)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Tidak ada bahasa (Abjad)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Abjad (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Abjad (QWERTZ)"</string>
diff --git a/java/res/values-it/strings-talkback-descriptions.xml b/java/res/values-it/strings-talkback-descriptions.xml
index 8665530..54bef08 100644
--- a/java/res/values-it/strings-talkback-descriptions.xml
+++ b/java/res/values-it/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"I maiuscola, punto sovrascritto"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Simbolo sconosciuto"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Emoji sconosciuta"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Faccina annoiata"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Faccina imbarazzata"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Faccina che indossa occhiali da sole"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Faccina sorpresa"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Faccina che dà baci"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Faccina corrucciata"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Sono disponibili caratteri alternativi"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"I caratteri alternativi vengono ignorati"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Sono disponibili suggerimenti alternativi"</string>
diff --git a/java/res/values-it/strings.xml b/java/res/values-it/strings.xml
index ff6cefc..473d712 100644
--- a/java/res/values-it/strings.xml
+++ b/java/res/values-it/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Suono tasti"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Popup sui tasti"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"Preferenze"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"Account e privacy"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"Aspetto e layout"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"Opzioni multilingue"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"Digitazione gestuale"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"Tocca qui per salvare"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Dizionario disponibile"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Tema della tastiera"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"Cambia account"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"Nessun account selezionato"</string>
+    <string name="account_selected" msgid="2846876462199625974">"Attualmente in uso: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"OK"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"Annulla"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"Esci"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"Seleziona un account da utilizzare"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"Inglese (UK)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"Inglese (USA)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"Spagnolo (USA)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"Serbo (latino)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"Inglese (Regno Unito) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"Inglese (Stati Uniti) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"Spagnolo (Stati Uniti) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbo (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradizionale)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Cirillico)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Latino)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Nessuna lingua (alfabeto)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabeto (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabeto (QWERTZ)"</string>
diff --git a/java/res/values-iw/strings-talkback-descriptions.xml b/java/res/values-iw/strings-talkback-descriptions.xml
index a43b64e..24cc1a6 100644
--- a/java/res/values-iw/strings-talkback-descriptions.xml
+++ b/java/res/values-iw/strings-talkback-descriptions.xml
@@ -57,7 +57,7 @@
     <string name="announce_keyboard_mode" msgid="6698257917367823205">"מציג מקלדת <xliff:g id="KEYBOARD_MODE">%s</xliff:g>"</string>
     <string name="keyboard_mode_date" msgid="6597407244976713364">"תאריך"</string>
     <string name="keyboard_mode_date_time" msgid="3642804408726668808">"תאריך ושעה"</string>
-    <string name="keyboard_mode_email" msgid="1239682082047693644">"דוא\"ל"</string>
+    <string name="keyboard_mode_email" msgid="1239682082047693644">"אימייל"</string>
     <string name="keyboard_mode_im" msgid="3812086215529493501">"העברת הודעות"</string>
     <string name="keyboard_mode_number" msgid="5395042245837996809">"מספרים"</string>
     <string name="keyboard_mode_phone" msgid="2486230278064523665">"מספרי טלפון"</string>
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"‏I גדולה, נקודה מעל"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"סמל לא מוכר"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"‏Emoji לא מוכר"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"פרצוף משועמם"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"פרצוף נבוך"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"פרצוף עם משקפי שמש"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"פרצוף מופתע"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"פרצוף מנשק"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"פרצוף זועף"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"תווים חלופיים זמינים"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"תווים חלופיים נדחים"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"הצעות חלופיות זמינות"</string>
diff --git a/java/res/values-iw/strings.xml b/java/res/values-iw/strings.xml
index d6e5111..317282d 100644
--- a/java/res/values-iw/strings.xml
+++ b/java/res/values-iw/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"צלילים בעת לחיצה על מקשים"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"חלון קופץ בלחיצה על מקש"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"העדפות"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"חשבונות ופרטיות"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"מראה ופריסות"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"אפשרויות לריבוי שפות"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"הקלדת החלקה"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"גע כאן כדי לשמור"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"מילון זמין"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"עיצוב מקלדת"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"החלף חשבונות"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"לא נבחרו חשבונות"</string>
+    <string name="account_selected" msgid="2846876462199625974">"משתמש כרגע ב-<xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"אישור"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"בטל"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"צא"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"בחר באיזה חשבון חשבון להשתמש"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"אנגלית (בריטניה)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"אנגלית (ארה\"ב)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"ספרדית (ארצות הברית)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"אנגלית הודית"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"סרבית (באותיות לטיניות)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"אנגלית (בריטניה)‏ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"אנגלית (ארה\"ב) ‏(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"ספרדית (ארה\"ב) ‏(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"אנגלית הודית (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"סרבית (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (מסורתית)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (קירילית)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (לטינית)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"ללא שפה (אלף-בית)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"‏אלף-בית (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"‏אלף-בית (QWERTZ)"</string>
diff --git a/java/res/values-ja/strings-talkback-descriptions.xml b/java/res/values-ja/strings-talkback-descriptions.xml
index e4f5db5..f3a23a8 100644
--- a/java/res/values-ja/strings-talkback-descriptions.xml
+++ b/java/res/values-ja/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"大文字I、上点"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"不明な記号"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"不明な絵文字"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"退屈そうな顔"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"恥ずかしそうな顔"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"サングラスをかけた顔"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"驚いた顔"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"キスしている顔"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"しかめっ面の顔"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"代替文字が利用可能です"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"代替文字が消去されます"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"代替候補が利用可能です"</string>
diff --git a/java/res/values-ja/strings.xml b/java/res/values-ja/strings.xml
index cee8c16..d3be98b 100644
--- a/java/res/values-ja/strings.xml
+++ b/java/res/values-ja/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"キー操作音"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"キー押下時ポップアップ"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"設定"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"アカウントとプライバシー"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"外観とレイアウト"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"多言語オプション"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"ジェスチャー入力"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"ここをタップして保存します"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"辞書を利用できます"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"キーボードのテーマ"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"アカウントの切り替え"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"アカウントが選択されていません"</string>
+    <string name="account_selected" msgid="2846876462199625974">"現在 <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g> を使用しています"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"OK"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"キャンセル"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"ログアウト"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"使用するアカウントの選択"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"英語 (英国)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"英語 (米国)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"スペイン語 (米国)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"ヒングリッシュ"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"セルビア語(ラテン文字)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"英語(英国)(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"英語(米国)(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"スペイン語(米国)(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"ヒングリッシュ(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"セルビア語(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g>(伝統言語)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g>(キリル文字)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g>(ラテン文字)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"言語なし(アルファベット)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"アルファベット(QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"アルファベット(QWERTZ)"</string>
diff --git a/java/res/values-ka-rGE/strings-talkback-descriptions.xml b/java/res/values-ka-rGE/strings-talkback-descriptions.xml
index 5d23b09..b0e13ee 100644
--- a/java/res/values-ka-rGE/strings-talkback-descriptions.xml
+++ b/java/res/values-ka-rGE/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"Capital I, dot above"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"უცნობი სიმბოლო"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"უცნობი emoji"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"შეწუხებული სახე"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"შერცხვენილი სახე"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"სახე მზის სათვალით"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"გაოცებული სახე"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"კოცნის სახე"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"წარბშეჭმუხნილი სახე"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"ალტერნატიული სიმბოლოები ხელმისაწვდომია"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"ალტერნატიული სიმბოლოები გამოტოვებულია"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"ალტერნატიული შეთავაზებები ხელმისაწვდომია"</string>
diff --git a/java/res/values-ka-rGE/strings.xml b/java/res/values-ka-rGE/strings.xml
index 656b320..f614ea0 100644
--- a/java/res/values-ka-rGE/strings.xml
+++ b/java/res/values-ka-rGE/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"ხმა კლავიშზე დაჭერისას"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"გადიდება ღილაკზე დაჭერისას"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"პარამეტრები"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"ანგარიშები და კონფიდენციალურობა"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"იერსახე &amp; განლაგებები"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"მრავალენობრივი ვარიანტები"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"ჟესტებით წერა"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"აქ შეეხეთ, რომ შეინახოს"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"ხელმისაწვდომია ლექსიკონი"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"კლავიატურის თემა"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"ანგარიშების გადართვა"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"ანგარიშები არჩეული არ არის"</string>
+    <string name="account_selected" msgid="2846876462199625974">"ამჟამად გამოიყენება <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"კარგი"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"გაუქმება"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"გამოსვლა"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"აირჩიეთ გამოსაყენებელი ანგარიში"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"ინგლისური (გართ. სამ.)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"ინგლისური (აშშ)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"ესპანური (აშშ)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"ჰინგლისური"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"სერბული (ლათინური)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"ინგლისური (გაერთ.სამ.) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"ინგლისური (აშშ) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"ესპანური (აშშ) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"ჰინგლისური (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"სერბული (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ტრადიციული)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (კირილიცა)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ლათინური)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"ენის გარეშე (ანბანი)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"ანბანი (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"ანბანი (QWERTZ)"</string>
diff --git a/java/res/values-km-rKH/strings-talkback-descriptions.xml b/java/res/values-km-rKH/strings-talkback-descriptions.xml
index e5b7860..f907832 100644
--- a/java/res/values-km-rKH/strings-talkback-descriptions.xml
+++ b/java/res/values-km-rKH/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"សញ្ញា İ"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"មិន​ស្គាល់​និមិត្តសញ្ញា"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"មិន​ស្គាល់​សញ្ញា​អារម្មណ៍"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"មុខ​អផ្សុក"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"មុខ​ខ្មាស​​អៀន"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"ពាក់​វ៉ែនតា​"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"មុខ​ភ្ញាក់ផ្អើល"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"មុខ​ថើប"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"មុខ​ចង​ចិញ្ចើម"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"តួអក្សរ​ជំនួស​អាច​ប្រើ​បាន"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"បាន​បដិសេធ​តួអក្សរ​ជំនួស"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"ការ​ស្នើ​ជំនួស​អាច​ប្រើ​បាន"</string>
diff --git a/java/res/values-km-rKH/strings.xml b/java/res/values-km-rKH/strings.xml
index 0188b3e..e9a29af 100644
--- a/java/res/values-km-rKH/strings.xml
+++ b/java/res/values-km-rKH/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"សំឡេង​នៅ​ពេល​ចុច​គ្រាប់ចុច"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"លេច​ឡើង​នៅ​​ពេល​ចុច​គ្រាប់​ចុច"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"ចំណូលចិត្ត"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"គណនី &amp; ភាព​ឯកជន"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"រូបរាង &amp; ប្លង់"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"ជម្រើស​ច្រើន​ភាសា"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"កាយវិការ​បញ្ចូល"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"ប៉ះ​ទីនេះ​ដើម្បី​រក្សាទុក"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"មាន​វចនានុក្រម"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"រូបរាង​ក្ដារចុច"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"ប្ដូរ​គណនី"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"មិន​បាន​ជ្រើស​គណនី"</string>
+    <string name="account_selected" msgid="2846876462199625974">"កំពុង​ប្រើ <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"យល់ព្រម"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"បោះបង់"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"ចេញ"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"ជ្រើស​គណនី​ដើម្បី​ប្រើ"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"អង់គ្លេស (​អង់គ្លេស)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"អង់គ្លេស (សហរដ្ឋ​អាមេរិក)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"អេស្ប៉ាញ (សហរដ្ឋ​អាមេរិក​)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"សើប (ឡាតាំង​)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"អង់គ្លេស (ចក្រភព​អង់គ្លេស) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"អង់គ្លេស (អាមេរិក) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"អេស្ប៉ាញ (អាមេរិក) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"សើប (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (អក្សរ​ពេញ)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ស៊ីរី)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ឡាតាំង)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"គ្មាន​ភាសា (អក្សរ​ក្រម)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"តាម​លំដាប់​អក្សរក្រម (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"តាម​លំដាប់​អក្សរក្រម (QWERTZ)"</string>
diff --git a/java/res/values-ko/strings-talkback-descriptions.xml b/java/res/values-ko/strings-talkback-descriptions.xml
index 2a3fe88..c699d2c 100644
--- a/java/res/values-ko/strings-talkback-descriptions.xml
+++ b/java/res/values-ko/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"대문자 아이, 위 닷"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"알 수 없는 기호"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"알 수 없는 그림 이모티콘"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"지루한 얼굴"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"당황한 얼굴"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"선글라스 쓴 얼굴"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"놀란 얼굴"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"키스하는 얼굴"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"찌푸린 얼굴"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"대체 문자를 사용할 수 있습니다"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"대체 문자를 닫았습니다."</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"대체 추천 단어를 사용할 수 있습니다"</string>
diff --git a/java/res/values-ko/strings.xml b/java/res/values-ko/strings.xml
index 0698b2b..c198032 100644
--- a/java/res/values-ko/strings.xml
+++ b/java/res/values-ko/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"키를 누를 때 소리 발생"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"키를 누를 때 팝업"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"환경설정"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"계정 및 개인정보 보호"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"모양 및 레이아웃"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"다국어 옵션"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"제스처 타이핑"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"저장하려면 여기를 터치하세요."</string>
     <string name="has_dictionary" msgid="6071847973466625007">"사전 사용 가능"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"키보드 테마"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"계정 전환"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"선택한 계정이 없습니다."</string>
+    <string name="account_selected" msgid="2846876462199625974">"현재 <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>을(를) 사용 중입니다."</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"확인"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"취소"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"로그아웃"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"사용할 계정 선택"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"영어(영국)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"영어(미국)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"스페인어(미국)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"인도 영어"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"세르비아어(라틴 문자)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"영어(영국)(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"영어(미국)(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"스페인어(미국)(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"인도 영어(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"세르비아어(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g>(번체)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g>(키릴어)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g>(라틴어)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"언어 없음(알파벳)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"알파벳(QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"알파벳(QWERTZ)"</string>
diff --git a/java/res/values-lo-rLA/strings-talkback-descriptions.xml b/java/res/values-lo-rLA/strings-talkback-descriptions.xml
index 187b63a..18f0c38 100644
--- a/java/res/values-lo-rLA/strings-talkback-descriptions.xml
+++ b/java/res/values-lo-rLA/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"​ໂຕພິມ​ໃຫຍ່ I ມີ​ຈ້ຳ​ເມັດ​ຢູ່​ເທິງ"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"ສັນ​ຍາ​ລັກ​ທີ່ບໍ່​ຮູ້​ຈັກ"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"ອີ​ໂມ​ຈິ​ທີ່ບໍ່​ຮູ້​ຈັກ"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"ໜ້າ​ຮູ້​ສຶກເບື່ອ"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"​ໜ້າ​ຮູ້​ສຶກ​ອາຍ"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"​ໜ້າ​ໃສ່​ແວ່ນ​ຕາ​ດຳ"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"​ໜ້າ​ຕົກ​ໃຈ"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"​ໜ້າ​ຈູບ"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"​ໜ້າ​ບູດ"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"​ມີ​ໂຕ​ອັກ​ສອນ​ສຳ​ຮອງ​ໃຫ້​ເລືອກ"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"​ປິດ​ໂຕ​ອັກ​ສອນ​ສຳ​ຮອງ​ແລ້ວ"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"​ມີ​ຄຳ​ແນະ​ນຳ​ສຳ​ຮອງ​ໃຫ້​ເລືອກ"</string>
diff --git a/java/res/values-lo-rLA/strings.xml b/java/res/values-lo-rLA/strings.xml
index f9a9171..bdc99fc 100644
--- a/java/res/values-lo-rLA/strings.xml
+++ b/java/res/values-lo-rLA/strings.xml
@@ -34,8 +34,7 @@
     <string name="settings_screen_correction" msgid="1616818407747682955">"​ການ​ແປງ​ຄຳ​ໃຫ້​ຖືກ​ຕ້ອງ"</string>
     <string name="settings_screen_advanced" msgid="7472408607625972994">"​ຂັ້ນ​ສູງ"</string>
     <string name="settings_screen_theme" msgid="2137262503543943871">"ຮູບແບບສີສັນ"</string>
-    <!-- no translation found for enable_split_keyboard (4177264923999493614) -->
-    <skip />
+    <string name="enable_split_keyboard" msgid="4177264923999493614">"ເປີດ​ນຳ​ໃຊ້​ແປ້ນ​ພິມ​ແຍກ​ຕົວ"</string>
     <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"ປ່ຽນໄປໃຊ້ການປ້ອນຂໍ້ມູນແບບອື່ນ"</string>
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"ໂຕປ່ຽນພາສາເປັນທັງໂຕປ່ຽນຮູບແບບການປ້ອນຂໍ້ມູນເຊັ່ນກັນ"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"ປຸ່ມປ່ຽນພາສາ"</string>
@@ -96,13 +95,13 @@
     <string name="subtype_en_US" msgid="6160452336634534239">"ອັງກິດ (ສະຫະລັດຯ)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"ສະເປນ (ອາເມລິກາ)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"ຮິງ​ລິສ"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"ເຊີ​ບຽນ (​ລາ​ຕິນ)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"ອັງ​ກິດ (ສະ​ຫະ​ລາດ​ຊະ​ອາ​ນາ​ຈັກ) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"ອັງ​ກິດ (ສະ​ຫະ​ລັດຯ) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"ສະ​ແປນ​ນິດ (ສະ​ຫະ​ລັດຯ) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"ຮິງ​ລິສ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"ເຊີ​ບຽນ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ດັ້ງ​ເດີມ)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ຊິ​ຣິວ​ລິກ)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ລາ​ຕິນ)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"ບໍ່ມີພາສາ (ໂຕອັກສອນ)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"ໂຕອັກສອນ (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"ໂຕອັກສອນ (QWERTZ)"</string>
diff --git a/java/res/values-lt/strings-talkback-descriptions.xml b/java/res/values-lt/strings-talkback-descriptions.xml
index 18b09dd..6e760d1 100644
--- a/java/res/values-lt/strings-talkback-descriptions.xml
+++ b/java/res/values-lt/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"Didžioji I su tašku viršuje"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Nežinomas simbolis"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Nežinomas jaustukas"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Nuobodžiaujantis veidas"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Susigėdęs veidas"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Veidas su akiniais nuo saulės"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Nustebęs veidas"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Bučiuojantis veidas"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Paniuręs veidas"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Alternatyvūs ženklai pasiekiami"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Alternatyvūs simboliai atmetami"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Alternatyvūs pasiūlymai pasiekiami"</string>
diff --git a/java/res/values-lt/strings.xml b/java/res/values-lt/strings.xml
index c8a212d..147d706 100644
--- a/java/res/values-lt/strings.xml
+++ b/java/res/values-lt/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Klavišo paspaudimo garsas"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Iššoka paspaudus klavišą"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"Nuostatos"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"Paskyros ir privatumas"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"Išvaizda ir išdėstymai"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"Kelių kalbų parinktys"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"Įvestis gestais"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"Palieskite čia, kad išsaugotumėte"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Žodynas galimas"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Klaviatūros tema"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"Perjungti paskyras"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"Nepasirinkta jokia paskyra"</string>
+    <string name="account_selected" msgid="2846876462199625974">"Šiuo metu naudojama <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"Gerai"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"Atšaukti"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"Atsijungti"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"Pasirinkite norimą naudoti paskyrą"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"Anglų k. (JK)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"Anglų k. (JAV)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"Ispanų k. (JAV)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hindi ir anglų k. derinys"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"Serbų k. (lot. rašmenys)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"Anglų (JK) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"Anglų (JAV) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"Ispanų (JAV) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hindi ir anglų derinys (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbų k. (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicinė)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (kirilica)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (lotynų)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Kalbos nėra (abėcėlė)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Abėcėlė (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Abėcėlė (QWERTZ)"</string>
diff --git a/java/res/values-lv/strings-talkback-descriptions.xml b/java/res/values-lv/strings-talkback-descriptions.xml
index 2fe7f91..e89ffa4 100644
--- a/java/res/values-lv/strings-talkback-descriptions.xml
+++ b/java/res/values-lv/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"Lielais burts “I” ar punktu virs tā"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Nezināms simbols"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Nezināma emocijzīme"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Garlaikots"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Apmulsis"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Ar saulesbrillēm"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Pārsteigts"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Skūpstu sniedzošs"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Drūms"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Ir pieejamas alternatīvas rakstzīmes."</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Alternatīvās rakstzīmes netiek rādītas."</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Ir pieejami alternatīvi ieteikumi."</string>
diff --git a/java/res/values-lv/strings.xml b/java/res/values-lv/strings.xml
index ecb7270..812cb9a 100644
--- a/java/res/values-lv/strings.xml
+++ b/java/res/values-lv/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Skaņa, nospiežot taustiņu"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Nospiežot taustiņu, parādīt uznirstošo izvēlni"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"Preferences"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"Konti un konfidencialitāte"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"Izskats un izkārtojumi"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"Vairākvalodu opcijas"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"Ievade ar žestiem"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"Lai saglabātu, pieskarieties šeit"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Ir pieejama vārdnīca."</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Tastatūras motīvs"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"Pārslēgt kontus"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"Nav atlasīts neviens konts."</string>
+    <string name="account_selected" msgid="2846876462199625974">"Pašlaik tiek izmantots konts <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>."</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"Labi"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"Atcelt"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"Izrakstīties"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"Izmantojamā konta atlasīšana"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"Angļu valoda (Lielbritānija)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"Angļu valoda (ASV)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"Spāņu (ASV)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hindi–angļu valoda"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"Serbu (latīņu)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"Angļu (Lielbritānija) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"Angļu (ASV) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"Spāņu (ASV) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hindi–angļu valoda (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbu (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicionālā)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (kirilica)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (latīņu)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Nav valodas (alfabēts)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabēts (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabēts (QWERTZ)"</string>
diff --git a/java/res/values-mn-rMN/strings-talkback-descriptions.xml b/java/res/values-mn-rMN/strings-talkback-descriptions.xml
index d2c583e..e943a8d 100644
--- a/java/res/values-mn-rMN/strings-talkback-descriptions.xml
+++ b/java/res/values-mn-rMN/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"Том I, дээрээ цэгтэй"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Үл мэдэгдэх симбол"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Үл мэдэгдэх эможи"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Уйтгартай царай"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Ичсэн царай"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Нарны шил зүүсэн царай"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Гайхсан царай"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Үнсэж буй царай"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Хөмсөг зангидсан царай"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Бусад тэмдэгтүүд ашиглах боломжтой"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Бусад тэмдэгтүүдийг хаагдсан"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Бусад санал болголтууд ашиглах боломжтой"</string>
diff --git a/java/res/values-mn-rMN/strings.xml b/java/res/values-mn-rMN/strings.xml
index 5dcf1f2..e61584b 100644
--- a/java/res/values-mn-rMN/strings.xml
+++ b/java/res/values-mn-rMN/strings.xml
@@ -95,13 +95,13 @@
     <string name="subtype_en_US" msgid="6160452336634534239">"Англи (АНУ)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"Испани (АНУ)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Хинглиш"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"Серьби хэл (латин)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"Англи (ИБ) ( <xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g> )"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"Англи (АНУ) ( <xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g> )"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"Испани (АНУ-ын) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Хинглиш (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Серьби хэл (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (уламжлалт)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Кирилл)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Латин)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Хэл байхгүй (Цагаан толгой)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Цагаан толгой (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Цагаан толгой (QWERTZ)"</string>
diff --git a/java/res/values-ms-rMY/strings-talkback-descriptions.xml b/java/res/values-ms-rMY/strings-talkback-descriptions.xml
index d5ae637..b09d3a7 100644
--- a/java/res/values-ms-rMY/strings-talkback-descriptions.xml
+++ b/java/res/values-ms-rMY/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"I huruf besar, titik di atas"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Simbol yang tidak diketahui"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Emoji yang tidak dikethui"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Muka bosan"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Muka malu"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Muka memakai cermin mata hitam"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Muka terkejut"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Muka bercium"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Muka masam"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Aksara alternatif adalah tersedia"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Aksara alternatif diketepikan"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Cadangan alternatif tersedia"</string>
diff --git a/java/res/values-ms-rMY/strings.xml b/java/res/values-ms-rMY/strings.xml
index 26d9d8c..07d86c5 100644
--- a/java/res/values-ms-rMY/strings.xml
+++ b/java/res/values-ms-rMY/strings.xml
@@ -27,16 +27,14 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Bunyi pada tekanan kekunci"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Pop timbul pada tekanan kunci"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"Pilihan"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"Akaun &amp; privasi"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"Rupa &amp; reka letak"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"Pilihan berbilang bahasa"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"Taipan Gerak Isyarat"</string>
     <string name="settings_screen_correction" msgid="1616818407747682955">"Pembetulan teks"</string>
     <string name="settings_screen_advanced" msgid="7472408607625972994">"Lanjutan"</string>
     <string name="settings_screen_theme" msgid="2137262503543943871">"Tema"</string>
-    <!-- no translation found for enable_split_keyboard (4177264923999493614) -->
-    <skip />
+    <string name="enable_split_keyboard" msgid="4177264923999493614">"Dayakan papan kekunci terpisah"</string>
     <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Tukar ke kaedah input lain"</string>
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Kunci pertukaran bahasa meliputi kaedah masukan lain juga"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"Kekunci tukar bahasa"</string>
@@ -86,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"Sentuh di sini untuk menyimpan"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Kamus tersedia"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Tema papan kekunci"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"Beralih akaun"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"Tiada akaun dipilih"</string>
+    <string name="account_selected" msgid="2846876462199625974">"Pada masa ini menggunakan <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"OK"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"Batal"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"Log keluar"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"Pilih akaun untuk digunakan"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"Bahasa Inggeris (UK)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"Bahasa Inggeris (Australia)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"Bahasa Sepanyol (AS)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"Bahasa Serbia (Latin)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"Bahasa Inggeris (UK) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"Bahasa Inggeris (AS) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"Bahasa Sepanyol (AS) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Bahasa Serbia (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Tradisional)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Cyril)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Latin)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Tiada bahasa (Abjad)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Abjad (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Abjad (QWERTZ)"</string>
diff --git a/java/res/values-nb/strings-talkback-descriptions.xml b/java/res/values-nb/strings-talkback-descriptions.xml
index cdb6858..5bf0e76 100644
--- a/java/res/values-nb/strings-talkback-descriptions.xml
+++ b/java/res/values-nb/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"Stor I med prikk over"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Ukjent symbol"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Ukjent emoji"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Uinteressert fjes"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Flaut fjes"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Fjes med solbriller"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Overrasket fjes"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Kyssende fjes"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Fjes med rynket panne"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Alternative tegn er tilgjengelige"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Alternative tegn fjernes"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Alternative forslag er tilgjengelige"</string>
diff --git a/java/res/values-nb/strings.xml b/java/res/values-nb/strings.xml
index d309126..268d76a 100644
--- a/java/res/values-nb/strings.xml
+++ b/java/res/values-nb/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Lyd ved tastetrykk"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Hurtigvindu ved tastetrykk"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"Innstillinger"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"Kontoer og personvern"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"Utseende og utforming"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"Flerspråksalternativer"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"Ordføring"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"Trykk her for å lagre"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Ordbok tilgjengelig"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Tastaturtema"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"Bytt konto"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"Ingen kontoer er valgt"</string>
+    <string name="account_selected" msgid="2846876462199625974">"Du bruker for øyeblikket <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"OK"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"Avbryt"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"Logg av"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"Velg kontoen du vil bruke"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"Engelsk (Storbritannia)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"Engelsk (USA)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"Spansk (USA)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"Serbisk (latin)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"Engelsk (Storbritannia) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"Engelsk (USA) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"Spansk (USA) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbisk (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradisjonelt)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (kyrillisk)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (latin)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Ingen språk (alfabet)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabet (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabet (QWERTZ)"</string>
diff --git a/java/res/values-ne-rNP/strings-talkback-descriptions.xml b/java/res/values-ne-rNP/strings-talkback-descriptions.xml
index 2bbd178..de60128 100644
--- a/java/res/values-ne-rNP/strings-talkback-descriptions.xml
+++ b/java/res/values-ne-rNP/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"Capital I, dot above"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"अज्ञात प्रतीक"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"अज्ञात इमोजी"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"थकित अनुहार"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"लज्जित अनुहार"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"घाम चस्मा लगाएको अनुहार"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"छक्क परेको अनुहार"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"चुम्बन अनुहार"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"पालिस लगाएको अनुहार"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"वैकल्पिक वर्णहरू उपलब्ध छन्"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"वैकल्पिक वर्णहरू खारेज गरियो"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"वैकल्पिक सुझावहरू उपलब्ध छन्"</string>
diff --git a/java/res/values-ne-rNP/strings.xml b/java/res/values-ne-rNP/strings.xml
index bb90608..29f821a 100644
--- a/java/res/values-ne-rNP/strings.xml
+++ b/java/res/values-ne-rNP/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"कुञ्जी थिच्दा आवाज"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"कुञ्जी दबाउँदा पपअप"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"प्राथमिकताहरू"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"खाता र गोपनीयता"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"देखिने; रूपरेखा"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"बहुभाषी विकल्पहरू"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"इशारा टाइपिङ"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"सुरक्षित गर्न यहाँ छुनुहोस्"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"उपलब्ध शब्दकोश"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"किबोर्ड थिम"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"खाताहरू स्विच गर्नुहोस्"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"कुनै खाता चयन गरिएको छैन"</string>
+    <string name="account_selected" msgid="2846876462199625974">"हाल प्रयोग गर्दै <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"ठीक छ"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"रद्द गर्नुहोस्"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"बाहिर निकल्नुहोस्"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"प्रयोग गर्न खाता चयन गर्नुहोस्"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"अंग्रेजी (युके)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"अंग्रेजी (युएस्)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"स्पेनिस (युएस्)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"हिङ्लिस"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"सर्बियाई (ल्याटिन)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"अंग्रेजी (बेलायत) ( <xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g> )"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"अंग्रेजी (अमेरिका) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"स्पेनेली (अमेरिका) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"हिङ्लिस (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"सर्बियाई (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (परम्परागत)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (सिरिलिक)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ल्याटिन)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"कुनै भाषा होइन (वर्णमाला)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"वर्णमाला (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"वर्णमाला (QWERTZ)"</string>
diff --git a/java/res/values-nl/strings-talkback-descriptions.xml b/java/res/values-nl/strings-talkback-descriptions.xml
index c59f3f2..09929f0 100644
--- a/java/res/values-nl/strings-talkback-descriptions.xml
+++ b/java/res/values-nl/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"Hoofdletter I, punt erboven"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Onbekend symbool"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Onbekende emoji"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Verveeld gezicht"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Beschaamd gezicht"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Gezicht met zonnebril"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Verbaasd gezicht"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Kussend gezicht"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Fronsend gezicht"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Alternatieve tekens zijn beschikbaar"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Alternatieve tekens worden verwijderd"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Alternatieve suggesties zijn beschikbaar"</string>
diff --git a/java/res/values-nl/strings.xml b/java/res/values-nl/strings.xml
index 26b1ff3..59b3a29 100644
--- a/java/res/values-nl/strings.xml
+++ b/java/res/values-nl/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Geluid bij toetsaanslag"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Pop-up bij toetsaanslag"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"Voorkeuren"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"Accounts en privacy"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"Uiterlijk en indelingen"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"Meertalige opties"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"Invoer met bewegingen"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"Tik hier om op te slaan"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Woordenboek beschikbaar"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Toetsenbordthema"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"Schakelen tussen accounts"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"Geen accounts geselecteerd"</string>
+    <string name="account_selected" msgid="2846876462199625974">"<xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g> wordt momenteel gebruikt"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"OK"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"Annuleren"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"Uitloggen"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"Selecteer een account om te gebruiken"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"Engels (GB)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"Engels (VS)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"Spaans (VS)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hindi-Engels"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"Servisch (Latijns)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"Engels (VK) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"Engels (VS) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"Spaans (VS) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hindi-Engels (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Servisch (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (traditioneel)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Cyrillisch)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Latijns)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Geen taal (alfabet)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabet (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabet (QWERTZ)"</string>
diff --git a/java/res/values-pl/strings-talkback-descriptions.xml b/java/res/values-pl/strings-talkback-descriptions.xml
index 43fca5f..ac30199 100644
--- a/java/res/values-pl/strings-talkback-descriptions.xml
+++ b/java/res/values-pl/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"Wielka litera I z kropką"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Nieznany symbol"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Nieznany emotikon"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Buźka znudzona"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Buźka zawstydzona"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Buźka w ciemnych okularach"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Buźka zdziwiona"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Buźka całująca"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Buźka marszcząca czoło"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Alternatywne znaki są dostępne"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Alternatywne znaki zostały zamknięte"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Alternatywne propozycje są dostępne"</string>
diff --git a/java/res/values-pl/strings.xml b/java/res/values-pl/strings.xml
index 29e255e..0bc3d4b 100644
--- a/java/res/values-pl/strings.xml
+++ b/java/res/values-pl/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Dźwięk przy naciśnięciu"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Powiększ po naciśnięciu"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"Ustawienia"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"Konta i prywatność"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"Wygląd i układy"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"Opcje obsługi wielu języków"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"Pisanie gestami"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"Kliknij tutaj, by zapisać"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Słownik dostępny"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Motyw klawiatury"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"Przełącz konta"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"Nie wybrałeś żadnych kont"</string>
+    <string name="account_selected" msgid="2846876462199625974">"Teraz używasz konta <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"OK"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"Anuluj"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"Wyloguj się"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"Wybierz konto, którego chcesz używać"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"angielski (Wielka Brytania)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"angielski (Stany Zjednoczone)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"hiszpański (USA)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"serbski (alfabet łaciński)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"Angielski (Wielka Brytania) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"Angielski (USA) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"Hiszpański (USA) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"serbski (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradycyjny)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (cyrylica)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (alfabet łaciński)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Bez języka (alfabet)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabet (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabet (QWERTZ)"</string>
diff --git a/java/res/values-pt-rPT/strings-talkback-descriptions.xml b/java/res/values-pt-rPT/strings-talkback-descriptions.xml
index 30bbd3f..49a5fdc 100644
--- a/java/res/values-pt-rPT/strings-talkback-descriptions.xml
+++ b/java/res/values-pt-rPT/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"I maiúsculo, ponto sobreposto"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Símbolo desconhecido"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Emoji desconhecido"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Cara aborrecida"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Cara envergonhada"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Cara com óculos de sol"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Cara surpreendida"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Cara a dar um beijo"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Cara franzida"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Os carateres alternativos estão disponíveis"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Os carateres alternativos são ignorados"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"As sugestões alternativas estão disponíveis"</string>
diff --git a/java/res/values-pt-rPT/strings.xml b/java/res/values-pt-rPT/strings.xml
index bf180bf..2c7ff8b 100644
--- a/java/res/values-pt-rPT/strings.xml
+++ b/java/res/values-pt-rPT/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Som ao premir as teclas"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Mostrar popup ao premir tecla"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"Preferências"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"Contas e privacidade"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"Aspeto e esquemas"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"Opções para vários idiomas"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"Escrita com gestos"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"Toque aqui para guardar"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Dicionário disponível"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Tema do teclado"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"Alternar entre contas"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"Nenhuma conta selecionada"</string>
+    <string name="account_selected" msgid="2846876462199625974">"A utilizar atualmente <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"OK"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"Cancelar"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"Terminar sessão"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"Selecionar uma conta a utilizar"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"Inglês (RU)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"Inglês (EUA)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"Espanhol (EUA)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"Sérvio (latim)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"Inglês (RU) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"Inglês (EUA) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"Espanhol (EUA) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Sérvio (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicional)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Cirílico)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Latim)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Sem idioma (alfabeto)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabeto (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabeto (QWERTZ)"</string>
diff --git a/java/res/values-pt/strings-talkback-descriptions.xml b/java/res/values-pt/strings-talkback-descriptions.xml
index f4e28fc..608e1b4 100644
--- a/java/res/values-pt/strings-talkback-descriptions.xml
+++ b/java/res/values-pt/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"I maiúscula, ponto acima"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Símbolo desconhecido"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Emoji desconhecido"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Rosto entediado"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Rosto envergonhado"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Rosto com óculos"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Rosto de surpresa"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Rosto beijando"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Rosto franzido"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Caracteres alternativos estão disponíveis"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Caracteres alternativos foram descartados"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Sugestões alternativas estão disponíveis"</string>
diff --git a/java/res/values-pt/strings.xml b/java/res/values-pt/strings.xml
index d3ea70b..f740722 100644
--- a/java/res/values-pt/strings.xml
+++ b/java/res/values-pt/strings.xml
@@ -27,16 +27,14 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Som ao tocar a tecla"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Exibir pop-up ao digitar"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"Preferências"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"Contas e privacidade"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"Aparência e layouts"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"Opções multilíngues"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"Escrita com gestos"</string>
     <string name="settings_screen_correction" msgid="1616818407747682955">"Correção de texto"</string>
     <string name="settings_screen_advanced" msgid="7472408607625972994">"Avançadas"</string>
     <string name="settings_screen_theme" msgid="2137262503543943871">"Tema"</string>
-    <!-- no translation found for enable_split_keyboard (4177264923999493614) -->
-    <skip />
+    <string name="enable_split_keyboard" msgid="4177264923999493614">"Ativar teclado dividido"</string>
     <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Outros métodos de entrada"</string>
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"A tecla p/ mudar o idioma também cobre outros métodos de entrada"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"Tecla de seleção de idioma"</string>
@@ -86,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"Toque aqui para salvar"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Dicionário disponível"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Tema do teclado"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"Alternar contas"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"Nenhuma conta selecionada"</string>
+    <string name="account_selected" msgid="2846876462199625974">"Atualmente usando <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"OK"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"Cancelar"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"Sair"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"Selecionar uma conta para usar"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"inglês (Reino Unido)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"inglês (EUA)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"espanhol (EUA)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Híndi-inglês"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"Sérvio (Latino)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"Inglês (Reino Unido) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"Inglês (EUA) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"Espanhol (EUA) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Híndi-inglês (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Sérvio (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicional)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (cirílico)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (latino)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Nenhum idioma (alfabeto)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabeto (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabeto (QWERTZ)"</string>
@@ -162,7 +153,7 @@
     <string name="user_dictionaries" msgid="3582332055892252845">"Dicionários do usuário"</string>
     <string name="default_user_dict_pref_name" msgid="1625055720489280530">"Dicionário do usuário"</string>
     <string name="dictionary_available" msgid="4728975345815214218">"Dicionário disponível"</string>
-    <string name="dictionary_downloading" msgid="2982650524622620983">"Download em andamento"</string>
+    <string name="dictionary_downloading" msgid="2982650524622620983">"Fazendo download..."</string>
     <string name="dictionary_installed" msgid="8081558343559342962">"Instalado"</string>
     <string name="dictionary_disabled" msgid="8950383219564621762">"Instalado, desativado"</string>
     <string name="cannot_connect_to_dict_service" msgid="9216933695765732398">"Prob. conexão dic."</string>
@@ -182,7 +173,7 @@
     <string name="do_not_download_over_metered" msgid="2176209579313941583">"Fazer o download por Wi-Fi"</string>
     <string name="dict_available_notification_title" msgid="4583842811218581658">"Há um dicionário disponível para <xliff:g id="LANGUAGE_NAME">%1$s</xliff:g>"</string>
     <string name="dict_available_notification_description" msgid="1075194169443163487">"Pressione para consultar e fazer o download"</string>
-    <string name="toast_downloading_suggestions" msgid="6128155879830851739">"Download em andamento: as sugestões para <xliff:g id="LANGUAGE_NAME">%1$s</xliff:g> estarão disponíveis em breve."</string>
+    <string name="toast_downloading_suggestions" msgid="6128155879830851739">"Fazendo download...: as sugestões para <xliff:g id="LANGUAGE_NAME">%1$s</xliff:g> estarão disponíveis em breve."</string>
     <string name="version_text" msgid="2715354215568469385">"Versão <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
     <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Adicionar"</string>
     <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Adicionar ao dicionário"</string>
diff --git a/java/res/values-ro/strings-talkback-descriptions.xml b/java/res/values-ro/strings-talkback-descriptions.xml
index 7a90162..5151c52 100644
--- a/java/res/values-ro/strings-talkback-descriptions.xml
+++ b/java/res/values-ro/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"I mare, punct deasupra"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Simbol necunoscut"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Emoji necunoscut"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Față plictisită"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Față jenată"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Față cu ochelari de soare"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Față surprinsă"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Față care sărută"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Față încruntată"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Sunt disponibile caractere alternative"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"S-au închis caracterele alternative"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Sunt disponibile sugestii alternative"</string>
diff --git a/java/res/values-ro/strings.xml b/java/res/values-ro/strings.xml
index 9eaf0f5..a06dcca 100644
--- a/java/res/values-ro/strings.xml
+++ b/java/res/values-ro/strings.xml
@@ -27,16 +27,14 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Sunet la apăsarea tastei"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Pop-up la apăsarea tastei"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"Preferinţe"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"Conturi și confidențialitate"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"Aspect"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"Opțiuni multilingve"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"Tastare gestuală"</string>
     <string name="settings_screen_correction" msgid="1616818407747682955">"Corectare text"</string>
     <string name="settings_screen_advanced" msgid="7472408607625972994">"Setări avansate"</string>
     <string name="settings_screen_theme" msgid="2137262503543943871">"Temă"</string>
-    <!-- no translation found for enable_split_keyboard (4177264923999493614) -->
-    <skip />
+    <string name="enable_split_keyboard" msgid="4177264923999493614">"Activați tastatura divizată"</string>
     <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Comut. alte metode de introd."</string>
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Tasta de comutare între limbi include şi alte metode de introd."</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"Tastă comutare limbi"</string>
@@ -86,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"Atingeți aici pentru a salva"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Dicţionar disponibil"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Temă pentru tastatură"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"Comutați între conturi"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"Niciun cont selectat"</string>
+    <string name="account_selected" msgid="2846876462199625974">"Acum utilizați <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"OK"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"Anulați"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"Deconectați-vă"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"Selectați un cont de utilizat"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"engleză (Regatul Unit)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"engleză (S.U.A.)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"spaniolă (S.U.A.)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"Sârbă (caractere latine)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"Engleză (Regatul Unit) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"Engleză (S.U.A.) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"Spaniolă (S.U.A.) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Sârbă (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradițională)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Chirilică)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Latină)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Nicio limbă (alfabet)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabet (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabet (QWERTZ)"</string>
diff --git a/java/res/values-ru/strings-talkback-descriptions.xml b/java/res/values-ru/strings-talkback-descriptions.xml
index b23d40d..16ff2c9 100644
--- a/java/res/values-ru/strings-talkback-descriptions.xml
+++ b/java/res/values-ru/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"Заглавная латинская I с точкой сверху."</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Неизвестный символ."</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Неизвестный смайлик."</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Скука."</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Смущение."</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Солнцезащитные очки."</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Удивление."</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Поцелуй."</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Хмурый взгляд."</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Доступны дополнительные символы."</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Дополнительные символы скрыты."</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Доступны дополнительные подсказки."</string>
diff --git a/java/res/values-ru/strings.xml b/java/res/values-ru/strings.xml
index 23e84b1..15ddb5e 100644
--- a/java/res/values-ru/strings.xml
+++ b/java/res/values-ru/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Звук клавиш"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Увеличение нажатых"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"Настройки"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"Аккаунты"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"Вид и раскладки"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"Дополнительные языки"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"Непрерывный ввод"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"Нажмите, чтобы сохранить"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Доступен словарь"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Тема клавиатуры"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"Сменить аккаунт"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"Аккаунт не выбран"</string>
+    <string name="account_selected" msgid="2846876462199625974">"Используется аккаунт <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"ОК"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"Отмена"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"Выйти"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"Выберите аккаунт"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"английский (Великобритания)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"английский (США)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"Испанский (США)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Хинглиш"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"Сербский (латиница)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"Английский (Великобритания, <xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"Английский (США, <xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"Испанский (США, <xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Хинглиш (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Сербский (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (классическая)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (кириллица)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (латиница)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Язык не определен (латиница)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Латиница (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Латиница (QWERTZ)"</string>
diff --git a/java/res/values-sk/strings-talkback-descriptions.xml b/java/res/values-sk/strings-talkback-descriptions.xml
index 2cd094e..6a4067f 100644
--- a/java/res/values-sk/strings-talkback-descriptions.xml
+++ b/java/res/values-sk/strings-talkback-descriptions.xml
@@ -63,7 +63,7 @@
     <string name="keyboard_mode_phone" msgid="2486230278064523665">"telefón"</string>
     <string name="keyboard_mode_text" msgid="9138789594969187494">"text"</string>
     <string name="keyboard_mode_time" msgid="8558297845514402675">"čas"</string>
-    <string name="keyboard_mode_url" msgid="8072011652949962550">"Adresa URL"</string>
+    <string name="keyboard_mode_url" msgid="8072011652949962550">"Webová adresa"</string>
     <string name="spoken_descrption_emoji_category_recents" msgid="4185344945205590692">"Nedávne"</string>
     <string name="spoken_descrption_emoji_category_people" msgid="8414196269847492817">"Ľudia"</string>
     <string name="spoken_descrption_emoji_category_objects" msgid="6116297906606195278">"Predmety"</string>
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"Veľké I s bodkou nad písmenom"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Neznámy symbol"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Neznámy symbol Emodži"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Znudená tvár"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Rozpačitá tvár"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Tvár so slnečnými okuliarmi"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Prekvapená tvár"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Bozkávajúca sa tvár"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Zamračená tvár"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Alternatívne znaky sú dostupné"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Alternatívne znaky boli zrušené"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Alternatívne návrhy sú dostupné"</string>
diff --git a/java/res/values-sk/strings.xml b/java/res/values-sk/strings.xml
index 77e718c..2f428ae 100644
--- a/java/res/values-sk/strings.xml
+++ b/java/res/values-sk/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Zvuk pri stlačení klávesu"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Zobraziť znaky pri stlačení klávesu"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"Predvoľby"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"Účty a ochrana osobných údajov"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"Vzhľad a rozloženia"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"Možnosti viacerých jazykov"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"Písanie gestami"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"Klepnutím tu uložíte"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"K dispozícii je slovník"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Motív klávesnice"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"Prepnúť účty"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"Nie sú vybrané žiadne účty"</string>
+    <string name="account_selected" msgid="2846876462199625974">"Momentálne sa používa účet <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"OK"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"Zrušiť"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"Odhlásiť sa"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"Výber účtu, ktorý chcete použiť"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"Anglická klávesnica (UK)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"Anglická klávesnica (US)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"španielčina (USA)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"srbčina (latinka)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"angličtina (UK) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"angličtina (USA) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"španielčina (USA) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"srbčina (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradičná)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (cyrilika)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (latinka)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Žiadny jazyk (latinka)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Latinka (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Latinka (QWERTZ)"</string>
diff --git a/java/res/values-sl/strings-talkback-descriptions.xml b/java/res/values-sl/strings-talkback-descriptions.xml
index baef01f..9390b33 100644
--- a/java/res/values-sl/strings-talkback-descriptions.xml
+++ b/java/res/values-sl/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"Velika črka I s piko zgoraj"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Neznan simbol"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Neznan znak »emoji«"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Zdolgočasen obraz"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Obraz v zadregi"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Obraz s sončnimi očali"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Presenečen obraz"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Obraz našobljenih ustnic za poljub"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Namrščen obraz"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Nadomestni znaki so na voljo"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Nadomestni znaki se ne upoštevajo"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Nadomestni predlogi so na voljo"</string>
diff --git a/java/res/values-sl/strings.xml b/java/res/values-sl/strings.xml
index d44fd0a..57d5d72 100644
--- a/java/res/values-sl/strings.xml
+++ b/java/res/values-sl/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Zvok ob pritisku tipke"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Povečaj črko ob pritisku"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"Nastavitve"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"Računi in zasebnost"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"Videz in postavitve"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"Večjezikovne možnosti"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"Pisanje s kretnjami"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"Če želite shraniti, se dotaknite tukaj"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Slovar je na voljo"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Tema tipkovnice"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"Preklop med računi"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"Ni izbranih računov"</string>
+    <string name="account_selected" msgid="2846876462199625974">"Trenutno uporabljate račun <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"V redu"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"Prekliči"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"Odjava"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"Izbira računa za uporabo"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"angleščina (Združeno kraljestvo)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"angleščina (ZDA)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"španščina (ZDA)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hindujska angleščina"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"Srbščina (latinica)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"angleščina (VB) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"angleščina (ZDA) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"španščina (ZDA) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hindujska angleščina (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Srbščina (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicionalna)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (cirilica)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (latinica)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Brez jezika (latinice)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Latinica (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Latinica (QWERTZ)"</string>
diff --git a/java/res/values-sr/strings-talkback-descriptions.xml b/java/res/values-sr/strings-talkback-descriptions.xml
index 974ad3a..89f1d63 100644
--- a/java/res/values-sr/strings-talkback-descriptions.xml
+++ b/java/res/values-sr/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"Велико И са тачком изнад"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Непознат симбол"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Непознати емоџи"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Лице коме је досадно"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Постиђено лице"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Лице са наочарима за сунце"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Изненађено лице"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Лице које љуби"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Намрштено лице"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Доступни су алтернативни знаци"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Алтернативни знаци су одбачени"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Доступни су алтернативни предлози"</string>
diff --git a/java/res/values-sr/strings.xml b/java/res/values-sr/strings.xml
index 1e86769..98ece20 100644
--- a/java/res/values-sr/strings.xml
+++ b/java/res/values-sr/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Звук на притисак тастера"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Искачући прозор приликом притиска тастера"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"Подешавања"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"Налози и приватност"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"Изглед и распореди"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"Опције за више језика"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"Куцање покретима"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"Додирните овде да бисте сачували"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Речник је доступан"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Тема тастатуре"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"Промените налог"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"Није изабран ниједан налог"</string>
+    <string name="account_selected" msgid="2846876462199625974">"Тренутно користите <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"Потврди"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"Откажи"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"Одјави ме"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"Изаберите налог који ћете користити"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"енглески (УК)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"енглески (САД)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"шпански (САД)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"хенглески"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"српски (латиница)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"енглески (УК) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"енглески (САД) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"шпански (САД) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"хенглески (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"српски (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (традиционални)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ћирилица)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (латиница)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Нема језика (абецеда)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Абецеда (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Абецеда (QWERTZ)"</string>
@@ -168,7 +160,7 @@
     <string name="no_dictionaries_available" msgid="8039920716566132611">"Нема доступних речника"</string>
     <string name="check_for_updates_now" msgid="8087688440916388581">"Освежи"</string>
     <string name="last_update" msgid="730467549913588780">"Последње ажурирање"</string>
-    <string name="message_updating" msgid="4457761393932375219">"Тражење ажурирања"</string>
+    <string name="message_updating" msgid="4457761393932375219">"Траже се ажурирања"</string>
     <string name="message_loading" msgid="5638680861387748936">"Учитавање…"</string>
     <string name="main_dict_description" msgid="3072821352793492143">"Главни речник"</string>
     <string name="cancel" msgid="6830980399865683324">"Откажи"</string>
diff --git a/java/res/values-sv/strings-talkback-descriptions.xml b/java/res/values-sv/strings-talkback-descriptions.xml
index e3f7ca1..058dd7e 100644
--- a/java/res/values-sv/strings-talkback-descriptions.xml
+++ b/java/res/values-sv/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"Versalt I, punkt ovanför"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Okänd symbol"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Okänd emoji"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Uttråkat ansikte"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Generat ansikte"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Ansikte med solglasögon"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Förvånat ansikte"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Kyssande ansikte"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Ansikte med rynkad panna"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Alternativa tecken är tillgängliga"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Alternativa tecken avvisas"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Alternativa förslag är tillgängliga"</string>
diff --git a/java/res/values-sv/strings.xml b/java/res/values-sv/strings.xml
index d66c6c8..f7a94d9 100644
--- a/java/res/values-sv/strings.xml
+++ b/java/res/values-sv/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Knappljud"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Popup vid knapptryck"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"Inställningar"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"Konton och sekretess"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"Utseende och layouter"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"Alternativ för flera språk"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"Svepskrivning"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"Tryck här om du vill spara"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"En ordlista är tillgänglig"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Tangentbordstema"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"Byt konto"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"Du har inte valt något konto"</string>
+    <string name="account_selected" msgid="2846876462199625974">"Du använder nu <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"OK"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"Avbryt"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"Logga ut"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"Välj ett konto"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"Engelskt (brittiskt)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"Engelskt (amerikanskt)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"spanska (USA)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hindi/engelska"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"Serbiska (latinsk)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"Engelska (Storbritannien) <xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"Engelska (USA) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"Spanska (USA (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hindi/engelska (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbiska (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (traditionell)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (kyrillisk)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (latinsk)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Inget språk (alfabet)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabet (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabet (QWERTZ)"</string>
diff --git a/java/res/values-sw/strings-talkback-descriptions.xml b/java/res/values-sw/strings-talkback-descriptions.xml
index 6f84aa8..8763213 100644
--- a/java/res/values-sw/strings-talkback-descriptions.xml
+++ b/java/res/values-sw/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"Herufi I kubwa, alama ya kitone inayowekwa sehemu ya juu ya herufi katika alfabeti za Kilatini na Kivietnamu"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Alama isiyojulikana"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Emoji isiyojulikana"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Kikaragosi cha uso uliochoka"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Kikaragosi cha uso ulio tahayari"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Kikaragosi cha uso uliovaa miwani ya jua"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Kikaragosi cha uso unaoonyesha kushangaa"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Kikaragosi cha uso unaoonyesha busu"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Kikaragosi cha uso uliokunja ndita"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Herufi mbadala zinapatikana"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Herufi mbadala huondolewa"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Mapendekezo mbadala yanapatikana"</string>
diff --git a/java/res/values-sw/strings.xml b/java/res/values-sw/strings.xml
index 761d0b3..5f86d35 100644
--- a/java/res/values-sw/strings.xml
+++ b/java/res/values-sw/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Toa sauti unapobofya kitufe"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Ibuka kitufe kinapobonyezwa"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"Mapendeleo"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"Akaunti na faragha"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"Mwonekano na mipangilio"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"Chaguo za lugha nyingi"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"Kuandika kwa Ishara"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"Gusa hapa ili uhifadhi"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Kamusi inapatikana"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Maandhari ya kibodi"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"Badili akaunti"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"Hakuna akaunti zilizochaguliwa"</string>
+    <string name="account_selected" msgid="2846876462199625974">"Kwa sasa unatumia <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"SAWA"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"Ghairi"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"Toka"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"Chagua akaunti ya kutumia"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"Kiingereza cha (Uingereza)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"Kiingereza cha (Marekani)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"Kihispania (Marekani)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"Kiserbia (Kilatino)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"Kiingereza (UK) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"Kiingereza (US) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"Kihispania (US) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Kiserbia (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (cha Jadi)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Kikriliki)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Kilatini)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Hakuna lugha (Alfabeti)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabeti (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabeti (QWERTZ)"</string>
diff --git a/java/res/values-th/strings-talkback-descriptions.xml b/java/res/values-th/strings-talkback-descriptions.xml
index 5d70d71..509df3f 100644
--- a/java/res/values-th/strings-talkback-descriptions.xml
+++ b/java/res/values-th/strings-talkback-descriptions.xml
@@ -31,7 +31,7 @@
     <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Shift"</string>
     <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"สัญลักษณ์"</string>
     <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Shift"</string>
-    <string name="spoken_description_delete" msgid="3878902286264983302">"นำออก"</string>
+    <string name="spoken_description_delete" msgid="3878902286264983302">"ลบ"</string>
     <string name="spoken_description_to_symbol" msgid="8244903740201126590">"สัญลักษณ์"</string>
     <string name="spoken_description_to_alpha" msgid="4081215210530031950">"ตัวอักษร"</string>
     <string name="spoken_description_to_numeric" msgid="4560261331530795682">"หมายเลข"</string>
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"I ตัวใหญ่เติมจุดด้านบน"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"สัญลักษณ์ที่ไม่รู้จัก"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"อีโมจิที่ไม่รู้จัก"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"หน้าเบื่อ"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"หน้าอาย"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"หน้าสวมแว่นกันแดด"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"หน้าประหลาดใจ"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"หน้าทำปากจูบ"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"หน้ามุ่ย"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"อักขระทางเลือกพร้อมใช้งาน"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"ปิดอักขระทางเลือกแล้ว"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"ข้อเสนอแนะทางเลือกพร้อมใช้งาน"</string>
diff --git a/java/res/values-th/strings.xml b/java/res/values-th/strings.xml
index 8c6701d..94374cf 100644
--- a/java/res/values-th/strings.xml
+++ b/java/res/values-th/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"ส่งเสียงเมื่อกดปุ่ม"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"ป๊อปอัปเมื่อกดแป้น"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"ค่ากำหนด"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"บัญชีและข้อมูลส่วนบุคคล"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"ลักษณะที่ปรากฏและรูปแบบ"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"ตัวเลือกหลายภาษา"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"การป้อนข้อมูลด้วยท่าทาง"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"แตะที่นี่เพื่อบันทึก"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"มีพจนานุกรมให้ใช้งาน"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"ชุดรูปแบบแป้นพิมพ์"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"สลับบัญชี"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"ไม่ได้เลือกบัญชี"</string>
+    <string name="account_selected" msgid="2846876462199625974">"ปัจจุบันใช้ <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"ตกลง"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"ยกเลิก"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"ออกจากระบบ"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"เลือกบัญชีที่จะใช้"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"อังกฤษ (สหราชอาณาจักร)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"อังกฤษ (อเมริกัน)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"สเปน (สหรัฐอเมริกา)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"ภาษาอังกฤษผสมกับฮินดู"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"เซอร์เบีย (ละติน)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"อังกฤษ (สหราชอาณาจักร) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"อังกฤษ (สหรัฐอเมริกา) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"สเปน (สหรัฐอเมริกา) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"ภาษาอังกฤษผสมกับฮินดู (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"เซอร์เบีย (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ดั้งเดิม)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ซีริลลิก)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ละติน)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"ไม่มีภาษา (ตัวอักษรละติน)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"ตัวอักษร (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"ตัวอักษร (QWERTZ)"</string>
diff --git a/java/res/values-tl/strings-talkback-descriptions.xml b/java/res/values-tl/strings-talkback-descriptions.xml
index f601ad4..c70c48d 100644
--- a/java/res/values-tl/strings-talkback-descriptions.xml
+++ b/java/res/values-tl/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"Capital I, tuldok sa itaas"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Hindi alam na simbolo"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Hindi alam na emoji"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Tinatamad na mukha"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Nahihiya na mukha"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Mukha na nakasuot ng salamin"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Nagulat na mukha"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Humahalik na mukha"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Nakasimangot na mukha"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Available ang mga alternatibong character"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Dini-dismiss ang mga alternatibong character"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Available ang mga alternatibong suhestyon"</string>
diff --git a/java/res/values-tl/strings.xml b/java/res/values-tl/strings.xml
index 2329c7b..d523e2e 100644
--- a/java/res/values-tl/strings.xml
+++ b/java/res/values-tl/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Tumunog sa keypress"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Mag-popup sa keypress"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"Mga Kagustuhan"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"Mga account at privacy"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"Hitsura at mga layout"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"Opsyon na pangmaraming wika"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"Pagta-type Gamit ang Galaw"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"Pumindot dito upang mag-save"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Available ang diksyunaryo"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Tema ng keyboard"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"Lumipat ng account"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"Walang mga account na pinili"</string>
+    <string name="account_selected" msgid="2846876462199625974">"Kasalukuyang ginagamit ang <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"OK"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"Kanselahin"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"Mag-sign out"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"Pumili ng account na gagamitin"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"Ingles (UK)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"Ingles (Estados Unidos)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"Spanish (US)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hinglish"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"Serbian (Latin)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"Ingles (UK) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"Ingles (US) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"Spanish (US) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbian (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Traditional)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Cyrillic)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Latin)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Walang wika (Alpabeto)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alpabeto (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alpabeto (QWERTZ)"</string>
diff --git a/java/res/values-tr/strings-talkback-descriptions.xml b/java/res/values-tr/strings-talkback-descriptions.xml
index a810eda..302fba5 100644
--- a/java/res/values-tr/strings-talkback-descriptions.xml
+++ b/java/res/values-tr/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"Büyük Harf I, üst noktalı"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Bilinmeyen simge"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Bilinmeyen emoji"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Sıkılmış yüz"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Utanmış yüz"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Güneş gözlüğü takan yüz"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Şaşırmış yüz"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Öpen yüz"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Çatık kaşlı yüz"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Alternatif karakterler kullanılabilir"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Alternatif karakterler yoksayılır"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Alternatif öneriler kullanılabilir"</string>
diff --git a/java/res/values-tr/strings.xml b/java/res/values-tr/strings.xml
index 8bba793..1db5ec1 100644
--- a/java/res/values-tr/strings.xml
+++ b/java/res/values-tr/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Tuşa basıldığında ses çıkar"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Tuşa basıldığında pop-up aç"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"Tercihler"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"Hesaplar ve gizlilik"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"Görünüm ve düzen"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"Çok dilli seçenekler"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"Hareketle Yazma"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"Kaydetmek için buraya dokunun"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Sözlük kullanılabilir"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Klavye teması"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"Hesap değiştir"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"Hiçbir hesap seçilmedi"</string>
+    <string name="account_selected" msgid="2846876462199625974">"Şu anda <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g> kullanılıyor"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"Tamam"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"İptal"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"Oturumu kapat"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"Kullanmak için bir hesap seçin"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"İngilizce (BK)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"İngilizce (ABD)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"İspanyolca (ABD)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Hingilizce"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"Sırpça (Latin alfabesi)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"İngilizce (İngiltere) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"İngilizce (ABD) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"İspanyolca (ABD) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hingilizce (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Sırpça (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Geleneksel)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Kiril)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Latin)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Dil yok (Alfabe)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabe (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabe (QWERTZ)"</string>
diff --git a/java/res/values-uk/strings-talkback-descriptions.xml b/java/res/values-uk/strings-talkback-descriptions.xml
index 80e4710..4666e24 100644
--- a/java/res/values-uk/strings-talkback-descriptions.xml
+++ b/java/res/values-uk/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"Велика I, крапка вгорі"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Невідомий символ"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Невідомий смайл Emoji"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Обличчя, що нудьгує"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Збентежене обличчя"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Обличчя в окулярах"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Здивоване обличчя"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Обличчя, що цілує"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Насуплене обличчя"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Доступні альтернативні символи"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Альтернативні символи відхилено"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Доступні альтернативні пропозиції"</string>
diff --git a/java/res/values-uk/strings.xml b/java/res/values-uk/strings.xml
index 850efc0..29003d1 100644
--- a/java/res/values-uk/strings.xml
+++ b/java/res/values-uk/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Звук при натиску клав."</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Сплив. при нат.клав."</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"Параметри"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"Облік. записи й конфіденційність"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"Вигляд і макети"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"Опції кількома мовами"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"Введення тексту жестами"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"Торкніться тут, щоб зберегти"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Словник доступний"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Тема клавіатури"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"Змінити обліковий запис"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"Не вибрано облікових записів"</string>
+    <string name="account_selected" msgid="2846876462199625974">"Зараз використовується <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"ОК"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"Скасувати"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"Вийти"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"Виберіть обліковий запис"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"Англійська (Великобританія)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"Англійська (США)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"іспанська (США)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Хінґліш"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"сербська (латиниця)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"Англійська (Британія) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"Англійська (США) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"Іспанська (США) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Хінґліш (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"сербська (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (традиційна)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (кирилиця)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (латиниця)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Стандартна (латиниця)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Латиниця (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Латиниця (QWERTZ)"</string>
diff --git a/java/res/values-v21/platform-theme.xml b/java/res/values-v21/platform-theme.xml
index 2a54798..58763fb 100644
--- a/java/res/values-v21/platform-theme.xml
+++ b/java/res/values-v21/platform-theme.xml
@@ -20,6 +20,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android">
     <style name="platformActivityTheme" parent="@android:style/Theme.DeviceDefault.Light" />
-    <style name="platformSettingsTheme" parent="@android:style/Theme.DeviceDefault.Light" />
+    <style name="platformSettingsTheme" parent="@android:style/Theme.DeviceDefault.Settings" />
     <style name="platformDialogTheme" parent="@android:style/Theme.DeviceDefault.Light.Dialog" />
 </resources>
diff --git a/java/res/values-vi/strings-talkback-descriptions.xml b/java/res/values-vi/strings-talkback-descriptions.xml
index 659b1d3..1906624 100644
--- a/java/res/values-vi/strings-talkback-descriptions.xml
+++ b/java/res/values-vi/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"I hoa, dấu chấm phía trên"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Ký hiệu không xác định"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"Biểu tượng cảm xúc không xác định"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Mặt buồn chán"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Mặt bối rối"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Mặt đeo kính râm"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Mặt ngạc nhiên"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Mặt đang hôn"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Mặt cau mày"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Có ký tự thay thế"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Ký tự thay thế đã bị loại bỏ"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Có đề xuất thay thế"</string>
diff --git a/java/res/values-vi/strings.xml b/java/res/values-vi/strings.xml
index de0d011..8a73626 100644
--- a/java/res/values-vi/strings.xml
+++ b/java/res/values-vi/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Âm thanh khi nhấn phím"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Cửa sổ bật lên khi nhấn phím"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"Tùy chọn"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"Tài khoản và bảo mật"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"Giao diện và bố cục"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"Tùy chọn đa ngôn ngữ"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"Nhập bằng cử chỉ"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"Chạm vào đây để lưu"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Có sẵn từ điển"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Chủ đề bàn phím"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"Chuyển đổi tài khoản"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"Chưa chọn tài khoản nào"</string>
+    <string name="account_selected" msgid="2846876462199625974">"Hiện đang sử dụng <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"OK"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"Hủy"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"Đăng xuất"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"Chọn một tài khoản để sử dụng"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"Tiếng Anh (Anh)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"Tiếng Anh (Mỹ)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"Tiếng Tây Ban Nha (Mỹ)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"Tiếng Anh-Hindi"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"Tiếng Serbia (La tinh)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"Tiếng Anh (Anh) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"Tiếng Anh (Mỹ) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"Tiếng Tây Ban Nha (Mỹ) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Tiếng Anh-Hindi (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Tiếng Serbia (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Truyền thống)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tiếng Kirin)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tiếng Latin)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Không ngôn ngữ nào (Bảng chữ cái)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Bảng chữ cái (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Bảng chữ cái (QWERTZ)"</string>
diff --git a/java/res/values-zh-rCN/strings-talkback-descriptions.xml b/java/res/values-zh-rCN/strings-talkback-descriptions.xml
index b7b6b41..5d3712b 100644
--- a/java/res/values-zh-rCN/strings-talkback-descriptions.xml
+++ b/java/res/values-zh-rCN/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"带上点的拉丁文大写字母I"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"未知符号"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"未知表情符号"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"无聊"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"尴尬"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"戴墨镜"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"惊讶"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"亲吻"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"皱眉"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"有可用的替代字符"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"已关闭替代字符"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"有可用的其他建议字词"</string>
diff --git a/java/res/values-zh-rCN/strings.xml b/java/res/values-zh-rCN/strings.xml
index a1c8d18..7e0a103 100644
--- a/java/res/values-zh-rCN/strings.xml
+++ b/java/res/values-zh-rCN/strings.xml
@@ -27,16 +27,14 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"按键音效"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"按键时弹出显示字符"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"偏好设置"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"帐户和隐私权"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"外观和布局"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"多语言选项"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"滑行输入"</string>
     <string name="settings_screen_correction" msgid="1616818407747682955">"文字更正"</string>
     <string name="settings_screen_advanced" msgid="7472408607625972994">"高级"</string>
     <string name="settings_screen_theme" msgid="2137262503543943871">"主题背景"</string>
-    <!-- no translation found for enable_split_keyboard (4177264923999493614) -->
-    <skip />
+    <string name="enable_split_keyboard" msgid="4177264923999493614">"使用分离式键盘"</string>
     <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"切换到其他输入法"</string>
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"语言切换键也可用于切换其他输入法"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"语言切换键"</string>
@@ -86,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"触摸此处即可保存"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"有可用字典"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"键盘主题"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"切换帐户"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"未选择任何帐户"</string>
+    <string name="account_selected" msgid="2846876462199625974">"当前使用的是<xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"确定"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"取消"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"退出帐户"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"选择要使用的帐户"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"英语(英国)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"英语(美国)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"西班牙语(美国)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"印地英语"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"塞尔维亚语(拉丁语布局)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"英式英语(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"美式英语(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"美式西班牙语(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"印地英语(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"塞尔维亚语(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>布局)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g>(传统)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g>(西里尔文)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g>(拉丁文)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"无语言(字母)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"字母 (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"字母 (QWERTZ)"</string>
diff --git a/java/res/values-zh-rHK/strings-talkback-descriptions.xml b/java/res/values-zh-rHK/strings-talkback-descriptions.xml
index 386b5b5..e290268 100644
--- a/java/res/values-zh-rHK/strings-talkback-descriptions.xml
+++ b/java/res/values-zh-rHK/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"上方加點的大寫 I"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"未知的符號"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"未知的表情符號"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"無聊"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"尷尬"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"戴著墨鏡"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"驚訝"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"親吻"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"皺眉"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"有可用的替代字元"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"已關閉替代字元"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"有可用的建議字詞"</string>
diff --git a/java/res/values-zh-rHK/strings.xml b/java/res/values-zh-rHK/strings.xml
index 475ae0a..74a5524 100644
--- a/java/res/values-zh-rHK/strings.xml
+++ b/java/res/values-zh-rHK/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"按鍵時播放音效"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"按鍵時顯示彈出式視窗"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"偏好設定"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"帳戶與私隱權"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"外觀和版面配置"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"多語選項"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"手勢輸入"</string>
@@ -79,37 +78,30 @@
     <string name="voice_input_disabled_summary" msgid="8141750303464726129">"尚未啟用語音輸入法,請檢查語言和輸入設定。"</string>
     <string name="configure_input_method" msgid="373356270290742459">"設定輸入法"</string>
     <string name="language_selection_title" msgid="3666971864764478269">"語言"</string>
-    <string name="help_and_feedback" msgid="5328219371839879161">"說明與意見反映"</string>
+    <string name="help_and_feedback" msgid="5328219371839879161">"說明和意見反映"</string>
     <string name="select_language" msgid="5709487854987078367">"語言"</string>
     <string name="hint_add_to_dictionary" msgid="573678656946085380">"再次輕觸即可儲存"</string>
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"輕觸即可儲存"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"可使用字典"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"鍵盤主題"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"切換帳戶"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"未選取任何帳戶"</string>
+    <string name="account_selected" msgid="2846876462199625974">"目前使用的是 <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"確定"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"取消"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"登出"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"選取要使用的帳戶"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"英文 (英國)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"英文 (美國)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"西班牙文 (美國)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"印度英文"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"塞爾維亞文 (拉丁文)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"英文 (英國) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"英文 (美國) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"西班牙文 (美國) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"印度英文 (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"塞爾維亞文 (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (傳統)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (西里爾文)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (拉丁文)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"無語言 (字母)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"字母 (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"字母 (QWERTZ)"</string>
diff --git a/java/res/values-zh-rTW/strings-talkback-descriptions.xml b/java/res/values-zh-rTW/strings-talkback-descriptions.xml
index 7a5f3df..07f969f 100644
--- a/java/res/values-zh-rTW/strings-talkback-descriptions.xml
+++ b/java/res/values-zh-rTW/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"上方加點的大寫 I"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"未知的符號"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"未知的表情符號"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"無聊"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"尷尬"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"戴墨鏡"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"驚訝"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"親吻"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"皺眉"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"有可用的替代字元"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"已關閉替代字元"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"有可用的建議字詞"</string>
diff --git a/java/res/values-zh-rTW/strings.xml b/java/res/values-zh-rTW/strings.xml
index 316f3f7..3b999ea 100644
--- a/java/res/values-zh-rTW/strings.xml
+++ b/java/res/values-zh-rTW/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"按鍵聲音"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"按鍵時彈出"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"偏好設定"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"帳戶與隱私權"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"外觀與版面配置"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"多語選項"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"手勢輸入"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"輕觸這裡即可儲存"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"可用的字典"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"鍵盤主題"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"切換帳戶"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"尚未選取帳戶"</string>
+    <string name="account_selected" msgid="2846876462199625974">"目前使用的帳戶是 <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"確定"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"取消"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"登出"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"選取要使用的帳戶"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"英文 (英國)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"英文 (美國)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"西班牙文 (美國)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"印度英文"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"塞爾維亞文 (拉丁文)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"英文 (英國) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"英文 (美國) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"西班牙文 (美國) (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"印度英文 (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"塞爾維亞文 (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (傳統)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (斯拉夫文)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (拉丁文)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"無語言 (字母)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"字母 (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"字母 (QWERTZ)"</string>
diff --git a/java/res/values-zu/strings-talkback-descriptions.xml b/java/res/values-zu/strings-talkback-descriptions.xml
index 6d3de70..3e73621 100644
--- a/java/res/values-zu/strings-talkback-descriptions.xml
+++ b/java/res/values-zu/strings-talkback-descriptions.xml
@@ -76,6 +76,12 @@
     <string name="spoken_letter_0130" msgid="4766619646231612274">"Ufeleba I, icashazi ngenhla"</string>
     <string name="spoken_symbol_unknown" msgid="717298227061173706">"Uphawu olungaziwa"</string>
     <string name="spoken_emoji_unknown" msgid="5981009928135394306">"I-emoji engaziwa"</string>
+    <string name="spoken_emoticon_3A_2D_21_20" msgid="2410905667389534573">"Ubuso obunesithukuthezi"</string>
+    <string name="spoken_emoticon_3A_2D_24_20" msgid="2481260475945560438">"Ubuso obuphoxekile"</string>
+    <string name="spoken_emoticon_42_2D_29_20" msgid="1063205250387128068">"Ubuso obugqoke izibuko"</string>
+    <string name="spoken_emoticon_3A_4F_20" msgid="532695091593447238">"Ubuso obumangele"</string>
+    <string name="spoken_emoticon_3A_2D_2A_20" msgid="5612342617244114291">"Ukubso bokuqabula"</string>
+    <string name="spoken_emoticon_3A_2D_5B_20" msgid="2223507987759905920">"Ubuso obuswacile"</string>
     <string name="spoken_open_more_keys_keyboard" msgid="6832897688371903747">"Ezinye izinhlamvu ziyatholakala"</string>
     <string name="spoken_close_more_keys_keyboard" msgid="3524914657934712026">"Ezinye izinhlamvu ziyalahlwa"</string>
     <string name="spoken_open_more_suggestions" msgid="4231720702882969760">"Ezinye iziphakamiso ziyatholakala"</string>
diff --git a/java/res/values-zu/strings.xml b/java/res/values-zu/strings.xml
index 607407e..4d2afab 100644
--- a/java/res/values-zu/strings.xml
+++ b/java/res/values-zu/strings.xml
@@ -27,8 +27,7 @@
     <string name="sound_on_keypress" msgid="6093592297198243644">"Umsindo wokucindezela ukhiye"</string>
     <string name="popup_on_keypress" msgid="123894815723512944">"Ugaxekile ngokucindezela ukhiye"</string>
     <string name="settings_screen_preferences" msgid="2696713156722014624">"Izintandokazi"</string>
-    <!-- no translation found for settings_screen_accounts (7570397912370223287) -->
-    <skip />
+    <string name="settings_screen_accounts" msgid="7570397912370223287">"Ama-Akhawunti nokobumfihlo"</string>
     <string name="settings_screen_appearance" msgid="9153102634339912029">"Ukubonakala nezakhiwo"</string>
     <string name="settings_screen_multilingual" msgid="1391301621464509659">"Izinketho zezilimi eziningi"</string>
     <string name="settings_screen_gesture" msgid="8826372746901183556">"Ukuthayipha ngokuthinta"</string>
@@ -85,31 +84,24 @@
     <string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"Thinta lapha ukuze ulondoloze"</string>
     <string name="has_dictionary" msgid="6071847973466625007">"Isichazamazwi siyatholakala"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"Indikimba yekhibhodi"</string>
-    <!-- no translation found for switch_accounts (3321216593719006162) -->
-    <skip />
-    <!-- no translation found for no_accounts_selected (2073821619103904330) -->
-    <skip />
-    <!-- no translation found for account_selected (2846876462199625974) -->
-    <skip />
-    <!-- no translation found for account_select_ok (9141195141763227797) -->
-    <skip />
-    <!-- no translation found for account_select_cancel (5181012062618504340) -->
-    <skip />
-    <!-- no translation found for account_select_sign_out (3299651159390187933) -->
-    <skip />
-    <!-- no translation found for account_select_title (6279711684772922649) -->
-    <skip />
+    <string name="switch_accounts" msgid="3321216593719006162">"Shintshanisa ama-akhawunti"</string>
+    <string name="no_accounts_selected" msgid="2073821619103904330">"Awekho ama-akhawunti akhethiwe"</string>
+    <string name="account_selected" msgid="2846876462199625974">"Okwamanje isebenzisa i-<xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+    <string name="account_select_ok" msgid="9141195141763227797">"KULUNGILE"</string>
+    <string name="account_select_cancel" msgid="5181012062618504340">"Khansela"</string>
+    <string name="account_select_sign_out" msgid="3299651159390187933">"Phuma ngemvume"</string>
+    <string name="account_select_title" msgid="6279711684772922649">"Khetha i-akhawunti ezosetshenziswa"</string>
     <string name="subtype_en_GB" msgid="88170601942311355">"i-English(UK)"</string>
     <string name="subtype_en_US" msgid="6160452336634534239">"i-English (US)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"I-Spanish (US)"</string>
     <string name="subtype_hi_ZZ" msgid="8860448146262798623">"I-Hinglish"</string>
+    <string name="subtype_sr_ZZ" msgid="9059219552986034343">"Isi-Serbian (Latin)"</string>
     <string name="subtype_with_layout_en_GB" msgid="1931018968641592304">"I-English (UK) ( <xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g> )"</string>
     <string name="subtype_with_layout_en_US" msgid="8809311287529805422">"I-English (US) ( <xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g> )"</string>
     <string name="subtype_with_layout_es_US" msgid="510930471167541338">"Isi-Spanish (US) ( <xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g> )"</string>
     <string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"I-Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Isi-Serbian (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_generic_traditional" msgid="8584594350973800586">"Isi-<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Tradition)"</string>
-    <string name="subtype_generic_cyrillic" msgid="7486451947618138947">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Isi-Cyrillic)"</string>
-    <string name="subtype_generic_latin" msgid="9128716486310604145">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Isi-Latin)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Alikho ulimi (Alfabhethi)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabhethi (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabhethi (QWERTZ)"</string>
diff --git a/java/res/values/donottranslate-debug-settings.xml b/java/res/values/donottranslate-debug-settings.xml
index 35e6efa..199f977 100644
--- a/java/res/values/donottranslate-debug-settings.xml
+++ b/java/res/values/donottranslate-debug-settings.xml
@@ -23,7 +23,7 @@
     <string name="prefs_debug_mode">Debug Mode</string>
     <string name="prefs_force_non_distinct_multitouch">Force non-distinct multitouch</string>
     <string name="prefs_force_physical_keyboard_special_key">Force physical keyboard special key</string>
-    <string name="prefs_show_ui_to_accept_typed_word">Show UI to accept typed word</string>
+    <string name="prefs_should_show_lxx_suggestion_ui">Show LXX suggestion UI</string>
     <!-- Option to enable sliding key input indicator. The user can see a rubber band-like effect during sliding key input. [CHAR LIMIT=30]-->
     <string name="sliding_key_input_preview">Show slide indicator</string>
     <!-- Option summary to enable sliding key input indicator. The user can see a rubber band-like effect during sliding key input. [CHAR LIMIT=66]-->
diff --git a/java/res/xml-sw600dp/key_period.xml b/java/res/xml-sw600dp/key_period.xml
index df1daf7..3045cbc 100644
--- a/java/res/xml-sw600dp/key_period.xml
+++ b/java/res/xml-sw600dp/key_period.xml
@@ -25,17 +25,6 @@
     <!-- Kept as a separate file for cleaner overriding by an overlay.  -->
     <switch>
         <case
-            latin:languageCode="hi"
-            latin:keyboardLayoutSet="hindi_compact"
-        >
-            <!-- U+0964: "।" DEVANAGARI DANDA -->
-            <Key
-                latin:keySpec="\u0964"
-                latin:keyLabelFlags="hasPopupHint"
-                latin:moreKeys="!autoColumnOrder!8,\\,,.,',#,),(,/,;,@,:,-,&quot;,+,\\%,&amp;"
-                latin:backgroundType="functional" />
-        </case>
-        <case
             latin:keyboardLayoutSet="dvorak"
         >
             <Key
diff --git a/java/res/xml-sw600dp/rows_bengali_akkhor.xml b/java/res/xml-sw600dp/rows_bengali_akkhor.xml
new file mode 100644
index 0000000..3e81a14
--- /dev/null
+++ b/java/res/xml-sw600dp/rows_bengali_akkhor.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+
+<merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" >
+    <include latin:keyboardLayout="@xml/key_styles_common" />
+    <Row
+        latin:keyLabelFlags="fontNormal"
+        latin:keyWidth="8.182%p" >
+        <include latin:keyboardLayout="@xml/rowkeys_bengali_akkhor1" />
+        <Key
+            latin:keyStyle="deleteKeyStyle"
+            latin:keyWidth="fillRight" />
+    </Row>
+    <Row
+        latin:keyLabelFlags="fontNormal"
+        latin:keyWidth="8.182%p" >
+        <include latin:keyboardLayout="@xml/rowkeys_bengali_akkhor2" />
+        <Key
+            latin:keyStyle="enterKeyStyle"
+            latin:keyWidth="fillRight" />
+    </Row>
+    <Row
+        latin:keyLabelFlags="fontNormal"
+        latin:keyWidth="8.182%p" >
+        <Key
+            latin:keyStyle="shiftKeyStyle"
+            latin:keyWidth="10.0%p" />
+
+        <include latin:keyboardLayout="@xml/rowkeys_bengali_akkhor3" />
+        <include latin:keyboardLayout="@xml/keys_exclamation_question" />
+    </Row>
+    <include latin:keyboardLayout="@xml/row_qwerty4" />
+</merge>
diff --git a/java/res/xml-sw600dp/rows_nepali_romanized.xml b/java/res/xml-sw600dp/rows_nepali_romanized.xml
index fe73fbd..41cd2f9 100644
--- a/java/res/xml-sw600dp/rows_nepali_romanized.xml
+++ b/java/res/xml-sw600dp/rows_nepali_romanized.xml
@@ -39,7 +39,7 @@
             latin:keyWidth="fillRight" />
     </Row>
     <Row
-        latin:keyWidth="8.182%p"
+        latin:keyWidth="8.0%p"
         latin:keyLabelFlags="fontNormal"
     >
         <Key
@@ -47,6 +47,9 @@
             latin:keyWidth="10.0%p" />
         <include latin:keyboardLayout="@xml/rowkeys_nepali_romanized3" />
         <include latin:keyboardLayout="@xml/keys_exclamation_question" />
+        <Key
+            latin:keyStyle="shiftKeyStyle"
+            latin:keyWidth="fillRight" />
     </Row>
     <include latin:keyboardLayout="@xml/row_qwerty4" />
 </merge>
diff --git a/java/res/xml-sw600dp/rows_nepali_traditional.xml b/java/res/xml-sw600dp/rows_nepali_traditional.xml
index e56271f..d23ca24 100644
--- a/java/res/xml-sw600dp/rows_nepali_traditional.xml
+++ b/java/res/xml-sw600dp/rows_nepali_traditional.xml
@@ -45,8 +45,8 @@
         <Key
             latin:keyStyle="shiftKeyStyle"
             latin:keyWidth="10.0%p" />
-        <include latin:keyboardLayout="@xml/rowkeys_nepali_traditional3_left6" />
-        <include latin:keyboardLayout="@xml/rowkeys_nepali_traditional3_right5" />
-    </Row>
+        <include latin:keyboardLayout="@xml/rowkeys_nepali_traditional3" />
+        <include latin:keyboardLayout="@xml/keys_exclamation_question" />
+        </Row>
     <include latin:keyboardLayout="@xml/row_qwerty4" />
 </merge>
diff --git a/java/res/xml-sw600dp/rows_uzbek.xml b/java/res/xml-sw600dp/rows_uzbek.xml
new file mode 100644
index 0000000..3cd9940
--- /dev/null
+++ b/java/res/xml-sw600dp/rows_uzbek.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+
+<merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" >
+    <include latin:keyboardLayout="@xml/key_styles_common" />
+    <Row latin:keyWidth="8.182%p" >
+        <include latin:keyboardLayout="@xml/rowkeys_uzbek1" />
+        <Key
+            latin:keyStyle="deleteKeyStyle"
+            latin:keyWidth="fillRight" />
+    </Row>
+    <Row latin:keyWidth="8.182%p" >
+        <include latin:keyboardLayout="@xml/rowkeys_uzbek2" />
+        <Key
+            latin:keyStyle="enterKeyStyle"
+            latin:keyWidth="fillRight" />
+    </Row>
+    <Row latin:keyWidth="8.182%p" >
+        <Key
+            latin:keyStyle="shiftKeyStyle"
+            latin:keyWidth="10.0%p" />
+        <Spacer latin:keyWidth="3.181%p" />
+        <include latin:keyboardLayout="@xml/rowkeys_qwerty3" />
+        <include latin:keyboardLayout="@xml/keys_exclamation_question" />
+        <Key
+            latin:keyStyle="shiftKeyStyle"
+            latin:keyWidth="fillRight"
+            latin:keyXPos="-10.0%p" />
+    </Row>
+    <include latin:keyboardLayout="@xml/row_qwerty4" />
+</merge>
\ No newline at end of file
diff --git a/java/res/xml/kbd_bengali_akkhor.xml b/java/res/xml/kbd_bengali_akkhor.xml
new file mode 100644
index 0000000..6c81d53
--- /dev/null
+++ b/java/res/xml/kbd_bengali_akkhor.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+
+<Keyboard xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" >
+    <include latin:keyboardLayout="@xml/rows_bengali_akkhor" />
+</Keyboard>
diff --git a/java/res/xml/kbd_uzbek.xml b/java/res/xml/kbd_uzbek.xml
new file mode 100644
index 0000000..84486a7
--- /dev/null
+++ b/java/res/xml/kbd_uzbek.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+
+<Keyboard xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" >
+    <include latin:keyboardLayout="@xml/rows_uzbek" />
+</Keyboard>
\ No newline at end of file
diff --git a/java/res/xml/key_period.xml b/java/res/xml/key_period.xml
index fc27c02..5b59cff 100644
--- a/java/res/xml/key_period.xml
+++ b/java/res/xml/key_period.xml
@@ -23,32 +23,6 @@
 >
     <switch>
         <case
-            latin:languageCode="hi"
-            latin:keyboardLayoutSet="hindi_compact"
-        >
-            <!-- U+0964: "।" DEVANAGARI DANDA -->
-            <Key
-                latin:keySpec="\u0964"
-                latin:keyLabelFlags="hasPopupHint"
-                latin:moreKeys="!autoColumnOrder!9,\\,,.,?,!,#,),(,/,;,',@,:,-,&quot;,+,\\%,&amp;"
-                latin:backgroundType="functional" />
-        </case>
-        <case
-            latin:languageCode="ne"
-            latin:keyboardLayoutSet="nepali_traditional"
-        >
-            <!-- Because the font rendering system prior to API version 16 can't automatically
-                 render dotted circle for incomplete combining letter of some scripts, different
-                 set of Key definitions are needed based on the API version. -->
-            <include
-                latin:keyboardLayout="@xml/keystyle_devanagari_sign_virama" />
-            <Key
-                latin:keyStyle="baseKeyDevanagariSignVirama"
-                latin:keyLabelFlags="hasPopupHint"
-                latin:moreKeys="!text/morekeys_punctuation"
-                latin:backgroundType="functional" />
-        </case>
-        <case
             latin:keyboardLayoutSet="dvorak"
         >
             <Key
diff --git a/java/res/xml/key_styles_currency.xml b/java/res/xml/key_styles_currency.xml
index 2ce6318..de5e6f2 100644
--- a/java/res/xml/key_styles_currency.xml
+++ b/java/res/xml/key_styles_currency.xml
@@ -98,8 +98,9 @@
             <include latin:keyboardLayout="@xml/key_styles_currency_generic" />
         </case>
         <!-- si_LK: Sinhala (Sri Lanka) (Sri Lanka Rupee)
-             ta_LK: Tamil (Sri Lanka) (Sri Lanka Rupee) -->
-        <case latin:countryCode="LK">
+             ta_LK: Tamil (Sri Lanka) (Sri Lanka Rupee)
+             bn_BD: Bengali (Bangladesh) (Taka) -->
+        <case latin:countryCode="LK|BD">
             <include latin:keyboardLayout="@xml/key_styles_currency_generic" />
         </case>
         <!-- bn_IN: Bengali (India) (Indian Rupee)
diff --git a/java/res/xml/keyboard_layout_set_bengali_akkhor.xml b/java/res/xml/keyboard_layout_set_bengali_akkhor.xml
new file mode 100644
index 0000000..b2b09b2
--- /dev/null
+++ b/java/res/xml/keyboard_layout_set_bengali_akkhor.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+
+<KeyboardLayoutSet xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" >
+    <Feature latin:supportedScript="devanagari" />
+    <Element
+        latin:elementKeyboard="@xml/kbd_bengali_akkhor"
+        latin:elementName="alphabet"
+        latin:enableProximityCharsCorrection="true" />
+    <Element
+        latin:elementKeyboard="@xml/kbd_bengali_akkhor"
+        latin:elementName="alphabetAutomaticShifted"
+        latin:enableProximityCharsCorrection="true" />
+    <!-- On these shifted alphabet layouts the proximity characters correction should be disabled
+         because the letters on these layouts aren't the ones in different case of the above
+         unshifted layouts.-->
+    <Element
+        latin:elementKeyboard="@xml/kbd_bengali_akkhor"
+        latin:elementName="alphabetManualShifted" />
+    <Element
+        latin:elementKeyboard="@xml/kbd_bengali_akkhor"
+        latin:elementName="alphabetShiftLocked" />
+    <Element
+        latin:elementKeyboard="@xml/kbd_bengali_akkhor"
+        latin:elementName="alphabetShiftLockShifted" />
+    <Element
+        latin:elementKeyboard="@xml/kbd_symbols"
+        latin:elementName="symbols" />
+    <Element
+        latin:elementKeyboard="@xml/kbd_symbols_shift"
+        latin:elementName="symbolsShifted" />
+    <Element
+        latin:elementKeyboard="@xml/kbd_phone"
+        latin:elementName="phone" />
+    <Element
+        latin:elementKeyboard="@xml/kbd_phone_symbols"
+        latin:elementName="phoneSymbols" />
+    <Element
+        latin:elementKeyboard="@xml/kbd_number"
+        latin:elementName="number" />
+</KeyboardLayoutSet>
diff --git a/java/res/xml/keyboard_layout_set_uzbek.xml b/java/res/xml/keyboard_layout_set_uzbek.xml
new file mode 100644
index 0000000..8251b01
--- /dev/null
+++ b/java/res/xml/keyboard_layout_set_uzbek.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+
+<KeyboardLayoutSet xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" >
+    <Element
+        latin:elementName="alphabet"
+        latin:elementKeyboard="@xml/kbd_uzbek"
+        latin:enableProximityCharsCorrection="true"
+        latin:allowRedundantMoreKeys="true" />
+    <Element
+        latin:elementKeyboard="@xml/kbd_symbols"
+        latin:elementName="symbols" />
+    <Element
+        latin:elementKeyboard="@xml/kbd_symbols_shift"
+        latin:elementName="symbolsShifted" />
+    <Element
+        latin:elementKeyboard="@xml/kbd_phone"
+        latin:elementName="phone" />
+    <Element
+        latin:elementKeyboard="@xml/kbd_phone_symbols"
+        latin:elementName="phoneSymbols" />
+    <Element
+        latin:elementKeyboard="@xml/kbd_number"
+        latin:elementName="number" />
+</KeyboardLayoutSet>
\ No newline at end of file
diff --git a/java/res/xml/keystyle_devanagari_sign_virama.xml b/java/res/xml/keystyle_devanagari_sign_virama.xml
index ff778d9..7863ca4 100644
--- a/java/res/xml/keystyle_devanagari_sign_virama.xml
+++ b/java/res/xml/keystyle_devanagari_sign_virama.xml
@@ -35,7 +35,7 @@
                  U+090D: "ऍ" DEVANAGARI LETTER CANDRA E -->
             <key-style
                 latin:styleName="moreKeysDevanagariSignVirama"
-                latin:moreKeys="&#x25CC;&#x0945;,&#x090D;" />
+                latin:moreKeys="&#x25CC;&#x0945;|&#x0945;,&#x090D;" />
         </case>
         <case latin:keyboardLayoutSet="marathi">
             <!-- U+0905: "अ" DEVANAGARI LETTER A -->
diff --git a/java/res/xml/method.xml b/java/res/xml/method.xml
index 270f90c..77a46f9 100644
--- a/java/res/xml/method.xml
+++ b/java/res/xml/method.xml
@@ -28,6 +28,7 @@
     be_BY: Belarusian (Belarus)/east_slavic
     bg: Bulgarian/bulgarian
     bg: Bulgarian/bulgarian_bds
+    (bn_BD: Bengali (Bangladesh)/bengali_akkhor) # This is a preliminary keyboard layout.
     bn_IN: Bengali (India)/bengali
     ca: Catalan/spanish
     cs: Czech/qwertz
@@ -100,6 +101,7 @@
     tl: Tagalog/spanish
     tr: Turkish/qwerty
     uk: Ukrainian/east_slavic
+    (uz_UZ: Uzbek (Uzbekistan)/uzbek) # This is a preliminary keyboard layout.
     vi: Vietnamese/qwerty
     zu: Zulu/qwerty
     zz: QWERTY/qwerty
@@ -179,6 +181,18 @@
             android:imeSubtypeExtraValue="KeyboardLayoutSet=bulgarian_bds,EmojiCapable"
             android:isAsciiCapable="false"
     />
+    <!-- TODO: This Bengali (Bangladesh) keyboard is a preliminary layout.
+               This isn't based on the final specification. -->
+    <!--
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
+            android:label="@string/subtype_generic"
+            android:subtypeId="0xa2144b0c"
+            android:imeSubtypeLocale="bn_BD"
+            android:imeSubtypeMode="keyboard"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=bengali_akkhor,EmojiCapable"
+            android:isAsciiCapable="false"
+    />
+    -->
     <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0xbff5986c"
@@ -357,6 +371,7 @@
     />
     <!-- TODO: This Hinglish keyboard is a preliminary layout.
                This isn't based on the final specification. -->
+    <!--
     <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_hi_ZZ"
             android:subtypeId="0x352eb37c"
@@ -365,6 +380,7 @@
             android:imeSubtypeExtraValue="AsciiCapable,KeyboardLayoutSet=qwerty,EmojiCapable"
             android:isAsciiCapable="true"
     />
+    -->
     <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x35b7526a"
@@ -537,6 +553,7 @@
     />
     <!-- TODO: This Myanmar keyboard is a preliminary layout.
                This isn't based on the final specification. -->
+    <!--
     <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0xea266ea4"
@@ -545,6 +562,7 @@
             android:imeSubtypeExtraValue="KeyboardLayoutSet=myanmar,EmojiCapable,CombiningRules=MyanmarReordering"
             android:isAsciiCapable="false"
     />
+    -->
     <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x3f12ee14"
@@ -661,6 +679,9 @@
             android:imeSubtypeExtraValue="SupportTouchPositionCorrection,EmojiCapable"
             android:isAsciiCapable="false"
     />
+    <!-- TODO: This Serbian Latin keyboard is a preliminary layout.
+               This isn't based on the final specification. -->
+    <!--
     <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_sr_ZZ"
             android:subtypeId="0xf4a5569c"
@@ -669,6 +690,7 @@
             android:imeSubtypeExtraValue="KeyboardLayoutSet=serbian_qwertz,AsciiCapable,EmojiCapable"
             android:isAsciiCapable="true"
     />
+    -->
     <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x48b4ff43"
@@ -693,7 +715,7 @@
             android:imeSubtypeExtraValue="KeyboardLayoutSet=tamil,EmojiCapable"
             android:isAsciiCapable="false"
     />
-    <!-- TODO: Enable ta_LK subtype when si_LK subtype is ready -->
+    <!-- TODO: Enabling/Disabling ta_LK subtype must be aligned with si_LK subtype. -->
     <!--
     <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
@@ -752,6 +774,18 @@
             android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic,EmojiCapable"
             android:isAsciiCapable="false"
     />
+    <!-- TODO: This Uzbek keyboard is a preliminary layout.
+               This isn't based on the final specification. -->
+    <!--
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
+            android:label="@string/subtype_generic"
+            android:subtypeId="0xad5cf7f6"
+            android:imeSubtypeLocale="uz_UZ"
+            android:imeSubtypeMode="keyboard"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=uzbek,AsciiCapable,EmojiCapable"
+            android:isAsciiCapable="true"
+    />
+    -->
     <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x93972eee"
diff --git a/java/res/xml/prefs_screen_debug.xml b/java/res/xml/prefs_screen_debug.xml
index c477402..25f7c66 100644
--- a/java/res/xml/prefs_screen_debug.xml
+++ b/java/res/xml/prefs_screen_debug.xml
@@ -36,8 +36,8 @@
         android:defaultValue="false"
         android:persistent="true" />
     <CheckBoxPreference
-        android:key="pref_show_ui_to_accept_typed_word"
-        android:title="@string/prefs_show_ui_to_accept_typed_word"
+        android:key="pref_should_show_lxx_suggestion_ui"
+        android:title="@string/prefs_should_show_lxx_suggestion_ui"
         android:defaultValue="true"
         android:persistent="true" />
     <CheckBoxPreference
diff --git a/java/res/xml/rowkeys_bengali_akkhor1.xml b/java/res/xml/rowkeys_bengali_akkhor1.xml
new file mode 100644
index 0000000..bc612be
--- /dev/null
+++ b/java/res/xml/rowkeys_bengali_akkhor1.xml
@@ -0,0 +1,183 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+
+<merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
+    <switch>
+        <case latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted">
+            <!-- U+09A2: "ঢ" BENGALI LETTER DDHA -->
+            <Key latin:keySpec="&#x09A2;" />
+            <!-- U+09A0: "ঠ" BENGALI LETTER TTHA -->
+            <Key latin:keySpec="&#x09A0;" />
+            <!-- U+09C8: "ৈ" BENGALI VOWEL SIGN AI
+                 U+0990: "ঐ" BENGALI LETTER AI -->
+            <Key
+                latin:keySpec="&#x09C8;"
+                latin:moreKeys="&#x0990;" />
+            <!-- U+09DC: "ড়" BENGALI LETTER RRA
+                 U+09BC: "়" BENGALI SIGN NUKTA -->
+            <Key
+                latin:keySpec="&#x09DC;"
+                latin:moreKeys="&#x09BC;" />
+            <!-- U+099F: "ট" BENGALI LETTER TTA
+                 U+09F3: "৳" BENGALI RUPEE SIGN
+                 U+099F/U+09CD/U+099F: "ট্ট" BENGALI LETTER TTA/BENGALI SIGN VIRAMA/BENGALI LETTER TTA
+                 U+099F/U+09CD/U+09AC: "ট্ব" BENGALI LETTER TTA/BENGALI SIGN VIRAMA/BENGALI LETTER BA
+                 U+099F/U+09CD/U+09AE: "ট্ম" BENGALI LETTER TTA/BENGALI SIGN VIRAMA/BENGALI LETTER MA -->
+            <Key
+                latin:keySpec="&#x099F;"
+                latin:moreKeys="&#x09F3;,&#x099F;&#x09CD;&#x099F;,&#x099F;&#x09CD;&#x09AC;,&#x099F;&#x09CD;&#x09AE;" />
+            <!-- U+099E: "ঞ" BENGALI LETTER NYA
+                 U+099E/U+09CD/U+099A: "ঞ্চ" BENGALI LETTER NYA/BENGALI SIGN VIRAMA/BENGALI LETTER CA
+                 U+099E/U+09CD/U+099B: "ঞ্ছ" BENGALI LETTER NYA/BENGALI SIGN VIRAMA/BENGALI LETTER CHA
+                 U+099E/U+09CD/U+099C: "ঞ্জ" BENGALI LETTER NYA/BENGALI SIGN VIRAMA/BENGALI LETTER JA -->
+            <Key
+                latin:keySpec="&#x099E;"
+                latin:moreKeys="&#x099E;&#x09CD;&#x099A;,&#x099E;&#x09CD;&#x099B;,&#x099E;&#x09CD;&#x099C;" />
+            <!-- U+09C2: "ূ" BENGALI VOWEL SIGN UU
+                 U+098A: "ঊ" BENGALI LETTER UU -->
+            <Key
+                latin:keySpec="&#x09C2;"
+                latin:moreKeys="&#x098A;" />
+            <!-- U+09C0: "ী" BENGALI VOWEL SIGN II
+                 U+0988: "ঈ" BENGALI LETTER II -->
+            <Key
+                latin:keySpec="&#x09C0;"
+                latin:moreKeys="&#x0988;" />
+            <!-- U+09CC: "ৌ" BENGALI VOWEL SIGN AU
+                 U+099A: "ঔ" BENGALI LETTER CA
+                 U+09D7: "ৗ" BENGALI AU LENGTH MARK -->
+            <Key
+                latin:keySpec="&#x09CC;"
+                latin:moreKeys="&#x099A;,&#x09D7;" />
+            <!-- U+09AB: "ফ" BENGALI LETTER PHA
+                 U+09AB/U+09CD/U+099F: "ফ্ট" BENGALI LETTER PHA/BENGALI SIGN VIRAMA/BENGALI LETTER TTA
+                 U+09AB/U+09CD/U+09AF: "ফ্য" BENGALI LETTER PHA/BENGALI SIGN VIRAMA/BENGALI LETTER YA
+                 U+09AB/U+09CD/U+09B0: "ফ্র" BENGALI LETTER PHA/BENGALI SIGN VIRAMA/BENGALI LETTER RA
+                 U+09AB/U+09CD/U+09B2: "ফ্ল" BENGALI LETTER PHA/BENGALI SIGN VIRAMA/BENGALI LETTER LA -->
+            <Key
+                latin:keySpec="&#x09AB;"
+                latin:moreKeys="&#x09AB;&#x09CD;&#x099F;,&#x09AB;&#x09CD;&#x09AF;,&#x09AB;&#x09CD;&#x09B0;,&#x09AB;&#x09CD;&#x09B2;" />
+            <!-- U+098B: "ঋ" BENGALI LETTER VOCALIC R
+                 U+098C: "ঌ" BENGALI LETTER VOCALIC L
+                 U+09E1: "ৡ" BENGALI LETTER VOCALIC LL
+                 U+09F4: "৴" BENGALI CURRENCY NUMERATOR ONE
+                 U+09F5: "৵" BENGALI CURRENCY NUMERATOR TWO
+                 U+09F6: "৶" BENGALI CURRENCY NUMERATOR THREE
+                 U+09E2: " ৢ" BENGALI VOWEL SIGN VOCALIC L
+                 U+09E3: " ৣ" BENGALI VOWEL SIGN VOCALIC LL -->
+            <Key
+                latin:keySpec="&#x098B;"
+                latin:moreKeys="&#x098C;,&#x09E1;,&#x09F4;,&#x09F5;,&#x09F6;,&#x09E2;,&#x09E3;" />
+        </case>
+        <default>
+            <!-- U+09A7: "ধ" BENGALI LETTER DHA
+                 U+09E7: "১" BENGALI DIGIT ONE
+                 U+09A7/U+09CD/U+09AC: "ধ্ব্র" BENGALI LETTER DHA/BENGALI SIGN VIRAMA/BENGALI LETTER BA
+                 U+09A7/U+09CD/U+09AF: "ধ্য্র" BENGALI LETTER DHA/BENGALI SIGN VIRAMA/BENGALI LETTER YA
+                 U+09A7/U+09CD/U+09B0: "ধ্র" BENGALI LETTER DHA/BENGALI SIGN VIRAMA/BENGALI LETTER RA -->
+            <Key
+                latin:keySpec="&#x09A7;"
+                latin:keyHintLabel="&#x09E7;"
+                latin:additionalMoreKeys="&#x09E7;"
+                latin:moreKeys="&#x09A7;&#x09CD;&#x09AC;,&#x09A7;&#x09CD;&#x09AF;,&#x09A7;&#x09CD;&#x09B0;" />
+            <!-- U+09A5: "থ" BENGALI LETTER THA
+                 U+09E8: "২" BENGALI DIGIT TWO
+                 U+09A5/U+09CD/U+09AF: "থ্য" BENGALI LETTER THA/BENGALI SIGN VIRAMA/BENGALI LETTER YA
+                 U+09A5/U+09CD/U+09B0: "থ্র" BENGALI LETTER THA/BENGALI SIGN VIRAMA/BENGALI LETTER RA -->
+            <Key
+                latin:keySpec="&#x09A5;"
+                latin:keyHintLabel="&#x09E8;"
+                latin:additionalMoreKeys="&#x09E8;"
+                latin:moreKeys="&#x09A5;&#x09CD;&#x09AF;,&#x09A5;&#x09CD;&#x09B0;" />
+            <!-- U+09C7: "ে" BENGALI VOWEL SIGN E
+                 U+09E9: "৩" BENGALI DIGIT THREE
+                 U+098F: "এ" BENGALI LETTER E -->
+            <Key
+                latin:keySpec="&#x09C7;"
+                latin:keyHintLabel="&#x09E9;"
+                latin:additionalMoreKeys="&#x09E9;"
+                latin:moreKeys="&#x098F;" />
+            <!-- U+09B0: "র" BENGALI LETTER RA
+                 U+09EA: "৪" BENGALI DIGIT FOUR -->
+            <Key
+                latin:keySpec="&#x09B0;"
+                latin:keyHintLabel="&#x09EA;"
+                latin:additionalMoreKeys="&#x09EA;" />
+            <!-- U+09A4: "ত" BENGALI LETTER TA
+                 U+09EB: "৫" BENGALI DIGIT FIVE
+                 U+09CE: "ৎ" BENGALI LETTER KHANDA TA
+                 U+09A4/U+09CD/U+09A4: "ত্ত" BENGALI LETTER TA/BENGALI SIGN VIRAMA/BENGALI LETTER TA
+                 U+09A4/U+09CD/U+09A8: "ত্ন" BENGALI LETTER TA/BENGALI SIGN VIRAMA/BENGALI LETTER NA
+                 U+09A4/U+09CD/U+09AC: "ত্ব" BENGALI LETTER TA/BENGALI SIGN VIRAMA/BENGALI LETTER BA
+                 U+09A4/U+09CD/U+09AE: "ত্ম" BENGALI LETTER TA/BENGALI SIGN VIRAMA/BENGALI LETTER MA -->
+            <Key
+                latin:keySpec="&#x09A4;"
+                latin:keyHintLabel="&#x09EB;"
+                latin:additionalMoreKeys="&#x09EB;"
+                latin:moreKeys="&#x09CE;,&#x09A4;&#x09CD;&#x09A4;,&#x09A4;&#x09CD;&#x09A8;,&#x09A4;&#x09CD;&#x09AC;,&#x09A4;&#x09CD;&#x09AE;" />
+            <!-- U+09DF: "য়" BENGALI LETTER YYA
+                 U+09EC: "৬" BENGALI DIGIT SIX -->
+            <Key
+                latin:keySpec="&#x09DF;"
+                latin:keyHintLabel="&#x09EC;"
+                latin:additionalMoreKeys="&#x09EC;" />
+            <!-- U+09C1: "ু" BENGALI VOWEL SIGN U
+                 U+09ED: "৭" BENGALI DIGIT SEVEN
+                 U+0989: "উ" BENGALI LETTER U -->
+            <Key
+                latin:keySpec="&#x09C1;"
+                latin:keyHintLabel="&#x09ED;"
+                latin:additionalMoreKeys="&#x09ED;"
+                latin:moreKeys="&#x0989;" />
+            <!-- U+09BF: "ি" BENGALI VOWEL SIGN I
+                 U+09EE: "৮" BENGALI DIGIT EIGHT
+                 U+0987: "ই BENGALI LETTER I -->
+            <Key
+                latin:keySpec="&#x09BF;"
+                latin:keyHintLabel="&#x09EE;"
+                latin:additionalMoreKeys="&#x09EE;"
+                latin:moreKeys="&#x0987;" />
+            <!-- U+09CB: "ো" BENGALI VOWEL SIGN O
+                 U+09EF: "৯" BENGALI DIGIT NINE
+                 U+0993: "ও" BENGALI LETTER O -->
+            <Key
+                latin:keySpec="&#x09CB;"
+                latin:keyHintLabel="&#x09EF;"
+                latin:additionalMoreKeys="&#x09EF;"
+                latin:moreKeys="&#x0993;" />
+            <!-- U+09AA: "প" BENGALI LETTER PA
+                 U+09E6: "০" BENGALI DIGIT ZERO
+                 U+09AA/U+09CD/U+09A4: "প্ত" BENGALI LETTER PA/BENGALI SIGN VIRAMA/BENGALI LETTER TA
+                 U+09AA/U+09CD/U+09A8: "প্ন" BENGALI LETTER PA/BENGALI SIGN VIRAMA/BENGALI LETTER NA
+                 U+09AA/U+09CD/U+09AA: "প্প" BENGALI LETTER PA/BENGALI SIGN VIRAMA/BENGALI LETTER PA
+                 U+09AA/U+09CD/U+09AF: "প্য" BENGALI LETTER PA/BENGALI SIGN VIRAMA/BENGALI LETTER YA
+                 U+09AA/U+09CD/U+09B0: "প্র" BENGALI LETTER PA/BENGALI SIGN VIRAMA/BENGALI LETTER RA
+                 U+09AA/U+09CD/U+09B2: "প্ল" BENGALI LETTER PA/BENGALI SIGN VIRAMA/BENGALI LETTER LA
+                 U+09AA/U+09CD/U+09B8: "প্স" BENGALI LETTER PA/BENGALI SIGN VIRAMA/BENGALI LETTER SA -->
+            <Key
+                latin:keySpec="&#x09AA;"
+                latin:keyHintLabel="&#x09E6;"
+                latin:additionalMoreKeys="&#x09E6;"
+                latin:moreKeys="&#x09AA;&#x09CD;&#x09A4;,&#x09AA;&#x09CD;&#x09A8;,&#x09AA;&#x09CD;&#x09AA;,&#x09AA;&#x09CD;&#x09AF;,&#x09AA;&#x09CD;&#x09B0;,&#x09AA;&#x09CD;&#x09B2;,&#x09AA;&#x09CD;&#x09B8;" />
+            <!-- U+0986: "আ" BENGALI LETTER AA -->
+            <Key latin:keySpec="&#x0986;" />
+        </default>
+    </switch>
+</merge>
diff --git a/java/res/xml/rowkeys_bengali_akkhor2.xml b/java/res/xml/rowkeys_bengali_akkhor2.xml
new file mode 100644
index 0000000..1dbab55
--- /dev/null
+++ b/java/res/xml/rowkeys_bengali_akkhor2.xml
@@ -0,0 +1,152 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+
+<merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
+    <switch>
+        <case latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted">
+            <!-- U+0985: "অ" BENGALI LETTER A -->
+            <Key latin:keySpec="&#x0985;" />
+            <!-- U+09B6: "শ" BENGALI LETTER SHA
+                 U+09B6/U+09CD/U+099A: "শ্চ" BENGALI LETTER SHA/BENGALI SIGN VIRAMA/BENGALI LETTER CA
+                 U+09B6/U+09CD/U+099B: "শ্ছ" BENGALI LETTER SHA/BENGALI SIGN VIRAMA/BENGALI LETTER CHA
+                 U+09B6/U+09CD/U+09A4: "শ্ত" BENGALI LETTER SHA/BENGALI SIGN VIRAMA/BENGALI LETTER TA
+                 U+09B6/U+09CD/U+09A8: "শ্ন" BENGALI LETTER SHA/BENGALI SIGN VIRAMA/BENGALI LETTER NA
+                 U+09B6/U+09CD/U+09AC: "শ্ব" BENGALI LETTER SHA/BENGALI SIGN VIRAMA/BENGALI LETTER BA
+                 U+09B6/U+09CD/U+09AE: "শ্ম" BENGALI LETTER SHA/BENGALI SIGN VIRAMA/BENGALI LETTER MA
+                 U+09B6/U+09CD/U+09B0: "শ্র" BENGALI LETTER SHA/BENGALI SIGN VIRAMA/BENGALI LETTER RA
+                 U+09B6/U+09CD/U+09B2: "শ্ল" BENGALI LETTER SHA/BENGALI SIGN VIRAMA/BENGALI LETTER LA -->
+            <Key
+                latin:keySpec="&#x09B6;"
+                latin:moreKeys="&#x09B6;&#x09CD;&#x099A;,&#x09B6;&#x09CD;&#x099B;,&#x09B6;&#x09CD;&#x09A4;,&#x09B6;&#x09CD;&#x09A8;,&#x09B6;&#x09CD;&#x09AC;,&#x09B6;&#x09CD;&#x09AE;,&#x09B6;&#x09CD;&#x09B0;,&#x09B6;&#x09CD;&#x09B2;" />
+            <!-- U+09A1: "ড" BENGALI LETTER DDA
+                 U+09A1/U+09CD/U+09A1: "ড্ড" BENGALI LETTER DDA/BENGALI SIGN VIRAMA/BENGALI LETTER DDA-->
+            <Key
+                latin:keySpec="&#x09A1;"
+                latin:moreKeys="&#x09A1;&#x09CD;&#x09A1;" />
+            <!-- U+09DD: "ঢ়" BENGALI LETTER RHA -->
+            <Key latin:keySpec="&#x09DD;" />
+            <!-- U+0998: "ঘ" BENGALI LETTER GHA -->
+            <Key latin:keySpec="&#x0998;" />
+            <!-- U+09B9: "হ" BENGALI LETTER HA
+                 U+09BD: "ঽ" BENGALI SIGN AVAGRAHA
+                 U+09B9/U+09CD/U+09A3: "হ্ণ" BENGALI LETTER HA/BENGALI SIGN VIRAMA/BENGALI LETTER NNA
+                 U+09B9/U+09CD/U+09A8: "হ্ন" BENGALI LETTER HA/BENGALI SIGN VIRAMA/BENGALI LETTER NA
+                 U+09B9/U+09CD/U+09AC: "হ্ব" BENGALI LETTER HA/BENGALI SIGN VIRAMA/BENGALI LETTER BA
+                 U+09B9/U+09CD/U+09AE: "হ্ম" BENGALI LETTER HA/BENGALI SIGN VIRAMA/BENGALI LETTER MA
+                 U+09B9/U+09CD/U+09B0: "হ্র" BENGALI LETTER HA/BENGALI SIGN VIRAMA/BENGALI LETTER RA
+                 U+09B9/U+09CD/U+09B2: "হ্ল" BENGALI LETTER HA/BENGALI SIGN VIRAMA/BENGALI LETTER LA -->
+            <Key
+                latin:keySpec="&#x09B9;"
+                latin:moreKeys="&#x09BD;,&#x09B9;&#x09CD;&#x09A3;,&#x09B9;&#x09CD;&#x09A8;,&#x09B9;&#x09CD;&#x09AC;,&#x09B9;&#x09CD;&#x09AE;,&#x09B9;&#x09CD;&#x09B0;,&#x09B9;&#x09CD;&#x09B2;" />
+            <!-- U+099D: "ঝ" BENGALI LETTER JHA -->
+            <Key latin:keySpec="&#x099D;" />
+            <!-- U+0996: "খ" BENGALI LETTER KHA -->
+            <Key latin:keySpec="&#x0996;" />
+            <!-- U+09CE: "ৎ" BENGALI LETTER KHANDA TA -->
+            <Key latin:keySpec="&#x09CE;" />
+            <!-- U+0988: "ঈ" BENGALI LETTER II -->
+            <Key latin:keySpec="&#x0988;" />
+            <!-- U+098A: "ঊ" BENGALI LETTER UU -->
+            <Key latin:keySpec="&#x098A;" />
+        </case>
+        <default>
+            <!-- U+09BE: "া BENGALI VOWEL SIGN AA
+                 U+0986: "আ" BENGALI LETTER AA -->
+            <Key
+                latin:keySpec="&#x09BE;"
+                latin:moreKeys="&#x0986;" />
+            <!-- U+09B8: "স" BENGALI LETTER SA
+                 U+09B8/U+09CD/U+09AC: "স্ব" BENGALI LETTER SA/BENGALI SIGN VIRAMA/BENGALI LETTER BA
+                 U+09B8/U+09CD/U+09A4: "স্ত" BENGALI LETTER SA/BENGALI SIGN VIRAMA/BENGALI LETTER TA
+                 U+09B8/U+09CD/U+099F: "স্ট" BENGALI LETTER SA/BENGALI SIGN VIRAMA/BENGALI LETTER TTA
+                 U+09B8/U+09CD/U+0995: "স্ক" BENGALI LETTER SA/BENGALI SIGN VIRAMA/BENGALI LETTER KA
+                 U+09B8/U+09CD/U+09AA: "স্প" BENGALI LETTER SA/BENGALI SIGN VIRAMA/BENGALI LETTER PA -->
+            <Key
+                latin:keySpec="&#x09B8;"
+                latin:moreKeys="&#x09B8;&#x09CD;&#x09AC;,&#x09B8;&#x09CD;&#x09A4;,&#x09B8;&#x09CD;&#x099F;,&#x09B8;&#x09CD;&#x0995;,&#x09B8;&#x09CD;&#x09AA;" />
+            <!-- U+09A6: "দ" BENGALI LETTER DA
+                 U+09A6/U+09CD/U+09A6: "দ্দ" BENGALI LETTER DA/BENGALI SIGN VIRAMA/BENGALI LETTER DA
+                 U+09A6/U+09CD/U+09A7: "দ্ধ" BENGALI LETTER DA/BENGALI SIGN VIRAMA/BENGALI LETTER DHA
+                 U+09A6/U+09CD/U+09AC: "দ্ব" BENGALI LETTER DA/BENGALI SIGN VIRAMA/BENGALI LETTER BA
+                 U+09A6/U+09CD/U+09AD: "দ্ভ" BENGALI LETTER DA/BENGALI SIGN VIRAMA/BENGALI LETTER BHA
+                 U+09A6/U+09CD/U+09AE: "দ্ম" BENGALI LETTER DA/BENGALI SIGN VIRAMA/BENGALI LETTER MA
+                 U+09A6/U+09CD/U+09AF: "দ্য" BENGALI LETTER DA/BENGALI SIGN VIRAMA/BENGALI LETTER YA
+                 U+09A6/U+09CD/U+09B0: "দ্র" BENGALI LETTER DA/BENGALI SIGN VIRAMA/BENGALI LETTER RA -->
+            <Key
+                latin:keySpec="&#x09A6;"
+                latin:moreKeys="&#x09A6;&#x09CD;&#x09A6;,&#x09A6;&#x09CD;&#x09A7;,&#x09A6;&#x09CD;&#x09AC;,&#x09A6;&#x09CD;&#x09AD;,&#x09A6;&#x09CD;&#x09AE;,&#x09A6;&#x09CD;&#x09AF;,&#x09A6;&#x09CD;&#x09B0;" />
+            <!-- U+09C3: "ৃ" BENGALI VOWEL SIGN VOCALIC R
+                 U+098B: "ঋ" BENGALI LETTER VOCALIC R -->
+            <Key
+                latin:keySpec="&#x09C3;"
+                latin:moreKeys="&#x098B;" />
+            <!-- U+0997: "গ" BENGALI LETTER GA
+                 U+0997/U+09CD/U+09A7: "গ্ধ" BENGALI LETTER GA/BENGALI SIGN VIRAMA/BENGALI LETTER DHA
+                 U+0997/U+09CD/U+09B0: "গ্র" BENGALI LETTER GA/BENGALI SIGN VIRAMA/BENGALI LETTER RA
+                 U+0997/U+09CD/U+09B2: "গ্ল" BENGALI LETTER GA/BENGALI SIGN VIRAMA/BENGALI LETTER LA
+                 U+0997/U+09CD/U+09A8: "গ্ন" BENGALI LETTER GA/BENGALI SIGN VIRAMA/BENGALI LETTER NA -->
+            <Key
+                latin:keySpec="&#x0997;"
+                latin:moreKeys="&#x0997;&#x09CD;&#x09A7;,&#x0997;&#x09CD;&#x09B0;,&#x0997;&#x09CD;&#x09B2;,&#x0997;&#x09CD;&#x09A8;" />
+            <!-- U+09CD: "্" BENGALI SIGN VIRAMA -->
+            <Key latin:keySpec="&#x09CD;" />
+            <!-- U+099C: "জ" BENGALI LETTER JA
+                 U+099C/U+09CD/U+099E: "জ্ঞ" BENGALI LETTER JA/BENGALI SIGN VIRAMA/BENGALI LETTER NYA
+                 U+099C/U+09CD/U+099C: "জ্জ" BENGALI LETTER JA/BENGALI SIGN VIRAMA/BENGALI LETTER JA
+                 U+099C/U+09CD/U+09AF: "জ্ব" BENGALI LETTER JA/BENGALI SIGN VIRAMA/BENGALI LETTER YA
+                 U+099C/U+09CD/U+09AC: "জ্য" BENGALI LETTER JA/BENGALI SIGN VIRAMA/BENGALI LETTER BA
+                 U+099C/U+09CD/U+09B0: "জ্র" BENGALI LETTER JA/BENGALI SIGN VIRAMA/BENGALI LETTER RA -->
+            <Key
+                latin:keySpec="&#x099C;"
+                latin:moreKeys="&#x099C;&#x09CD;&#x099E;,&#x099C;&#x09CD;&#x099C;,&#x099C;&#x09CD;&#x09AF;,&#x099C;&#x09CD;&#x09AC;,&#x099C;&#x09CD;&#x09B0;" />
+            <!-- U+0995: "ক" BENGALI LETTER KA
+                 U+0995/U+09CD/U+09B7: "ক্ষ" BENGALI LETTER KA/BENGALI SIGN VIRAMA/BENGALI LETTER SSA
+                 U+0995/U+09CD/U+0995: "ক্ক" BENGALI LETTER KA/BENGALI SIGN VIRAMA/BENGALI LETTER KA
+                 U+0995/U+09CD/U+099F: "ক্ট" BENGALI LETTER KA/BENGALI SIGN VIRAMA/BENGALI LETTER TTA
+                 U+0995/U+09CD/U+09A4: "ক্ত" BENGALI LETTER KA/BENGALI SIGN VIRAMA/BENGALI LETTER TA
+                 U+0995/U+09CD/U+09B0: "ক্র" BENGALI LETTER KA/BENGALI SIGN VIRAMA/BENGALI LETTER RA
+                 U+0995/U+09CD/U+09B8: "ক্স" BENGALI LETTER KA/BENGALI SIGN VIRAMA/BENGALI LETTER SA
+                 U+0995/U+09CD/U+09B2: "ক্ল" BENGALI LETTER KA/BENGALI SIGN VIRAMA/BENGALI LETTER LA -->
+            <Key
+                latin:keySpec="&#x0995;"
+                latin:moreKeys="&#x0995;&#x09CD;&#x09B7;,&#x0995;&#x09CD;&#x0995;,&#x0995;&#x09CD;&#x099F;,&#x0995;&#x09CD;&#x09A4;,&#x0995;&#x09CD;&#x09B0;,&#x0995;&#x09CD;&#x09B8;,&#x0995;&#x09CD;&#x09B2;" />
+            <!-- U+09B2: "ল" BENGALI LETTER LA
+                 U+09B2/U+09CD/U+0995: "ল্ক" BENGALI LETTER LA/BENGALI SIGN VIRAMA/BENGALI LETTER KA
+                 U+09B2/U+09CD/U+0997: "ল্গ" BENGALI LETTER LA/BENGALI SIGN VIRAMA/BENGALI LETTER GA
+                 U+09B2/U+09CD/U+099F: "ল্ট" BENGALI LETTER LA/BENGALI SIGN VIRAMA/BENGALI LETTER TTA
+                 U+09B2/U+09CD/U+09A1: "ল্ড" BENGALI LETTER LA/BENGALI SIGN VIRAMA/BENGALI LETTER DDA
+                 U+09B2/U+09CD/U+09A4: "ল্ত" BENGALI LETTER LA/BENGALI SIGN VIRAMA/BENGALI LETTER TA
+                 U+09B2/U+09CD/U+09A6: "ল্দ" BENGALI LETTER LA/BENGALI SIGN VIRAMA/BENGALI LETTER DA
+                 U+09B2/U+09CD/U+09A7: "ল্ধ" BENGALI LETTER LA/BENGALI SIGN VIRAMA/BENGALI LETTER DHA
+                 U+09B2/U+09CD/U+09AA: "ল্প" BENGALI LETTER LA/BENGALI SIGN VIRAMA/BENGALI LETTER PA
+                 U+09B2/U+09CD/U+09AB: "ল্ফ" BENGALI LETTER LA/BENGALI SIGN VIRAMA/BENGALI LETTER PHA
+                 U+09B2/U+09CD/U+09AC: "ল্ব" BENGALI LETTER LA/BENGALI SIGN VIRAMA/BENGALI LETTER BA
+                 U+09B2/U+09CD/U+09AE: "ল্ম" BENGALI LETTER LA/BENGALI SIGN VIRAMA/BENGALI LETTER MA
+                 U+09B2/U+09CD/U+09B2: "ল্ল" BENGALI LETTER LA/BENGALI SIGN VIRAMA/BENGALI LETTER LA -->
+            <Key
+                latin:keySpec="&#x09B2;"
+                latin:moreKeys="&#x09B2;&#x09CD;&#x0995;,&#x09B2;&#x09CD;&#x0997;,&#x09B2;&#x09CD;&#x099F;,&#x09B2;&#x09CD;&#x09A1;,&#x09B2;&#x09CD;&#x09A4;,&#x09B2;&#x09CD;&#x09A6;,&#x09B2;&#x09CD;&#x09A7;,&#x09B2;&#x09CD;&#x09AA;,&#x09B2;&#x09CD;&#x09AB;,&#x09B2;&#x09CD;&#x09AC;,&#x09B2;&#x09CD;&#x09AE;,&#x09B2;&#x09CD;&#x09B2;" />
+            <!-- U+0987: "ই" BENGALI LETTER I -->
+            <Key latin:keySpec="&#x0987;" />
+            <!-- U+0989: "উ" BENGALI LETTER U -->
+            <Key latin:keySpec="&#x0989;" />
+        </default>
+    </switch>
+</merge>
diff --git a/java/res/xml/rowkeys_bengali_akkhor3.xml b/java/res/xml/rowkeys_bengali_akkhor3.xml
new file mode 100644
index 0000000..eb9e836
--- /dev/null
+++ b/java/res/xml/rowkeys_bengali_akkhor3.xml
@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+
+<merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
+    <switch>
+        <case latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted">
+            <!-- U+0964: "।" DEVANAGARI DANDA
+                 U+0965: "॥" DEVANAGARI DOUBLE DANDA -->
+            <Key
+                 latin:keySpec="&#x0964;"
+                 latin:moreKeys="&#x0965;" />
+            <!-- U+0999: "ঙ BENGALI LETTER NGA
+                 U+0999/U+09CD/U+0995: "ঙ্ক"
+                 U+0999/U+09CD/U+0996: "ঙ্খ"
+                 U+0999/U+09CD/U+0997: "ঙ্গ" -->
+            <Key latin:keySpec="&#x0999;"
+                 latin:moreKeys="&#x0999;&#x09CD;&#x0995;,&#x0999;&#x09CD;&#x0996;,&#x0999;&#x09CD;&#x0997;" />
+            <!-- U+099B: "ছ" BENGALI LETTER CHA -->
+            <Key latin:keySpec="&#x099B;" />
+            <!-- U+0983: "ঃ" BENGALI SIGN VISARGA -->
+            <Key latin:keySpec="&#x0983;" />
+            <!-- U+0981: "ঁ" BENGALI SIGN CANDRABINDU -->
+            <Key latin:keySpec="&#x0981;" />
+            <!-- U+09A3: "ণ" BENGALI LETTER NNA
+                 U+09A3/U+09CD/U+099F: "ণ্ট" BENGALI LETTER NNA/BENGALI SIGN VIRAMA/BENGALI LETTER TTA
+                 U+09A3/U+09CD/U+09A1: "ণ্ড" BENGALI LETTER NNA/BENGALI SIGN VIRAMA/BENGALI LETTER DDA
+                 U+09A3/U+09CD/U+09A3: "ণ্ণ" BENGALI LETTER NNA/BENGALI SIGN VIRAMA/BENGALI LETTER NNA -->
+            <Key latin:keySpec="&#x09A3;"
+                 latin:moreKeys="&#x09A3;&#x09CD;&#x099F;,&#x09A3;&#x09CD;&#x09A1;,&#x09A3;&#x09CD;&#x09A3;" />
+            <!-- U+0982: "ং" BENGALI SIGN ANUSVARA -->
+            <Key latin:keySpec="&#x0982;" />
+            <!-- U+0990: "ঐ" BENGALI LETTER AI -->
+            <Key latin:keySpec="&#x0990;" />
+            <!-- U+0994: "ঔ" BENGALI LETTER AU -->
+            <Key latin:keySpec="&#x0994;" />
+        </case>
+        <default>
+            <!-- U+09AF: "য" BENGALI LETTER YA
+                 U+09CD/U+09AF: "্য" BENGALI SIGN VIRAMA/BENGALI LETTER YA -->
+            <Key
+                 latin:keySpec="&#x09AF;"
+                 latin:moreKeys="&#x09CD;&#x09AF;" />
+            <!-- U+09B7: "ষ" BENGALI LETTER SSA
+                 U+09B7/U+09CD/U+0995: "ষ্ক" BENGALI LETTER SSA/BENGALI SIGN VIRAMA/BENGALI LETTER KA
+                 U+09B7/U+09CD/U+099F: "ষ্ট" BENGALI LETTER SSA/BENGALI SIGN VIRAMA/BENGALI LETTER TTA
+                 U+09B7/U+09CD/U+09A0: "ষ্ঠ" BENGALI LETTER SSA/BENGALI SIGN VIRAMA/BENGALI LETTER TTHA
+                 U+09B7/U+09CD/U+09A3: "ষ্ণ" BENGALI LETTER SSA/BENGALI SIGN VIRAMA/BENGALI LETTER NNA
+                 U+09B7/U+09CD/U+09AA: "ষ্প" BENGALI LETTER SSA/BENGALI SIGN VIRAMA/BENGALI LETTER PA
+                 U+09B7/U+09CD/U+09AB: "ষ্ফ" BENGALI LETTER SSA/BENGALI SIGN VIRAMA/BENGALI LETTER PHA
+                 U+09B7/U+09CD/U+09AE: "ষ্ম" BENGALI LETTER SSA/BENGALI SIGN VIRAMA/BENGALI LETTER MA -->
+            <Key
+                 latin:keySpec="&#x09B7;"
+                 latin:moreKeys="&#x09B7;&#x09CD;&#x0995;,&#x09B7;&#x09CD;&#x099F;,&#x09B7;&#x09CD;&#x09A0;,&#x09B7;&#x09CD;&#x09A3;,&#x09B7;&#x09CD;&#x09AA;,&#x09B7;&#x09CD;&#x09AB;,&#x09B7;&#x09CD;&#x09AE;" />
+            <!-- U+099A: "চ" BENGALI LETTER CA
+                 U+099A/U+09CD/U+099A: "চ্চ" BENGALI LETTER CA/BENGALI SIGN VIRAMA/BENGALI LETTER CA
+                 U+099A/U+09CD/U+099B: "চ্ছ" BENGALI LETTER CA/BENGALI SIGN VIRAMA/BENGALI LETTER CHA -->
+            <Key
+                 latin:keySpec="&#x099A;"
+                 latin:moreKeys="&#x099A;&#x09CD;&#x099A;,&#x099A;&#x09CD;&#x099B;" />
+            <!-- U+09AD: "ভ" BENGALI LETTER BHA
+                 U+09AD/U+09CD/U+09AF: "ভ্" BENGALI LETTER BHA/BENGALI SIGN VIRAMA/BENGALI LETTER YA
+                 U+09AD/U+09CD/U+09B0: "ভ্র" BENGALI LETTER BHA/BENGALI SIGN VIRAMA/BENGALI LETTER RA
+                 U+09AD/U+09CD/U+09B2: "ভ্ল" BENGALI LETTER BHA/BENGALI SIGN VIRAMA/BENGALI LETTER LA -->
+            <Key
+                 latin:keySpec="&#x09AD;"
+                 latin:moreKeys="&#x09AD;&#x09CD;&#x09AF;,&#x09AD;&#x09CD;&#x09B0;,&#x09AD;&#x09CD;&#x09B2;" />
+            <!-- U+09AC: "ব" BENGALI LETTER BA
+                 U+09CD/U+09AC: "্ব" BENGALI SIGN VIRAMA/BENGALI LETTER BA
+                 U+09AC/U+09CD/U+09B0: "ব্র" BENGALI LETTER BA/BENGALI SIGN VIRAMA/BENGALI LETTER RA
+                 U+09AC/U+09CD/U+099C: "ব্জ" BENGALI LETTER BA/BENGALI SIGN VIRAMA/BENGALI LETTER JA
+                 U+09AC/U+09CD/U+09A6: "ব্দ" BENGALI LETTER BA/BENGALI SIGN VIRAMA/BENGALI LETTER DA
+                 U+09AC/U+09CD/U+09A7: "ব্ধ" BENGALI LETTER BA/BENGALI SIGN VIRAMA/BENGALI LETTER DHA
+                 U+09AC/U+09CD/U+09AC: "ব্ব" BENGALI LETTER BA/BENGALI SIGN VIRAMA/BENGALI LETTER BA
+                 U+09AC/U+09CD/U+09B2: "ব্ল" BENGALI LETTER BA/BENGALI SIGN VIRAMA/BENGALI LETTER LA
+                 U+09F1: "ৱ" BENGALI LETTER RA WITH MIDDLE DIAGONAL
+                 U+09F0: "ৰ" BENGALI LETTER RA WITH LOWER DIAGONAL -->
+            <Key
+                 latin:keySpec="&#x09AC;"
+                 latin:moreKeys="&#x09CD;&#x09AC;,&#x09AC;&#x09CD;&#x09B0;,&#x09AC;&#x09CD;&#x099C;,&#x09AC;&#x09CD;&#x09A6;,&#x09AC;&#x09CD;&#x09A7;,&#x09AC;&#x09CD;&#x09AC;,&#x09AC;&#x09CD;&#x09B2;,&#x09F1;,&#x09F0;" />
+            <!-- U+09A8: "ন" BENGALI LETTER NA
+                 U+09A8/U+09CD/U+09A4: "ন্ত" BENGALI LETTER NA/BENGALI SIGN VIRAMA/BENGALI LETTER TA
+                 U+09A8/U+09CD/U+09A5: "ন্থ" BENGALI LETTER NA/BENGALI SIGN VIRAMA/BENGALI LETTER THA
+                 U+09A8/U+09CD/U+099F: "ন্ট" BENGALI LETTER NA/BENGALI SIGN VIRAMA/BENGALI LETTER TTA
+                 U+09A8/U+09CD/U+09A6: "ন্দ" BENGALI LETTER NA/BENGALI SIGN VIRAMA/BENGALI LETTER DA
+                 U+09A8/U+09CD/U+09A7: "ন্ধ" BENGALI LETTER NA/BENGALI SIGN VIRAMA/BENGALI LETTER DHA
+                 U+09A8/U+09CD/U+09A1: "ন্ড" BENGALI LETTER NA/BENGALI SIGN VIRAMA/BENGALI LETTER DDA
+                 U+09A8/U+09CD/U+09A8: "ন্ন" BENGALI LETTER NA/BENGALI SIGN VIRAMA/BENGALI LETTER NA
+                 U+09A8/U+09CD/U+09AC: "ন্ব" BENGALI LETTER NA/BENGALI SIGN VIRAMA/BENGALI LETTER BA
+                 U+09A8/U+09CD/U+09AE: "ন্ম" BENGALI LETTER NA/BENGALI SIGN VIRAMA/BENGALI LETTER MA
+                 U+09A8/U+09CD/U+09B8: "ন্স" BENGALI LETTER NA/BENGALI SIGN VIRAMA/BENGALI LETTER SA -->
+            <Key
+                 latin:keySpec="&#x09A8;"
+                 latin:moreKeys="&#x09A8;&#x09CD;&#x09A4;,&#x09A8;&#x09CD;&#x09A5;,&#x09A8;&#x09CD;&#x099F;,&#x09A8;&#x09CD;&#x09A6;,&#x09A8;&#x09CD;&#x09A7;,&#x09A8;&#x09CD;&#x09A1;,&#x09A8;&#x09CD;&#x09A8;,&#x09A8;&#x09CD;&#x09AC;,&#x09A8;&#x09CD;&#x09AE;,&#x09A8;&#x09CD;&#x09B8;" />
+            <!-- U+09AE: "ম" BENGALI LETTER MA
+                 U+09AE/U+09CD/U+09A8: "ম্ন" BENGALI LETTER MA/BENGALI SIGN VIRAMA/BENGALI LETTER NA
+                 U+09AE/U+09CD/U+09AA: "ম্প" BENGALI LETTER MA/BENGALI SIGN VIRAMA/BENGALI LETTER PA
+                 U+09AE/U+09CD/U+09AC: "ম্ব" BENGALI LETTER MA/BENGALI SIGN VIRAMA/BENGALI LETTER BA
+                 U+09AE/U+09CD/U+09AD: "ম্ভ" BENGALI LETTER MA/BENGALI SIGN VIRAMA/BENGALI LETTER BHA
+                 U+09AE/U+09CD/U+09AE: "ম্ম" BENGALI LETTER MA/BENGALI SIGN VIRAMA/BENGALI LETTER MA
+                 U+09AE/U+09CD/U+09B0: "ম্র" BENGALI LETTER MA/BENGALI SIGN VIRAMA/BENGALI LETTER RA
+                 U+09AE/U+09CD/U+09B2: "ম্ল" BENGALI LETTER MA/BENGALI SIGN VIRAMA/BENGALI LETTER LA -->
+            <Key
+                 latin:keySpec="&#x09AE;"
+                 latin:moreKeys=",&#x09AE;&#x09CD;&#x09A8;,&#x09AE;&#x09CD;&#x09AA;,&#x09AE;&#x09CD;&#x09AC;,&#x09AE;&#x09CD;&#x09AD;,&#x09AE;&#x09CD;&#x09AE;,&#x09AE;&#x09CD;&#x09B0;,&#x09AE;&#x09CD;&#x09B2;" />
+            <!-- U+098F: "এ" BENGALI LETTER E -->
+            <Key latin:keySpec="&#x098F;" />
+            <!-- U+0993: "ও" BENGALI LETTER O -->
+            <Key latin:keySpec="&#x0993;" />
+        </default>
+    </switch>
+</merge>
diff --git a/java/res/xml/rowkeys_nepali_romanized3.xml b/java/res/xml/rowkeys_nepali_romanized3.xml
index 24f5908..c6a1e28 100644
--- a/java/res/xml/rowkeys_nepali_romanized3.xml
+++ b/java/res/xml/rowkeys_nepali_romanized3.xml
@@ -43,11 +43,6 @@
             <Key latin:keyStyle="baseKeyDevanagariSignAnusvara" />
             <!-- U+0919: "ङ" DEVANAGARI LETTER NGA -->
             <Key latin:keySpec="&#x0919;" />
-            <!-- Because the font rendering system prior to API version 16 can't automatically
-                 render dotted circle for incomplete combining letter of some scripts, different
-                 set of Key definitions are needed based on the API version. -->
-            <include latin:keyboardLayout="@xml/keystyle_devanagari_sign_virama" />
-            <Key latin:keyStyle="baseKeyDevanagariSignVirama" />
         </case>
         <default>
             <!-- U+0937: "ष" DEVANAGARI LETTER SSA -->
@@ -64,16 +59,14 @@
             <Key latin:keySpec="&#x0928;" />
             <!-- U+092E: "म" DEVANAGARI LETTER MA -->
             <Key latin:keySpec="&#x092E;" />
-            <!-- U+0964: "।" DEVANAGARI DANDA
-                 U+093D: "ऽ" DEVANAGARI SIGN AVAGRAHA -->
-            <Key
-                latin:keySpec="&#x0964;"
-                latin:moreKeys="&#x093D;" />
             <!-- Because the font rendering system prior to API version 16 can't automatically
                  render dotted circle for incomplete combining letter of some scripts, different
                  set of Key definitions are needed based on the API version. -->
             <include latin:keyboardLayout="@xml/keystyle_devanagari_sign_virama" />
-            <Key latin:keyStyle="baseKeyDevanagariSignVirama" />
+            <!-- U+093D: "ऽ" DEVANAGARI SIGN AVAGRAHA -->
+            <Key
+                latin:keyStyle="baseKeyDevanagariSignVirama"
+                latin:moreKeys="&#x093D;" />
         </default>
     </switch>
 </merge>
diff --git a/java/res/xml/rowkeys_nepali_traditional3_right5.xml b/java/res/xml/rowkeys_nepali_traditional3.xml
similarity index 62%
rename from java/res/xml/rowkeys_nepali_traditional3_right5.xml
rename to java/res/xml/rowkeys_nepali_traditional3.xml
index c7c73a4..c43c593 100644
--- a/java/res/xml/rowkeys_nepali_traditional3_right5.xml
+++ b/java/res/xml/rowkeys_nepali_traditional3.xml
@@ -21,6 +21,27 @@
 <merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
     <switch>
         <case latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted">
+            <!-- U+0915/U+094D: "क्" DEVANAGARI LETTER KA/DEVANAGARI SIGN VIRAMA -->
+            <Key
+                latin:keySpec="&#x0915;&#x094D;"
+                latin:keyLabelFlags="followKeyLetterRatio" />
+            <!-- U+0939/U+094D/U+092E: "ह्म" DEVANAGARI LETTER HA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER MA -->
+            <Key
+                latin:keySpec="&#x0939;&#x094D;&#x092E;"
+                latin:keyLabelFlags="followKeyLetterRatio" />
+            <!-- U+090B: "ऋ" DEVANAGARI LETTER VOCALIC R -->
+            <Key latin:keySpec="&#x090B;" />
+            <!-- U+0950: "ॐ" DEVANAGARI OM -->
+            <Key latin:keySpec="&#x0950;" />
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <include latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_au" />
+            <Key latin:keyStyle="baseKeyDevanagariVowelSignAu" />
+            <!-- U+0926/U+094D/U+092F: "द्य" DEVANAGARI LETTER DA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER YA -->
+            <Key
+                latin:keySpec="&#x0926;&#x094D;&#x092F;"
+                latin:keyLabelFlags="followKeyLetterRatio" />
             <!-- Because the font rendering system prior to API version 16 can't automatically
                  render dotted circle for incomplete combining letter of some scripts, different
                  set of Key definitions are needed based on the API version. -->
@@ -33,44 +54,36 @@
                  set of Key definitions are needed based on the API version. -->
             <include latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_ai" />
             <Key latin:keyStyle="baseKeyDevanagariVowelSignAi" />
-            <!-- U+0930/U+0941: "रु" DEVANAGARI LETTER RA/DEVANAGARI VOWEL SIGN U -->
-            <Key
-                latin:keySpec="&#x0930;&#x0941;"
-                latin:moreKeys="!"
-                latin:keyLabelFlags="followKeyLetterRatio" />
-            <Key
-                latin:keySpec="\?"
-                latin:keyLabelFlags="fontDefault" />
         </case>
         <default>
-            <!-- Because the font rendering system prior to API version 16 can't automatically
-                 render dotted circle for incomplete combining letter of some scripts, different
-                 set of Key definitions are needed based on the API version. -->
-            <include latin:keyboardLayout="@xml/keystyle_devanagari_sign_visarga" />
-            <!-- U+093D: "ऽ" DEVANAGARI SIGN AVAGRAHA -->
-            <Key
-                latin:keyStyle="baseKeyDevanagariSignVisarga"
-                latin:moreKeys="&#x093D;" />
+            <!-- U+0936: "श" DEVANAGARI LETTER SHA -->
+            <Key latin:keySpec="&#x0936;" />
+            <!-- U+0939: "ह" DEVANAGARI LETTER HA -->
+            <Key latin:keySpec="&#x0939;" />
+            <!-- U+0905: "अ" DEVANAGARI LETTER A -->
+            <Key latin:keySpec="&#x0905;" />
+            <!-- U+0916: "ख" DEVANAGARI LETTER KHA -->
+            <Key latin:keySpec="&#x0916;" />
+            <!-- U+0926: "द" DEVANAGARI LETTER DA -->
+            <Key latin:keySpec="&#x0926;" />
+            <!-- U+0932: "ल" DEVANAGARI LETTER LA -->
+            <Key latin:keySpec="&#x0932;" />
             <!-- Because the font rendering system prior to API version 16 can't automatically
                  render dotted circle for incomplete combining letter of some scripts, different
                  set of Key definitions are needed based on the API version. -->
             <include latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_e" />
-            <!-- Override more keys with empty definition -->
-            <key-style latin:styleName="moreKeysDevanagariVowelSignE" />
             <Key latin:keyStyle="baseKeyDevanagariVowelSignE" />
-            <!-- U+0964: "।" DEVANAGARI DANDA -->
-            <Key latin:keySpec="&#x0964;" />
-            <!-- U+0930: "र" DEVANAGARI LETTER RA -->
-            <Key
-                latin:keySpec="&#x0930;"
-                latin:moreKeys="!" />
             <!-- Because the font rendering system prior to API version 16 can't automatically
                  render dotted circle for incomplete combining letter of some scripts, different
                  set of Key definitions are needed based on the API version. -->
-            <include latin:keyboardLayout="@xml/keystyle_devanagari_sign_virama" />
+            <include
+                latin:keyboardLayout="@xml/keystyle_devanagari_sign_virama" />
+            <Key latin:keyStyle="baseKeyDevanagariSignVirama" />
+            <!-- U+0930: "र" DEVANAGARI LETTER RA
+                 U+0930/U+0941: "रु" DEVANAGARI LETTER RA/DEVANAGARI VOWEL SIGN U -->
             <Key
-                latin:keyStyle="baseKeyDevanagariSignVirama"
-                latin:moreKeys="\?" />
+                latin:keySpec="&#x0930;"
+                latin:moreKeys="&#x0930;&#x0941;" />
         </default>
     </switch>
 </merge>
diff --git a/java/res/xml/rowkeys_nepali_traditional3_left6.xml b/java/res/xml/rowkeys_nepali_traditional3_left6.xml
deleted file mode 100644
index ade2787..0000000
--- a/java/res/xml/rowkeys_nepali_traditional3_left6.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2013, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
-    <switch>
-        <case latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted">
-            <!-- U+0915/U+094D: "क्" DEVANAGARI LETTER KA/DEVANAGARI SIGN VIRAMA -->
-            <Key
-                latin:keySpec="&#x0915;&#x094D;"
-                latin:keyLabelFlags="followKeyLetterRatio" />
-            <!-- U+0939/U+094D/U+092E: "ह्म" DEVANAGARI LETTER HA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER MA -->
-            <Key
-                latin:keySpec="&#x0939;&#x094D;&#x092E;"
-                latin:keyLabelFlags="followKeyLetterRatio" />
-            <!-- U+090B: "ऋ" DEVANAGARI LETTER VOCALIC R -->
-            <Key latin:keySpec="&#x090B;" />
-            <!-- U+0950: "ॐ" DEVANAGARI OM -->
-            <Key latin:keySpec="&#x0950;" />
-            <!-- Because the font rendering system prior to API version 16 can't automatically
-                 render dotted circle for incomplete combining letter of some scripts, different
-                 set of Key definitions are needed based on the API version. -->
-            <include latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_au" />
-            <Key latin:keyStyle="baseKeyDevanagariVowelSignAu" />
-            <!-- U+0926/U+094D/U+092F: "द्य" DEVANAGARI LETTER DA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER YA -->
-            <Key
-                latin:keySpec="&#x0926;&#x094D;&#x092F;"
-                latin:keyLabelFlags="followKeyLetterRatio" />
-        </case>
-        <default>
-            <!-- U+0936: "श" DEVANAGARI LETTER SHA -->
-            <Key latin:keySpec="&#x0936;" />
-            <!-- U+0939: "ह" DEVANAGARI LETTER HA -->
-            <Key latin:keySpec="&#x0939;" />
-            <!-- U+0905: "अ" DEVANAGARI LETTER A -->
-            <Key latin:keySpec="&#x0905;" />
-            <!-- U+0916: "ख" DEVANAGARI LETTER KHA -->
-            <Key latin:keySpec="&#x0916;" />
-            <!-- U+0926: "द" DEVANAGARI LETTER DA -->
-            <Key latin:keySpec="&#x0926;" />
-            <!-- U+0932: "ल" DEVANAGARI LETTER LA -->
-            <Key latin:keySpec="&#x0932;" />
-        </default>
-    </switch>
-</merge>
diff --git a/java/res/xml/rowkeys_nepali_traditional3_right3.xml b/java/res/xml/rowkeys_nepali_traditional3_right3.xml
deleted file mode 100644
index 4db438d..0000000
--- a/java/res/xml/rowkeys_nepali_traditional3_right3.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2013, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
-    <switch>
-        <case latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted">
-            <!-- Because the font rendering system prior to API version 16 can't automatically
-                 render dotted circle for incomplete combining letter of some scripts, different
-                 set of Key definitions are needed based on the API version. -->
-            <include latin:keyboardLayout="@xml/keystyle_devanagari_sign_anusvara" />
-            <Key latin:keyStyle="baseKeyDevanagariSignAnusvara" />
-            <!-- U+0919: "ङ" DEVANAGARI LETTER NGA -->
-            <Key latin:keySpec="&#x0919;" />
-            <!-- Because the font rendering system prior to API version 16 can't automatically
-                 render dotted circle for incomplete combining letter of some scripts, different
-                 set of Key definitions are needed based on the API version. -->
-            <include latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_ai" />
-            <Key latin:keyStyle="baseKeyDevanagariVowelSignAi" />
-        </case>
-        <default>
-            <!-- Because the font rendering system prior to API version 16 can't automatically
-                 render dotted circle for incomplete combining letter of some scripts, different
-                 set of Key definitions are needed based on the API version. -->
-            <include latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_e" />
-            <Key latin:keyStyle="baseKeyDevanagariVowelSignE" />
-            <!-- U+0964: "।" DEVANAGARI DANDA -->
-            <Key latin:keySpec="&#x0964;" />
-            <!-- U+0930: "र" DEVANAGARI LETTER RA
-                 U+0930/U+0941: "रु" DEVANAGARI LETTER RA/DEVANAGARI VOWEL SIGN U -->
-            <Key
-                latin:keySpec="&#x0930;"
-                latin:moreKeys="&#x0930;&#x0941;" />
-        </default>
-    </switch>
-</merge>
diff --git a/java/res/xml/rowkeys_symbols2.xml b/java/res/xml/rowkeys_symbols2.xml
index 8119158..e55b1b8 100644
--- a/java/res/xml/rowkeys_symbols2.xml
+++ b/java/res/xml/rowkeys_symbols2.xml
@@ -44,7 +44,8 @@
         </default>
     </switch>
     <Key
-        latin:keyStyle="currencyKeyStyle" />
+        latin:keyStyle="currencyKeyStyle"
+        latin:keyLabelFlags="followKeyLetterRatio" />
     <Key
         latin:keySpec="!text/keyspec_symbols_percent"
         latin:moreKeys="!text/morekeys_symbols_percent" />
diff --git a/java/res/xml/rowkeys_uzbek1.xml b/java/res/xml/rowkeys_uzbek1.xml
new file mode 100644
index 0000000..967e9a7
--- /dev/null
+++ b/java/res/xml/rowkeys_uzbek1.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+
+<merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" >
+    <include latin:keyboardLayout="@xml/rowkeys_qwerty1" />
+    <!-- U+006F/U+02BB: "oʻ" LATIN SMALL LETTER O/MODIFIER LETTER TURNED COMMA -->
+    <Key
+        latin:keySpec="o&#x02BB;"
+        latin:keyLabelFlags="followKeyLetterRatio" />
+</merge>
\ No newline at end of file
diff --git a/java/res/xml/rowkeys_uzbek2.xml b/java/res/xml/rowkeys_uzbek2.xml
new file mode 100644
index 0000000..b768a12
--- /dev/null
+++ b/java/res/xml/rowkeys_uzbek2.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+
+<merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" >
+    <include latin:keyboardLayout="@xml/rowkeys_qwerty2" />
+    <!-- U+0067/U+02BB: "gʻ" LATIN SMALL LETTER G/MODIFIER LETTER TURNED COMMA -->
+    <Key
+        latin:keySpec="g&#x02BB;"
+        latin:keyLabelFlags="followKeyLetterRatio" />
+    <!-- U+02BC: "ʼ" MODIFIER LETTER APOSTROPHE -->
+    <Key latin:keySpec="&#x02BC;" />
+</merge>
\ No newline at end of file
diff --git a/java/res/xml/rows_bengali_akkhor.xml b/java/res/xml/rows_bengali_akkhor.xml
new file mode 100644
index 0000000..1c7e772
--- /dev/null
+++ b/java/res/xml/rows_bengali_akkhor.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+
+<merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" >
+    <include latin:keyboardLayout="@xml/key_styles_common" />
+    <Row
+        latin:keyLabelFlags="fontNormal"
+        latin:keyWidth="9.091%p" >
+        <include latin:keyboardLayout="@xml/rowkeys_bengali_akkhor1" />
+    </Row>
+    <Row
+        latin:keyLabelFlags="fontNormal"
+        latin:keyWidth="9.091%p" >
+        <include latin:keyboardLayout="@xml/rowkeys_bengali_akkhor2" />
+    </Row>
+    <Row
+        latin:keyLabelFlags="fontNormal"
+        latin:keyWidth="8.711%p" >
+        <Key
+            latin:keyStyle="shiftKeyStyle"
+            latin:keyWidth="10.8%p" />
+        <include latin:keyboardLayout="@xml/rowkeys_bengali_akkhor3" />
+        <Key
+            latin:keyStyle="deleteKeyStyle"
+            latin:keyWidth="fillRight" />
+    </Row>
+    <include latin:keyboardLayout="@xml/row_qwerty4" />
+</merge>
diff --git a/java/res/xml/rows_nepali_romanized.xml b/java/res/xml/rows_nepali_romanized.xml
index daca3ee..26737ec 100644
--- a/java/res/xml/rows_nepali_romanized.xml
+++ b/java/res/xml/rows_nepali_romanized.xml
@@ -33,16 +33,18 @@
         <include latin:keyboardLayout="@xml/rowkeys_nepali_romanized2" />
     </Row>
     <Row
-        latin:keyWidth="8.711%p"
+        latin:keyWidth="8.75%p"
         latin:keyLabelFlags="fontNormal"
     >
         <Key
             latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="10.8%p" />
+            latin:keyWidth="15%p"
+            latin:visualInsetsRight="1%p" />
         <include latin:keyboardLayout="@xml/rowkeys_nepali_romanized3" />
         <Key
             latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight" />
+            latin:keyWidth="fillRight"
+            latin:visualInsetsLeft="1%p" />
     </Row>
     <include latin:keyboardLayout="@xml/row_qwerty4" />
 </merge>
diff --git a/java/res/xml/rows_nepali_traditional.xml b/java/res/xml/rows_nepali_traditional.xml
index edcc73a..e2e710c 100644
--- a/java/res/xml/rows_nepali_traditional.xml
+++ b/java/res/xml/rows_nepali_traditional.xml
@@ -40,8 +40,7 @@
         <Key
             latin:keyStyle="shiftKeyStyle"
             latin:keyWidth="10.8%p" />
-        <include latin:keyboardLayout="@xml/rowkeys_nepali_traditional3_left6" />
-        <include latin:keyboardLayout="@xml/rowkeys_nepali_traditional3_right3" />
+        <include latin:keyboardLayout="@xml/rowkeys_nepali_traditional3" />
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyWidth="fillRight" />
diff --git a/java/res/xml/rows_uzbek.xml b/java/res/xml/rows_uzbek.xml
new file mode 100644
index 0000000..ae655d8
--- /dev/null
+++ b/java/res/xml/rows_uzbek.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+
+<merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" >
+    <include latin:keyboardLayout="@xml/key_styles_common" />
+    <Row latin:keyWidth="9.091%p" >
+        <include latin:keyboardLayout="@xml/rowkeys_uzbek1" />
+    </Row>
+    <Row latin:keyWidth="9.091%p" >
+        <include latin:keyboardLayout="@xml/rowkeys_uzbek2" />
+    </Row>
+    <Row latin:keyWidth="9.2%p" >
+        <Key
+            latin:keyStyle="shiftKeyStyle"
+            latin:keyWidth="15%p"
+            latin:visualInsetsRight="1%p" />
+        <Spacer latin:keyWidth="2.8%p" />
+        <include latin:keyboardLayout="@xml/rowkeys_qwerty3" />
+        <Key
+            latin:keyStyle="deleteKeyStyle"
+            latin:keyWidth="fillRight"
+            latin:keyXPos="-15%p"
+            latin:visualInsetsLeft="1%p" />
+    </Row>
+    <include latin:keyboardLayout="@xml/row_qwerty4" />
+</merge>
\ No newline at end of file
diff --git a/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java b/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java
index c937eee..5af3179 100644
--- a/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java
+++ b/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java
@@ -84,8 +84,8 @@
     }
 
     @UsedForTesting
-    public static boolean isAvailable() {
-        return sCursorAnchorInfoClass.exists();
+    public boolean isAvailable() {
+        return sCursorAnchorInfoClass.exists() && mInstance != null;
     }
 
     private Object mInstance;
@@ -96,7 +96,7 @@
 
     @UsedForTesting
     public static CursorAnchorInfoCompatWrapper fromObject(final Object instance) {
-        if (!isAvailable()) {
+        if (!sCursorAnchorInfoClass.exists()) {
             return new CursorAnchorInfoCompatWrapper(null);
         }
         return new CursorAnchorInfoCompatWrapper(instance);
diff --git a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
index c33c015..d3e24e3 100644
--- a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
+++ b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
@@ -23,13 +23,18 @@
 import android.text.TextUtils;
 import android.text.style.SuggestionSpan;
 
+import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.latin.SuggestedWords;
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
-import com.android.inputmethod.latin.define.DebugFlags;
 import com.android.inputmethod.latin.SuggestionSpanPickedNotificationReceiver;
+import com.android.inputmethod.latin.define.DebugFlags;
+import com.android.inputmethod.latin.utils.LocaleUtils;
 
 import java.lang.reflect.Field;
 import java.util.ArrayList;
+import java.util.Locale;
+
+import javax.annotation.Nullable;
 
 public final class SuggestionSpanUtils {
     // Note that SuggestionSpan.FLAG_AUTO_CORRECTION has been introduced
@@ -51,12 +56,14 @@
         // This utility class is not publicly instantiable.
     }
 
+    @UsedForTesting
     public static CharSequence getTextWithAutoCorrectionIndicatorUnderline(
             final Context context, final String text) {
         if (TextUtils.isEmpty(text) || OBJ_FLAG_AUTO_CORRECTION == null) {
             return text;
         }
         final Spannable spannable = new SpannableString(text);
+        // TODO: Set locale if it is feasible.
         final SuggestionSpan suggestionSpan = new SuggestionSpan(context, null /* locale */,
                 new String[] {} /* suggestions */, OBJ_FLAG_AUTO_CORRECTION,
                 SuggestionSpanPickedNotificationReceiver.class);
@@ -65,6 +72,7 @@
         return spannable;
     }
 
+    @UsedForTesting
     public static CharSequence getTextWithSuggestionSpan(final Context context,
             final String pickedWord, final SuggestedWords suggestedWords) {
         if (TextUtils.isEmpty(pickedWord) || suggestedWords.isEmpty()
@@ -86,6 +94,7 @@
                 suggestionsList.add(word.toString());
             }
         }
+        // TODO: Set locale if it is feasible.
         final SuggestionSpan suggestionSpan = new SuggestionSpan(context, null /* locale */,
                 suggestionsList.toArray(new String[suggestionsList.size()]), 0 /* flags */,
                 SuggestionSpanPickedNotificationReceiver.class);
@@ -93,4 +102,28 @@
         spannable.setSpan(suggestionSpan, 0, pickedWord.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
         return spannable;
     }
+
+    /**
+     * Returns first {@link Locale} found in the given array of {@link SuggestionSpan}.
+     * @param suggestionSpans the array of {@link SuggestionSpan} to be examined.
+     * @return the first {@link Locale} found in {@code suggestionSpans}. {@code null} when not
+     * found.
+     */
+    @UsedForTesting
+    @Nullable
+    public static Locale findFirstLocaleFromSuggestionSpans(
+            final SuggestionSpan[] suggestionSpans) {
+        for (final SuggestionSpan suggestionSpan : suggestionSpans) {
+            final String localeString = suggestionSpan.getLocale();
+            if (TextUtils.isEmpty(localeString)) {
+                continue;
+            }
+            final Locale locale = LocaleUtils.constructLocaleFromString(localeString);
+            if (locale == null) {
+                continue;
+            }
+            return locale;
+        }
+        return null;
+    }
 }
diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java b/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java
index 41916b6..568c80a 100644
--- a/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java
+++ b/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java
@@ -76,19 +76,29 @@
      * How often, in milliseconds, we want to update the metadata. This is a
      * floor value; actually, it may happen several hours later, or even more.
      */
-    private static final long UPDATE_FREQUENCY = TimeUnit.DAYS.toMillis(4);
+    private static final long UPDATE_FREQUENCY_MILLIS = TimeUnit.DAYS.toMillis(4);
 
     /**
      * We are waked around midnight, local time. We want to wake between midnight and 6 am,
      * roughly. So use a random time between 0 and this delay.
      */
-    private static final int MAX_ALARM_DELAY = (int)TimeUnit.HOURS.toMillis(6);
+    private static final int MAX_ALARM_DELAY_MILLIS = (int)TimeUnit.HOURS.toMillis(6);
 
     /**
      * How long we consider a "very long time". If no update took place in this time,
      * the content provider will trigger an update in the background.
      */
-    private static final long VERY_LONG_TIME = TimeUnit.DAYS.toMillis(14);
+    private static final long VERY_LONG_TIME_MILLIS = TimeUnit.DAYS.toMillis(14);
+
+    /**
+     * After starting a download, how long we wait before considering it may be stuck. After this
+     * period is elapsed, if the keyboard tries to download again, then we cancel and re-register
+     * the request; if it's within this time, we just leave it be.
+     * It's important to note that we do not re-submit the request merely because the time is up.
+     * This is only to decide whether to cancel the old one and re-requesting when the keyboard
+     * fires a new request for the same data.
+     */
+    public static final long NO_CANCEL_DOWNLOAD_PERIOD_MILLIS = TimeUnit.SECONDS.toMillis(30);
 
     /**
      * An executor that serializes tasks given to it.
@@ -188,16 +198,16 @@
      */
     private static void checkTimeAndMaybeSetupUpdateAlarm(final Context context) {
         // Of all clients, if the one that hasn't been updated for the longest
-        // is still more recent than UPDATE_FREQUENCY, do nothing.
-        if (!isLastUpdateAtLeastThisOld(context, UPDATE_FREQUENCY)) return;
+        // is still more recent than UPDATE_FREQUENCY_MILLIS, do nothing.
+        if (!isLastUpdateAtLeastThisOld(context, UPDATE_FREQUENCY_MILLIS)) return;
 
         PrivateLog.log("Date changed - registering alarm");
         AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
 
-        // Best effort to wake between midnight and MAX_ALARM_DELAY in the morning.
+        // Best effort to wake between midnight and MAX_ALARM_DELAY_MILLIS in the morning.
         // It doesn't matter too much if this is very inexact.
         final long now = System.currentTimeMillis();
-        final long alarmTime = now + new Random().nextInt(MAX_ALARM_DELAY);
+        final long alarmTime = now + new Random().nextInt(MAX_ALARM_DELAY_MILLIS);
         final Intent updateIntent = new Intent(DictionaryPackConstants.UPDATE_NOW_INTENT_ACTION);
         final PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0,
                 updateIntent, PendingIntent.FLAG_CANCEL_CURRENT);
@@ -223,11 +233,11 @@
     /**
      * Refreshes data if it hasn't been refreshed in a very long time.
      *
-     * This will check the last update time, and if it's been more than VERY_LONG_TIME,
+     * This will check the last update time, and if it's been more than VERY_LONG_TIME_MILLIS,
      * update metadata now - and possibly take subsequent update actions.
      */
     public static void updateNowIfNotUpdatedInAVeryLongTime(final Context context) {
-        if (!isLastUpdateAtLeastThisOld(context, VERY_LONG_TIME)) return;
+        if (!isLastUpdateAtLeastThisOld(context, VERY_LONG_TIME_MILLIS)) return;
         UpdateHandler.tryUpdate(context, false);
     }
 
diff --git a/java/src/com/android/inputmethod/dictionarypack/DownloadIdAndStartDate.java b/java/src/com/android/inputmethod/dictionarypack/DownloadIdAndStartDate.java
new file mode 100644
index 0000000..6247a15
--- /dev/null
+++ b/java/src/com/android/inputmethod/dictionarypack/DownloadIdAndStartDate.java
@@ -0,0 +1,29 @@
+/*
+ * 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.dictionarypack;
+
+/**
+ * A simple container of download ID and download start date.
+ */
+public class DownloadIdAndStartDate {
+    public final long mId;
+    public final long mStartDate;
+    public DownloadIdAndStartDate(final long id, final long startDate) {
+        mId = id;
+        mStartDate = startDate;
+    }
+}
diff --git a/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java b/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java
index e9dde42..c9e8f91 100644
--- a/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java
+++ b/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java
@@ -433,18 +433,18 @@
      *
      * @param context a context instance to open the database on
      * @param uri the URI to retrieve the metadata download ID of
-     * @return the metadata download ID, or NOT_AN_ID if no download is in progress
+     * @return the download id and start date, or null if the URL is not known
      */
-    public static long getMetadataDownloadIdForURI(final Context context,
-            final String uri) {
+    public static DownloadIdAndStartDate getMetadataDownloadIdAndStartDateForURI(
+            final Context context, final String uri) {
         SQLiteDatabase defaultDb = getDb(context, null);
         final Cursor cursor = defaultDb.query(CLIENT_TABLE_NAME,
-                new String[] { CLIENT_PENDINGID_COLUMN },
+                new String[] { CLIENT_PENDINGID_COLUMN, CLIENT_LAST_UPDATE_DATE_COLUMN },
                 CLIENT_METADATA_URI_COLUMN + " = ?", new String[] { uri },
                 null, null, null, null);
         try {
-            if (!cursor.moveToFirst()) return UpdateHandler.NOT_AN_ID;
-            return cursor.getInt(0); // Only one column, return it
+            if (!cursor.moveToFirst()) return null;
+            return new DownloadIdAndStartDate(cursor.getInt(0), cursor.getLong(1));
         } finally {
             cursor.close();
         }
@@ -922,6 +922,7 @@
             final long downloadId) {
         final ContentValues values = new ContentValues();
         values.put(CLIENT_PENDINGID_COLUMN, downloadId);
+        values.put(CLIENT_LAST_UPDATE_DATE_COLUMN, System.currentTimeMillis());
         final SQLiteDatabase defaultDb = getDb(context, "");
         final Cursor cursor = MetadataDbHelper.queryClientIds(context);
         if (null == cursor) return;
diff --git a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java
index d8c5f31..90a7504 100644
--- a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java
+++ b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java
@@ -251,12 +251,16 @@
                 res.getBoolean(R.bool.metadata_downloads_visible_in_download_UI));
 
         final DownloadManagerWrapper manager = new DownloadManagerWrapper(context);
-        cancelUpdateWithDownloadManager(context, metadataUri, manager);
+        if (maybeCancelUpdateAndReturnIfStillRunning(context, metadataUri, manager,
+                DictionaryService.NO_CANCEL_DOWNLOAD_PERIOD_MILLIS)) {
+            // We already have a recent download in progress. Don't register a new download.
+            return;
+        }
         final long downloadId;
         synchronized (sSharedIdProtector) {
             downloadId = manager.enqueue(metadataRequest);
             DebugLogUtils.l("Metadata download requested with id", downloadId);
-            // If there is already a download in progress, it's been there for a while and
+            // If there is still a download in progress, it's been there for a while and
             // there is probably something wrong with download manager. It's best to just
             // overwrite the id and request it again. If the old one happens to finish
             // anyway, we don't know about its ID any more, so the downloadFinished
@@ -267,21 +271,29 @@
     }
 
     /**
-     * Cancels downloading a file, if there is one for this URI.
+     * Cancels downloading a file if there is one for this URI and it's too long.
      *
      * If we are not currently downloading the file at this URI, this is a no-op.
      *
      * @param context the context to open the database on
      * @param metadataUri the URI to cancel
      * @param manager an wrapped instance of DownloadManager
+     * @param graceTime if there was a download started less than this many milliseconds, don't
+     *  cancel and return true
+     * @return whether the download is still active
      */
-    private static void cancelUpdateWithDownloadManager(final Context context,
-            final String metadataUri, final DownloadManagerWrapper manager) {
+    private static boolean maybeCancelUpdateAndReturnIfStillRunning(final Context context,
+            final String metadataUri, final DownloadManagerWrapper manager, final long graceTime) {
         synchronized (sSharedIdProtector) {
-            final long metadataDownloadId =
-                    MetadataDbHelper.getMetadataDownloadIdForURI(context, metadataUri);
-            if (NOT_AN_ID == metadataDownloadId) return;
-            manager.remove(metadataDownloadId);
+            final DownloadIdAndStartDate metadataDownloadIdAndStartDate =
+                    MetadataDbHelper.getMetadataDownloadIdAndStartDateForURI(context, metadataUri);
+            if (null == metadataDownloadIdAndStartDate) return false;
+            if (NOT_AN_ID == metadataDownloadIdAndStartDate.mId) return false;
+            if (metadataDownloadIdAndStartDate.mStartDate + graceTime
+                    > System.currentTimeMillis()) {
+                return true;
+            }
+            manager.remove(metadataDownloadIdAndStartDate.mId);
             writeMetadataDownloadId(context, metadataUri, NOT_AN_ID);
         }
         // Consider a cancellation as a failure. As such, inform listeners that the download
@@ -289,6 +301,7 @@
         for (UpdateEventListener listener : linkedCopyOfList(sUpdateEventListeners)) {
             listener.downloadedMetadata(false);
         }
+        return false;
     }
 
     /**
@@ -303,7 +316,7 @@
     public static void cancelUpdate(final Context context, final String clientId) {
         final DownloadManagerWrapper manager = new DownloadManagerWrapper(context);
         final String metadataUri = MetadataDbHelper.getMetadataUriAsString(context, clientId);
-        cancelUpdateWithDownloadManager(context, metadataUri, manager);
+        maybeCancelUpdateAndReturnIfStillRunning(context, metadataUri, manager, 0 /* graceTime */);
     }
 
     /**
@@ -387,7 +400,7 @@
             // If any of these is metadata, we should update the DB
             boolean hasMetadata = false;
             for (DownloadRecord record : downloadRecords) {
-                if (null == record.mAttributes) {
+                if (record.isMetadata()) {
                     hasMetadata = true;
                     break;
                 }
diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java
index bf29b5f..45ce6a8 100644
--- a/java/src/com/android/inputmethod/keyboard/Key.java
+++ b/java/src/com/android/inputmethod/keyboard/Key.java
@@ -278,8 +278,8 @@
 
         mLabelFlags = style.getFlags(keyAttr, R.styleable.Keyboard_Key_keyLabelFlags)
                 | row.getDefaultKeyLabelFlags();
-        final boolean needsToUpperCase = needsToUpperCase(mLabelFlags, params.mId.mElementId);
-        final Locale locale = params.mId.mLocale;
+        final boolean needsToUpcase = needsToUpcase(mLabelFlags, params.mId.mElementId);
+        final Locale localeForUpcasing = params.mId.getLocales()[0];
         int actionFlags = style.getFlags(keyAttr, R.styleable.Keyboard_Key_keyActionFlags);
         String[] moreKeys = style.getStringArray(keyAttr, R.styleable.Keyboard_Key_moreKeys);
 
@@ -321,7 +321,7 @@
             actionFlags |= ACTION_FLAGS_ENABLE_LONG_PRESS;
             mMoreKeys = new MoreKeySpec[moreKeys.length];
             for (int i = 0; i < moreKeys.length; i++) {
-                mMoreKeys[i] = new MoreKeySpec(moreKeys[i], needsToUpperCase, locale);
+                mMoreKeys[i] = new MoreKeySpec(moreKeys[i], needsToUpcase, localeForUpcasing);
             }
         } else {
             mMoreKeys = null;
@@ -342,16 +342,16 @@
             mLabel = new StringBuilder().appendCodePoint(code).toString();
         } else {
             mLabel = StringUtils.toUpperCaseOfStringForLocale(
-                    KeySpecParser.getLabel(keySpec), needsToUpperCase, locale);
+                    KeySpecParser.getLabel(keySpec), needsToUpcase, localeForUpcasing);
         }
         if ((mLabelFlags & LABEL_FLAGS_DISABLE_HINT_LABEL) != 0) {
             mHintLabel = null;
         } else {
             mHintLabel = StringUtils.toUpperCaseOfStringForLocale(style.getString(keyAttr,
-                    R.styleable.Keyboard_Key_keyHintLabel), needsToUpperCase, locale);
+                    R.styleable.Keyboard_Key_keyHintLabel), needsToUpcase, localeForUpcasing);
         }
         String outputText = StringUtils.toUpperCaseOfStringForLocale(
-                KeySpecParser.getOutputText(keySpec), needsToUpperCase, locale);
+                KeySpecParser.getOutputText(keySpec), needsToUpcase, localeForUpcasing);
         // Choose the first letter of the label as primary code if not specified.
         if (code == CODE_UNSPECIFIED && TextUtils.isEmpty(outputText)
                 && !TextUtils.isEmpty(mLabel)) {
@@ -377,12 +377,12 @@
                 mCode = CODE_OUTPUT_TEXT;
             }
         } else {
-            mCode = StringUtils.toUpperCaseOfCodeForLocale(code, needsToUpperCase, locale);
+            mCode = StringUtils.toUpperCaseOfCodeForLocale(code, needsToUpcase, localeForUpcasing);
         }
         final int altCodeInAttr = KeySpecParser.parseCode(
                 style.getString(keyAttr, R.styleable.Keyboard_Key_altCode), CODE_UNSPECIFIED);
         final int altCode = StringUtils.toUpperCaseOfCodeForLocale(
-                altCodeInAttr, needsToUpperCase, locale);
+                altCodeInAttr, needsToUpcase, localeForUpcasing);
         mOptionalAttributes = OptionalAttributes.newInstance(outputText, altCode,
                 disabledIconId, visualInsetsLeft, visualInsetsRight);
         mKeyVisualAttributes = KeyVisualAttributes.newInstance(keyAttr);
@@ -432,7 +432,7 @@
         return (filteredMoreKeys == moreKeys) ? key : new Key(key, filteredMoreKeys);
     }
 
-    private static boolean needsToUpperCase(final int labelFlags, final int keyboardElementId) {
+    private static boolean needsToUpcase(final int labelFlags, final int keyboardElementId) {
         if ((labelFlags & LABEL_FLAGS_PRESERVE_CASE) != 0) return false;
         switch (keyboardElementId) {
         case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED:
diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java
index 85dfea4..d35c8fa 100644
--- a/java/src/com/android/inputmethod/keyboard/Keyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java
@@ -108,10 +108,9 @@
         mAltCodeKeysWhileTyping = Collections.unmodifiableList(params.mAltCodeKeysWhileTyping);
         mIconsSet = params.mIconsSet;
 
-        mProximityInfo = new ProximityInfo(params.mId.mLocale.toString(),
-                params.GRID_WIDTH, params.GRID_HEIGHT, mOccupiedWidth, mOccupiedHeight,
-                mMostCommonKeyWidth, mMostCommonKeyHeight, mSortedKeys,
-                params.mTouchPositionCorrection);
+        mProximityInfo = new ProximityInfo(params.GRID_WIDTH, params.GRID_HEIGHT,
+                mOccupiedWidth, mOccupiedHeight, mMostCommonKeyWidth, mMostCommonKeyHeight,
+                mSortedKeys, params.mTouchPositionCorrection);
         mProximityCharsCorrectionEnabled = params.mProximityCharsCorrectionEnabled;
     }
 
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
index f9cf353..ab0d633 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
@@ -63,7 +63,6 @@
     public static final int ELEMENT_EMOJI_CATEGORY6 = 16;
 
     public final RichInputMethodSubtype mSubtype;
-    public final Locale mLocale;
     public final int mWidth;
     public final int mHeight;
     public final int mMode;
@@ -79,7 +78,6 @@
 
     public KeyboardId(final int elementId, final KeyboardLayoutSet.Params params) {
         mSubtype = params.mSubtype;
-        mLocale = SubtypeLocaleUtils.getSubtypeLocale(mSubtype);
         mWidth = params.mKeyboardWidth;
         mHeight = params.mKeyboardHeight;
         mMode = params.mMode;
@@ -167,6 +165,10 @@
         return InputTypeUtils.getImeOptionsActionIdFromEditorInfo(mEditorInfo);
     }
 
+    public Locale[] getLocales() {
+        return mSubtype.getLocales();
+    }
+
     @Override
     public boolean equals(final Object other) {
         return other instanceof KeyboardId && equals((KeyboardId) other);
@@ -181,7 +183,8 @@
     public String toString() {
         return String.format(Locale.ROOT, "[%s %s:%s %dx%d %s %s%s%s%s%s%s%s%s%s]",
                 elementIdToName(mElementId),
-                mLocale, mSubtype.getExtraValueOf(KEYBOARD_LAYOUT_SET),
+                Arrays.deepToString(mSubtype.getLocales()),
+                mSubtype.getExtraValueOf(KEYBOARD_LAYOUT_SET),
                 mWidth, mHeight,
                 modeName(mMode),
                 actionName(imeAction()),
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index 246d11b..93123d1 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -120,7 +120,9 @@
         mKeyboardLayoutSet = builder.build();
         try {
             mState.onLoadKeyboard(currentAutoCapsState, currentRecapitalizeState);
-            mKeyboardTextsSet.setLocale(mSubtypeSwitcher.getCurrentSubtypeLocale(), mThemeContext);
+            // TODO: revisit this for multi-lingual input
+            mKeyboardTextsSet.setLocale(mSubtypeSwitcher.getCurrentSubtypeLocales()[0],
+                    mThemeContext);
         } catch (KeyboardLayoutSetException e) {
             Log.w(TAG, "loading keyboard failed: " + e.mKeyboardId, e.getCause());
             return;
@@ -161,7 +163,7 @@
                 currentSettingsValues.mKeyPreviewDismissDuration);
         keyboardView.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady());
         final boolean subtypeChanged = (oldKeyboard == null)
-                || !keyboard.mId.mLocale.equals(oldKeyboard.mId.mLocale);
+                || !keyboard.mId.mSubtype.equals(oldKeyboard.mId.mSubtype);
         final int languageOnSpacebarFormatType = mSubtypeSwitcher.getLanguageOnSpacebarFormatType(
                 keyboard.mId.mSubtype);
         final boolean hasMultipleEnabledIMEsOrSubtypes = RichInputMethodManager.getInstance()
@@ -254,8 +256,9 @@
     }
 
     public void onToggleEmojiKeyboard() {
-        if (mKeyboardLayoutSet == null || !isShowingEmojiPalettes()) {
-            mLatinIME.startShowingInputView();
+        final boolean needsToLoadKeyboard = (mKeyboardLayoutSet == null);
+        if (needsToLoadKeyboard || !isShowingEmojiPalettes()) {
+            mLatinIME.startShowingInputView(needsToLoadKeyboard);
             setEmojiKeyboard();
         } else {
             mLatinIME.stopShowingInputView();
diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index cded2cb..ad30b74 100644
--- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -875,6 +875,11 @@
     // Layout language name on spacebar.
     private String layoutLanguageOnSpacebar(final Paint paint,
             final RichInputMethodSubtype subtype, final int width) {
+        if (mLanguageOnSpacebarFormatType == LanguageOnSpacebarHelper.FORMAT_TYPE_MULTIPLE) {
+            // TODO: return an appropriate string
+            return "";
+        }
+
         // Choose appropriate language name to fit into the width.
         if (mLanguageOnSpacebarFormatType == LanguageOnSpacebarHelper.FORMAT_TYPE_FULL_LOCALE) {
             final String fullText = subtype.getFullDisplayName();
diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
index c19cd67..9c5abcd 100644
--- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
+++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
@@ -52,18 +52,11 @@
     private final int mMostCommonKeyHeight;
     private final List<Key> mSortedKeys;
     private final List<Key>[] mGridNeighbors;
-    private final String mLocaleStr;
 
     @SuppressWarnings("unchecked")
-    ProximityInfo(final String localeStr, final int gridWidth, final int gridHeight,
-            final int minWidth, final int height, final int mostCommonKeyWidth,
-            final int mostCommonKeyHeight, final List<Key> sortedKeys,
+    ProximityInfo(final int gridWidth, final int gridHeight, final int minWidth, final int height,
+            final int mostCommonKeyWidth, final int mostCommonKeyHeight, final List<Key> sortedKeys,
             final TouchPositionCorrection touchPositionCorrection) {
-        if (TextUtils.isEmpty(localeStr)) {
-            mLocaleStr = "";
-        } else {
-            mLocaleStr = localeStr;
-        }
         mGridWidth = gridWidth;
         mGridHeight = gridHeight;
         mGridSize = mGridWidth * mGridHeight;
@@ -89,11 +82,10 @@
     }
 
     // TODO: Stop passing proximityCharsArray
-    private static native long setProximityInfoNative(String locale,
-            int displayWidth, int displayHeight, int gridWidth, int gridHeight,
-            int mostCommonKeyWidth, int mostCommonKeyHeight, int[] proximityCharsArray,
-            int keyCount, int[] keyXCoordinates, int[] keyYCoordinates, int[] keyWidths,
-            int[] keyHeights, int[] keyCharCodes, float[] sweetSpotCenterXs,
+    private static native long setProximityInfoNative(int displayWidth, int displayHeight,
+            int gridWidth, int gridHeight, int mostCommonKeyWidth, int mostCommonKeyHeight,
+            int[] proximityCharsArray, int keyCount, int[] keyXCoordinates, int[] keyYCoordinates,
+            int[] keyWidths, int[] keyHeights, int[] keyCharCodes, float[] sweetSpotCenterXs,
             float[] sweetSpotCenterYs, float[] sweetSpotRadii);
 
     private static native void releaseProximityInfoNative(long nativeProximityInfo);
@@ -221,10 +213,10 @@
         }
 
         // TODO: Stop passing proximityCharsArray
-        return setProximityInfoNative(mLocaleStr, mKeyboardMinWidth, mKeyboardHeight,
-                mGridWidth, mGridHeight, mMostCommonKeyWidth, mMostCommonKeyHeight,
-                proximityCharsArray, keyCount, keyXCoordinates, keyYCoordinates, keyWidths,
-                keyHeights, keyCharCodes, sweetSpotCenterXs, sweetSpotCenterYs, sweetSpotRadii);
+        return setProximityInfoNative(mKeyboardMinWidth, mKeyboardHeight, mGridWidth, mGridHeight,
+                mMostCommonKeyWidth, mMostCommonKeyHeight, proximityCharsArray, keyCount,
+                keyXCoordinates, keyYCoordinates, keyWidths, keyHeights, keyCharCodes,
+                sweetSpotCenterXs, sweetSpotCenterYs, sweetSpotRadii);
     }
 
     public long getNativeProximityInfo() {
diff --git a/java/src/com/android/inputmethod/keyboard/TextDecorator.java b/java/src/com/android/inputmethod/keyboard/TextDecorator.java
index 6e4e328..c22717f 100644
--- a/java/src/com/android/inputmethod/keyboard/TextDecorator.java
+++ b/java/src/com/android/inputmethod/keyboard/TextDecorator.java
@@ -182,7 +182,7 @@
     private void layoutMain() {
         final CursorAnchorInfoCompatWrapper info = mCursorAnchorInfoWrapper;
 
-        if (info == null) {
+        if (info == null || !info.isAvailable()) {
             cancelLayoutInternalExpectedly("CursorAnchorInfo isn't available.");
             return;
         }
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
index 5038555..f4e010c 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
@@ -45,6 +45,7 @@
 
 import java.io.IOException;
 import java.util.Arrays;
+import java.util.Locale;
 
 /**
  * Keyboard Building helper.
@@ -281,7 +282,8 @@
 
             params.mThemeId = keyboardAttr.getInt(R.styleable.Keyboard_themeId, 0);
             params.mIconsSet.loadIcons(keyboardAttr);
-            params.mTextsSet.setLocale(params.mId.mLocale, mContext);
+            // TODO: this needs to be revisited for multi-lingual input.
+            params.mTextsSet.setLocale(params.mId.getLocales()[0], mContext);
 
             final int resourceId = keyboardAttr.getResourceId(
                     R.styleable.Keyboard_touchPositionCorrectionData, 0);
@@ -672,12 +674,10 @@
                     R.styleable.Keyboard_Case_imeAction, id.imeAction());
             final boolean isIconDefinedMatched = isIconDefined(caseAttr,
                     R.styleable.Keyboard_Case_isIconDefined, mParams.mIconsSet);
-            final boolean localeCodeMatched = matchString(caseAttr,
-                    R.styleable.Keyboard_Case_localeCode, id.mLocale.toString());
-            final boolean languageCodeMatched = matchString(caseAttr,
-                    R.styleable.Keyboard_Case_languageCode, id.mLocale.getLanguage());
-            final boolean countryCodeMatched = matchString(caseAttr,
-                    R.styleable.Keyboard_Case_countryCode, id.mLocale.getCountry());
+            final Locale[] locales = id.getLocales();
+            final boolean localeCodeMatched = matchLocaleCodes(caseAttr, locales);
+            final boolean languageCodeMatched = matchLanguageCodes(caseAttr, locales);
+            final boolean countryCodeMatched = matchCountryCodes(caseAttr, locales);
             final boolean splitLayoutMatched = matchBoolean(caseAttr,
                     R.styleable.Keyboard_Case_isSplitLayout, id.mIsSplitLayout);
             final boolean selected = keyboardLayoutSetMatched && keyboardLayoutSetElementMatched
@@ -733,6 +733,23 @@
         }
     }
 
+    private boolean matchLocaleCodes(TypedArray caseAttr, final Locale[] locales) {
+        // TODO: adujst this for multilingual input
+        return matchString(caseAttr, R.styleable.Keyboard_Case_localeCode, locales[0].toString());
+    }
+
+    private boolean matchLanguageCodes(TypedArray caseAttr, Locale[] locales) {
+        // TODO: adujst this for multilingual input
+        return matchString(caseAttr, R.styleable.Keyboard_Case_languageCode,
+                locales[0].getLanguage());
+    }
+
+    private boolean matchCountryCodes(TypedArray caseAttr, Locale[] locales) {
+        // TODO: adujst this for multilingual input
+        return matchString(caseAttr, R.styleable.Keyboard_Case_countryCode,
+                locales[0].getCountry());
+    }
+
     private static boolean matchInteger(final TypedArray a, final int index, final int value) {
         // If <case> does not have "index" attribute, that means this <case> is wild-card for
         // the attribute.
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java
index 1e1188b..71ce768 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java
@@ -27,6 +27,9 @@
 import java.util.SortedSet;
 import java.util.TreeSet;
 
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
 public class KeyboardParams {
     public KeyboardId mId;
     public int mThemeId;
@@ -67,7 +70,7 @@
     public final KeyboardTextsSet mTextsSet = new KeyboardTextsSet();
     public final KeyStylesSet mKeyStyles = new KeyStylesSet(mTextsSet);
 
-    public KeysCache mKeysCache;
+    @Nullable public KeysCache mKeysCache;
     public boolean mAllowRedundantMoreKeys;
 
     public int mMostCommonKeyHeight = 0;
@@ -96,7 +99,7 @@
         clearHistogram();
     }
 
-    public void onAddKey(final Key newKey) {
+    public void onAddKey(@Nonnull final Key newKey) {
         final Key key = (mKeysCache != null) ? mKeysCache.get(newKey) : newKey;
         final boolean isSpacer = key.isSpacer();
         if (isSpacer && key.getWidth() == 0) {
@@ -129,7 +132,10 @@
         mSortedKeys.clear();
         for (final Key key : allKeys) {
             final Key filteredKey = Key.removeRedundantMoreKeys(key, lettersOnBaseLayout);
-            mSortedKeys.add(mKeysCache.replace(key, filteredKey));
+            if (mKeysCache != null) {
+                mKeysCache.replace(key, filteredKey);
+            }
+            mSortedKeys.add(filteredKey);
         }
     }
 
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java
index a778ee8..a81d7ea 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java
@@ -83,24 +83,24 @@
 
     private static final String[] NAMES = {
     //  /* index:histogram */ "name",
-        /*   0:32 */ "morekeys_a",
-        /*   1:32 */ "morekeys_o",
-        /*   2:31 */ "morekeys_e",
-        /*   3:30 */ "morekeys_u",
-        /*   4:30 */ "keylabel_to_alpha",
-        /*   5:29 */ "morekeys_i",
-        /*   6:24 */ "morekeys_n",
-        /*   7:24 */ "morekeys_c",
+        /*   0:33 */ "morekeys_a",
+        /*   1:33 */ "morekeys_o",
+        /*   2:32 */ "morekeys_e",
+        /*   3:31 */ "morekeys_u",
+        /*   4:31 */ "keylabel_to_alpha",
+        /*   5:30 */ "morekeys_i",
+        /*   6:25 */ "morekeys_n",
+        /*   7:25 */ "morekeys_c",
         /*   8:23 */ "double_quotes",
-        /*   9:22 */ "single_quotes",
-        /*  10:21 */ "morekeys_s",
-        /*  11:18 */ "keyspec_currency",
-        /*  12:16 */ "morekeys_y",
-        /*  13:15 */ "morekeys_z",
+        /*   9:22 */ "morekeys_s",
+        /*  10:22 */ "single_quotes",
+        /*  11:19 */ "keyspec_currency",
+        /*  12:17 */ "morekeys_y",
+        /*  13:16 */ "morekeys_z",
         /*  14:14 */ "morekeys_d",
         /*  15:10 */ "morekeys_t",
         /*  16:10 */ "morekeys_l",
-        /*  17: 9 */ "morekeys_g",
+        /*  17:10 */ "morekeys_g",
         /*  18: 9 */ "single_angle_quotes",
         /*  19: 9 */ "double_angle_quotes",
         /*  20: 8 */ "morekeys_r",
@@ -136,57 +136,57 @@
         /*  50: 5 */ "additional_morekeys_symbols_8",
         /*  51: 5 */ "additional_morekeys_symbols_9",
         /*  52: 5 */ "additional_morekeys_symbols_0",
-        /*  53: 4 */ "morekeys_nordic_row2_11",
-        /*  54: 4 */ "morekeys_punctuation",
-        /*  55: 4 */ "keyspec_tablet_comma",
-        /*  56: 3 */ "keyspec_swiss_row1_11",
-        /*  57: 3 */ "keyspec_swiss_row2_10",
-        /*  58: 3 */ "keyspec_swiss_row2_11",
-        /*  59: 3 */ "morekeys_swiss_row1_11",
-        /*  60: 3 */ "morekeys_swiss_row2_10",
-        /*  61: 3 */ "morekeys_swiss_row2_11",
-        /*  62: 3 */ "morekeys_star",
-        /*  63: 3 */ "keyspec_left_parenthesis",
-        /*  64: 3 */ "keyspec_right_parenthesis",
-        /*  65: 3 */ "keyspec_left_square_bracket",
-        /*  66: 3 */ "keyspec_right_square_bracket",
-        /*  67: 3 */ "keyspec_left_curly_bracket",
-        /*  68: 3 */ "keyspec_right_curly_bracket",
-        /*  69: 3 */ "keyspec_less_than",
-        /*  70: 3 */ "keyspec_greater_than",
-        /*  71: 3 */ "keyspec_less_than_equal",
-        /*  72: 3 */ "keyspec_greater_than_equal",
-        /*  73: 3 */ "keyspec_left_double_angle_quote",
-        /*  74: 3 */ "keyspec_right_double_angle_quote",
-        /*  75: 3 */ "keyspec_left_single_angle_quote",
-        /*  76: 3 */ "keyspec_right_single_angle_quote",
-        /*  77: 3 */ "keyspec_comma",
-        /*  78: 3 */ "morekeys_tablet_comma",
-        /*  79: 3 */ "keyhintlabel_period",
-        /*  80: 3 */ "morekeys_tablet_period",
-        /*  81: 3 */ "morekeys_question",
-        /*  82: 2 */ "morekeys_h",
-        /*  83: 2 */ "morekeys_w",
-        /*  84: 2 */ "morekeys_east_slavic_row2_2",
-        /*  85: 2 */ "morekeys_cyrillic_u",
-        /*  86: 2 */ "morekeys_cyrillic_en",
-        /*  87: 2 */ "morekeys_cyrillic_ghe",
-        /*  88: 2 */ "morekeys_cyrillic_o",
-        /*  89: 2 */ "morekeys_cyrillic_i",
-        /*  90: 2 */ "keyspec_south_slavic_row1_6",
-        /*  91: 2 */ "keyspec_south_slavic_row2_11",
-        /*  92: 2 */ "keyspec_south_slavic_row3_1",
-        /*  93: 2 */ "keyspec_south_slavic_row3_8",
-        /*  94: 2 */ "morekeys_tablet_punctuation",
-        /*  95: 2 */ "keyspec_spanish_row2_10",
-        /*  96: 2 */ "morekeys_bullet",
-        /*  97: 2 */ "morekeys_left_parenthesis",
-        /*  98: 2 */ "morekeys_right_parenthesis",
-        /*  99: 2 */ "morekeys_arabic_diacritics",
-        /* 100: 2 */ "keyhintlabel_tablet_comma",
-        /* 101: 2 */ "keyspec_period",
-        /* 102: 2 */ "morekeys_period",
-        /* 103: 2 */ "keyspec_tablet_period",
+        /*  53: 5 */ "morekeys_tablet_period",
+        /*  54: 4 */ "morekeys_nordic_row2_11",
+        /*  55: 4 */ "morekeys_punctuation",
+        /*  56: 4 */ "keyspec_tablet_comma",
+        /*  57: 4 */ "keyspec_period",
+        /*  58: 4 */ "morekeys_period",
+        /*  59: 4 */ "keyspec_tablet_period",
+        /*  60: 3 */ "keyspec_swiss_row1_11",
+        /*  61: 3 */ "keyspec_swiss_row2_10",
+        /*  62: 3 */ "keyspec_swiss_row2_11",
+        /*  63: 3 */ "morekeys_swiss_row1_11",
+        /*  64: 3 */ "morekeys_swiss_row2_10",
+        /*  65: 3 */ "morekeys_swiss_row2_11",
+        /*  66: 3 */ "morekeys_star",
+        /*  67: 3 */ "keyspec_left_parenthesis",
+        /*  68: 3 */ "keyspec_right_parenthesis",
+        /*  69: 3 */ "keyspec_left_square_bracket",
+        /*  70: 3 */ "keyspec_right_square_bracket",
+        /*  71: 3 */ "keyspec_left_curly_bracket",
+        /*  72: 3 */ "keyspec_right_curly_bracket",
+        /*  73: 3 */ "keyspec_less_than",
+        /*  74: 3 */ "keyspec_greater_than",
+        /*  75: 3 */ "keyspec_less_than_equal",
+        /*  76: 3 */ "keyspec_greater_than_equal",
+        /*  77: 3 */ "keyspec_left_double_angle_quote",
+        /*  78: 3 */ "keyspec_right_double_angle_quote",
+        /*  79: 3 */ "keyspec_left_single_angle_quote",
+        /*  80: 3 */ "keyspec_right_single_angle_quote",
+        /*  81: 3 */ "keyspec_comma",
+        /*  82: 3 */ "morekeys_tablet_comma",
+        /*  83: 3 */ "keyhintlabel_period",
+        /*  84: 3 */ "morekeys_question",
+        /*  85: 2 */ "morekeys_h",
+        /*  86: 2 */ "morekeys_w",
+        /*  87: 2 */ "morekeys_east_slavic_row2_2",
+        /*  88: 2 */ "morekeys_cyrillic_u",
+        /*  89: 2 */ "morekeys_cyrillic_en",
+        /*  90: 2 */ "morekeys_cyrillic_ghe",
+        /*  91: 2 */ "morekeys_cyrillic_o",
+        /*  92: 2 */ "morekeys_cyrillic_i",
+        /*  93: 2 */ "keyspec_south_slavic_row1_6",
+        /*  94: 2 */ "keyspec_south_slavic_row2_11",
+        /*  95: 2 */ "keyspec_south_slavic_row3_1",
+        /*  96: 2 */ "keyspec_south_slavic_row3_8",
+        /*  97: 2 */ "morekeys_tablet_punctuation",
+        /*  98: 2 */ "keyspec_spanish_row2_10",
+        /*  99: 2 */ "morekeys_bullet",
+        /* 100: 2 */ "morekeys_left_parenthesis",
+        /* 101: 2 */ "morekeys_right_parenthesis",
+        /* 102: 2 */ "morekeys_arabic_diacritics",
+        /* 103: 2 */ "keyhintlabel_tablet_comma",
         /* 104: 2 */ "keyhintlabel_tablet_period",
         /* 105: 2 */ "keyspec_symbols_question",
         /* 106: 2 */ "keyspec_symbols_semicolon",
@@ -274,8 +274,8 @@
         EMPTY, EMPTY, EMPTY,
         /* ~ morekeys_c */
         /* double_quotes */ "!text/double_lqm_rqm",
-        /* single_quotes */ "!text/single_lqm_rqm",
         /* morekeys_s */ EMPTY,
+        /* single_quotes */ "!text/single_lqm_rqm",
         /* keyspec_currency */ "$",
         /* morekeys_y ~ */
         EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
@@ -298,10 +298,16 @@
         // Label for "switch to symbols" key.
         /* keylabel_to_symbol */ "?123",
         /* additional_morekeys_symbols_1 ~ */
-        EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
-        /* ~ morekeys_nordic_row2_11 */
+        EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+        /* ~ additional_morekeys_symbols_0 */
+        /* morekeys_tablet_period */ "!text/morekeys_tablet_punctuation",
+        /* morekeys_nordic_row2_11 */ EMPTY,
         /* morekeys_punctuation */ "!autoColumnOrder!8,\\,,?,!,#,!text/keyspec_right_parenthesis,!text/keyspec_left_parenthesis,/,;,',@,:,-,\",+,\\%,&",
         /* keyspec_tablet_comma */ ",",
+        // Period key
+        /* keyspec_period */ ".",
+        /* morekeys_period */ "!text/morekeys_punctuation",
+        /* keyspec_tablet_period */ ".",
         /* keyspec_swiss_row1_11 ~ */
         EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
         /* ~ morekeys_swiss_row2_11 */
@@ -335,7 +341,6 @@
         /* keyspec_comma */ ",",
         /* morekeys_tablet_comma */ EMPTY,
         /* keyhintlabel_period */ EMPTY,
-        /* morekeys_tablet_period */ "!text/morekeys_tablet_punctuation",
         // U+00BF: "¿" INVERTED QUESTION MARK
         /* morekeys_question */ "\u00BF",
         /* morekeys_h ~ */
@@ -352,13 +357,9 @@
         /* morekeys_bullet */ "\u266A,\u2665,\u2660,\u2666,\u2663",
         /* morekeys_left_parenthesis */ "!fixedColumnOrder!3,!text/keyspecs_left_parenthesis_more_keys",
         /* morekeys_right_parenthesis */ "!fixedColumnOrder!3,!text/keyspecs_right_parenthesis_more_keys",
-        /* morekeys_arabic_diacritics */ EMPTY,
-        /* keyhintlabel_tablet_comma */ EMPTY,
-        // Period key
-        /* keyspec_period */ ".",
-        /* morekeys_period */ "!text/morekeys_punctuation",
-        /* keyspec_tablet_period */ ".",
-        /* keyhintlabel_tablet_period */ EMPTY,
+        /* morekeys_arabic_diacritics ~ */
+        EMPTY, EMPTY, EMPTY,
+        /* ~ keyhintlabel_tablet_period */
         /* keyspec_symbols_question */ "?",
         /* keyspec_symbols_semicolon */ ";",
         /* keyspec_symbols_percent */ "%",
@@ -587,14 +588,17 @@
         // U+066B: "٫" ARABIC DECIMAL SEPARATOR
         // U+066C: "٬" ARABIC THOUSANDS SEPARATOR
         /* additional_morekeys_symbols_0 */ "0,\u066B,\u066C",
+        /* morekeys_tablet_period */ "!text/morekeys_arabic_diacritics",
         /* morekeys_nordic_row2_11 */ null,
         /* morekeys_punctuation */ null,
         // U+061F: "؟" ARABIC QUESTION MARK
         // U+060C: "،" ARABIC COMMA
         // U+061B: "؛" ARABIC SEMICOLON
         /* keyspec_tablet_comma */ "\u060C",
-        /* keyspec_swiss_row1_11 ~ */
-        null, null, null, null, null, null,
+        /* keyspec_period */ null,
+        /* morekeys_period */ "!text/morekeys_arabic_diacritics",
+        /* keyspec_tablet_period ~ */
+        null, null, null, null, null, null, null,
         /* ~ morekeys_swiss_row2_11 */
         // U+2605: "★" BLACK STAR
         // U+066D: "٭" ARABIC FIVE POINTED STAR
@@ -624,7 +628,6 @@
         /* morekeys_tablet_comma */ "!fixedColumnOrder!4,:,!,\u061F,\u061B,-,\",\'",
         // U+0651: "ّ" ARABIC SHADDA
         /* keyhintlabel_period */ "\u0651",
-        /* morekeys_tablet_period */ "!text/morekeys_arabic_diacritics",
         // U+00BF: "¿" INVERTED QUESTION MARK
         /* morekeys_question */ "?,\u00BF",
         /* morekeys_h ~ */
@@ -656,9 +659,6 @@
         // Note: The space character is needed as a preceding letter to draw Arabic diacritics characters correctly.
         /* morekeys_arabic_diacritics */ "!fixedColumnOrder!7, \u0655|\u0655, \u0654|\u0654, \u0652|\u0652, \u064D|\u064D, \u064C|\u064C, \u064B|\u064B, \u0651|\u0651, \u0656|\u0656, \u0670|\u0670, \u0653|\u0653, \u0650|\u0650, \u064F|\u064F, \u064E|\u064E,\u0640\u0640\u0640|\u0640",
         /* keyhintlabel_tablet_comma */ "\u061F",
-        /* keyspec_period */ null,
-        /* morekeys_period */ "!text/morekeys_arabic_diacritics",
-        /* keyspec_tablet_period */ null,
         /* keyhintlabel_tablet_period */ "\u0651",
         /* keyspec_symbols_question */ "\u061F",
         /* keyspec_symbols_semicolon */ "\u061B",
@@ -671,6 +671,7 @@
 
     /* Locale az_AZ: Azerbaijani (Azerbaijan) */
     private static final String[] TEXTS_az_AZ = {
+        // This is the same as Turkish
         // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
         // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
         // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
@@ -710,12 +711,12 @@
         // U+010D: "č" LATIN SMALL LETTER C WITH CARON
         /* morekeys_c */ "\u00E7,\u0107,\u010D",
         /* double_quotes */ null,
-        /* single_quotes */ null,
         // U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
         // U+00DF: "ß" LATIN SMALL LETTER SHARP S
         // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
         // U+0161: "š" LATIN SMALL LETTER S WITH CARON
         /* morekeys_s */ "\u015F,\u00DF,\u015B,\u0161",
+        /* single_quotes */ null,
         /* keyspec_currency */ null,
         // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
         /* morekeys_y */ "\u00FD",
@@ -742,9 +743,10 @@
         null, null, null,
         /* ~ morekeys_c */
         /* double_quotes */ "!text/double_9qm_lqm",
+        /* morekeys_s */ null,
         /* single_quotes */ "!text/single_9qm_lqm",
-        /* morekeys_s ~ */
-        null, null, null, null, null, null, null, null, null, null, null, null,
+        /* keyspec_currency ~ */
+        null, null, null, null, null, null, null, null, null, null, null,
         /* ~ morekeys_k */
         // U+0451: "ё" CYRILLIC SMALL LETTER IO
         /* morekeys_cyrillic_ie */ "\u0451",
@@ -780,6 +782,23 @@
         /* double_quotes */ "!text/double_9qm_lqm",
     };
 
+    /* Locale bn_BD: Bengali (Bangladesh) */
+    private static final String[] TEXTS_bn_BD = {
+        /* morekeys_a ~ */
+        null, null, null, null,
+        /* ~ morekeys_u */
+        // Label for "switch to alphabetic" key.
+        // U+0995: "क" BENGALI LETTER KA
+        // U+0996: "ख" BENGALI LETTER KHA
+        // U+0997: "ग" BENGALI LETTER GA
+        /* keylabel_to_alpha */ "\u0995\u0996\u0997",
+        /* morekeys_i ~ */
+        null, null, null, null, null, null,
+        /* ~ single_quotes */
+        // U+09F3: "৳" BENGALI RUPEE SIGN
+        /* keyspec_currency */ "\u09F3",
+    };
+
     /* Locale bn_IN: Bengali (India) */
     private static final String[] TEXTS_bn_IN = {
         /* morekeys_a ~ */
@@ -792,7 +811,7 @@
         /* keylabel_to_alpha */ "\u0995\u0996\u0997",
         /* morekeys_i ~ */
         null, null, null, null, null, null,
-        /* ~ morekeys_s */
+        /* ~ single_quotes */
         // U+20B9: "₹" INDIAN RUPEE SIGN
         /* keyspec_currency */ "\u20B9",
     };
@@ -858,14 +877,14 @@
         /* morekeys_g ~ */
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
-        null, null, null, null, null, null, null,
+        null, null, null, null, null, null, null, null,
         /* ~ morekeys_nordic_row2_11 */
         // U+00B7: "·" MIDDLE DOT
         /* morekeys_punctuation */ "!autoColumnOrder!9,\\,,?,!,\u00B7,#,),(,/,;,',@,:,-,\",+,\\%,&",
         /* keyspec_tablet_comma ~ */
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
-        null, null, null, null, null, null, null, null, null,
+        null, null, null, null, null, null, null, null, null, null, null,
         /* ~ keyspec_south_slavic_row3_8 */
         /* morekeys_tablet_punctuation */ "!autoColumnOrder!8,\\,,',\u00B7,#,),(,/,;,@,:,-,\",+,\\%,&",
         // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
@@ -925,11 +944,11 @@
         // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
         /* morekeys_c */ "\u010D,\u00E7,\u0107",
         /* double_quotes */ "!text/double_9qm_lqm",
-        /* single_quotes */ "!text/single_9qm_lqm",
         // U+0161: "š" LATIN SMALL LETTER S WITH CARON
         // U+00DF: "ß" LATIN SMALL LETTER SHARP S
         // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
         /* morekeys_s */ "\u0161,\u00DF,\u015B",
+        /* single_quotes */ "!text/single_9qm_lqm",
         /* keyspec_currency */ null,
         // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
         // U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
@@ -988,11 +1007,11 @@
         /* morekeys_n */ "\u00F1,\u0144",
         /* morekeys_c */ null,
         /* double_quotes */ "!text/double_9qm_lqm",
-        /* single_quotes */ "!text/single_9qm_lqm",
         // U+00DF: "ß" LATIN SMALL LETTER SHARP S
         // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
         // U+0161: "š" LATIN SMALL LETTER S WITH CARON
         /* morekeys_s */ "\u00DF,\u015B,\u0161",
+        /* single_quotes */ "!text/single_9qm_lqm",
         /* keyspec_currency */ null,
         // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
         // U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
@@ -1019,8 +1038,8 @@
         /* morekeys_nordic_row2_10 */ "\u00E4",
         /* keyspec_east_slavic_row1_9 ~ */
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
-        null, null, null, null, null, null, null, null, null, null, null,
-        /* ~ additional_morekeys_symbols_0 */
+        null, null, null, null, null, null, null, null, null, null, null, null,
+        /* ~ morekeys_tablet_period */
         // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
         /* morekeys_nordic_row2_11 */ "\u00F6",
     };
@@ -1064,11 +1083,11 @@
         /* morekeys_n */ "\u00F1,\u0144",
         /* morekeys_c */ null,
         /* double_quotes */ "!text/double_9qm_lqm",
-        /* single_quotes */ "!text/single_9qm_lqm",
         // U+00DF: "ß" LATIN SMALL LETTER SHARP S
         // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
         // U+0161: "š" LATIN SMALL LETTER S WITH CARON
         /* morekeys_s */ "\u00DF,\u015B,\u0161",
+        /* single_quotes */ "!text/single_9qm_lqm",
         /* keyspec_currency ~ */
         null, null, null, null, null, null, null,
         /* ~ morekeys_g */
@@ -1077,8 +1096,8 @@
         /* morekeys_r ~ */
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
-        null, null, null, null, null, null,
-        /* ~ keyspec_tablet_comma */
+        null, null, null, null, null, null, null, null, null, null,
+        /* ~ keyspec_tablet_period */
         // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
         /* keyspec_swiss_row1_11 */ "\u00FC",
         // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
@@ -1149,7 +1168,6 @@
         // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
         /* morekeys_c */ "\u00E7",
         /* double_quotes */ null,
-        /* single_quotes */ null,
         // U+00DF: "ß" LATIN SMALL LETTER SHARP S
         /* morekeys_s */ "\u00DF",
     };
@@ -1223,13 +1241,13 @@
         // U+010B: "ċ" LATIN SMALL LETTER C WITH DOT ABOVE
         /* morekeys_c */ "\u0107,\u010D,\u00E7,\u010B",
         /* double_quotes */ null,
-        /* single_quotes */ null,
         // U+00DF: "ß" LATIN SMALL LETTER SHARP S
         // U+0161: "š" LATIN SMALL LETTER S WITH CARON
         // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
         // U+0219: "ș" LATIN SMALL LETTER S WITH COMMA BELOW
         // U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
         /* morekeys_s */ "\u00DF,\u0161,\u015B,\u0219,\u015F",
+        /* single_quotes */ null,
         /* keyspec_currency */ null,
         // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
         // U+0177: "ŷ" LATIN SMALL LETTER Y WITH CIRCUMFLEX
@@ -1273,6 +1291,7 @@
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+        null, null, null,
         /* ~ morekeys_question */
         // U+0125: "ĥ" LATIN SMALL LETTER H WITH CIRCUMFLEX
         // U+0127: "ħ" LATIN SMALL LETTER H WITH STROKE
@@ -1286,7 +1305,7 @@
         /* keyspec_spanish_row2_10 */ "\u0135",
         /* morekeys_bullet ~ */
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
-        null, null, null, null, null, null, null,
+        null, null, null, null,
         /* ~ label_wait_key */
         // U+0175: "ŵ" LATIN SMALL LETTER W WITH CIRCUMFLEX
         /* morekeys_v */ "w,\u0175",
@@ -1359,7 +1378,7 @@
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
-        null,
+        null, null,
         /* ~ morekeys_nordic_row2_11 */
         // U+00A1: "¡" INVERTED EXCLAMATION MARK
         // U+00BF: "¿" INVERTED QUESTION MARK
@@ -1423,12 +1442,12 @@
         // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
         /* morekeys_c */ "\u010D,\u00E7,\u0107",
         /* double_quotes */ "!text/double_9qm_lqm",
-        /* single_quotes */ "!text/single_9qm_lqm",
         // U+0161: "š" LATIN SMALL LETTER S WITH CARON
         // U+00DF: "ß" LATIN SMALL LETTER SHARP S
         // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
         // U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
         /* morekeys_s */ "\u0161,\u00DF,\u015B,\u015F",
+        /* single_quotes */ "!text/single_9qm_lqm",
         /* keyspec_currency */ null,
         // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
         // U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
@@ -1536,7 +1555,7 @@
         /* keylabel_to_alpha */ "\u0627\u200C\u0628\u200C\u067E",
         /* morekeys_i ~ */
         null, null, null, null, null, null,
-        /* ~ morekeys_s */
+        /* ~ single_quotes */
         // U+FDFC: "﷼" RIAL SIGN
         /* keyspec_currency */ "\uFDFC",
         /* morekeys_y ~ */
@@ -1578,6 +1597,7 @@
         // U+066B: "٫" ARABIC DECIMAL SEPARATOR
         // U+066C: "٬" ARABIC THOUSANDS SEPARATOR
         /* additional_morekeys_symbols_0 */ "0,\u066B,\u066C",
+        /* morekeys_tablet_period */ "!text/morekeys_arabic_diacritics",
         /* morekeys_nordic_row2_11 */ null,
         /* morekeys_punctuation */ null,
         // U+060C: "،" ARABIC COMMA
@@ -1586,8 +1606,10 @@
         // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
         // U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
         /* keyspec_tablet_comma */ "\u060C",
-        /* keyspec_swiss_row1_11 ~ */
-        null, null, null, null, null, null,
+        /* keyspec_period */ null,
+        /* morekeys_period */ "!text/morekeys_arabic_diacritics",
+        /* keyspec_tablet_period ~ */
+        null, null, null, null, null, null, null,
         /* ~ morekeys_swiss_row2_11 */
         // U+2605: "★" BLACK STAR
         // U+066D: "٭" ARABIC FIVE POINTED STAR
@@ -1611,7 +1633,6 @@
         /* morekeys_tablet_comma */ "!fixedColumnOrder!4,:,!,\u061F,\u061B,-,!text/keyspec_left_double_angle_quote,!text/keyspec_right_double_angle_quote",
         // U+064B: "ً" ARABIC FATHATAN
         /* keyhintlabel_period */ "\u064B",
-        /* morekeys_tablet_period */ "!text/morekeys_arabic_diacritics",
         // U+00BF: "¿" INVERTED QUESTION MARK
         /* morekeys_question */ "?,\u00BF",
         /* morekeys_h ~ */
@@ -1643,9 +1664,6 @@
         // Note: The space character is needed as a preceding letter to draw Arabic diacritics characters correctly.
         /* morekeys_arabic_diacritics */ "!fixedColumnOrder!7, \u0655|\u0655, \u0652|\u0652, \u0651|\u0651, \u064C|\u064C, \u064D|\u064D, \u064B|\u064B, \u0654|\u0654, \u0656|\u0656, \u0670|\u0670, \u0653|\u0653, \u064F|\u064F, \u0650|\u0650, \u064E|\u064E,\u0640\u0640\u0640|\u0640",
         /* keyhintlabel_tablet_comma */ "\u061F",
-        /* keyspec_period */ null,
-        /* morekeys_period */ "!text/morekeys_arabic_diacritics",
-        /* keyspec_tablet_period */ null,
         /* keyhintlabel_tablet_period */ "\u064B",
         /* keyspec_symbols_question */ "\u061F",
         /* keyspec_symbols_semicolon */ "\u061B",
@@ -1692,14 +1710,15 @@
         // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
         /* morekeys_u */ "\u00FC",
         /* keylabel_to_alpha ~ */
-        null, null, null, null, null, null,
-        /* ~ single_quotes */
+        null, null, null, null, null,
+        /* ~ double_quotes */
         // U+0161: "š" LATIN SMALL LETTER S WITH CARON
         // U+00DF: "ß" LATIN SMALL LETTER SHARP S
         // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
         /* morekeys_s */ "\u0161,\u00DF,\u015B",
-        /* keyspec_currency */ null,
-        /* morekeys_y */ null,
+        /* single_quotes ~ */
+        null, null, null,
+        /* ~ morekeys_y */
         // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
         // U+017A: "ź" LATIN SMALL LETTER Z WITH ACUTE
         // U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE
@@ -1717,8 +1736,8 @@
         /* morekeys_nordic_row2_10 */ "\u00F8",
         /* keyspec_east_slavic_row1_9 ~ */
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
-        null, null, null, null, null, null, null, null, null, null, null,
-        /* ~ additional_morekeys_symbols_0 */
+        null, null, null, null, null, null, null, null, null, null, null, null,
+        /* ~ morekeys_tablet_period */
         // U+00E6: "æ" LATIN SMALL LETTER AE
         /* morekeys_nordic_row2_11 */ "\u00E6",
     };
@@ -1780,8 +1799,9 @@
         /* morekeys_z ~ */
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
-        null, null, null, null, null, null, null, null, null, null, null, null, null,
-        /* ~ keyspec_tablet_comma */
+        null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+        null, null,
+        /* ~ keyspec_tablet_period */
         // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
         /* keyspec_swiss_row1_11 */ "\u00E8",
         // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
@@ -1862,7 +1882,7 @@
         /* keylabel_to_alpha */ "\u0915\u0916\u0917",
         /* morekeys_i ~ */
         null, null, null, null, null, null,
-        /* ~ morekeys_s */
+        /* ~ single_quotes */
         // U+20B9: "₹" INDIAN RUPEE SIGN
         /* keyspec_currency */ "\u20B9",
         /* morekeys_y ~ */
@@ -1901,13 +1921,21 @@
         /* additional_morekeys_symbols_8 */ "8",
         /* additional_morekeys_symbols_9 */ "9",
         /* additional_morekeys_symbols_0 */ "0",
+        /* morekeys_tablet_period */ "!autoColumnOrder!8,\\,,.,',#,),(,/,;,@,:,-,\",+,\\%,&",
+        /* morekeys_nordic_row2_11 ~ */
+        null, null, null,
+        /* ~ keyspec_tablet_comma */
+        // U+0964: "।" DEVANAGARI DANDA
+        /* keyspec_period */ "\u0964",
+        /* morekeys_period */ "!autoColumnOrder!9,\\,,.,?,!,#,),(,/,;,',@,:,-,\",+,\\%,&",
+        /* keyspec_tablet_period */ "\u0964",
     };
 
     /* Locale hi_ZZ: Hindi (ZZ) */
     private static final String[] TEXTS_hi_ZZ = {
         /* morekeys_a ~ */
         null, null, null, null, null, null, null, null, null, null, null,
-        /* ~ morekeys_s */
+        /* ~ single_quotes */
         // U+20B9: "₹" INDIAN RUPEE SIGN
         /* keyspec_currency */ "\u20B9",
         /* morekeys_y ~ */
@@ -1942,11 +1970,11 @@
         // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
         /* morekeys_c */ "\u010D,\u0107,\u00E7",
         /* double_quotes */ "!text/double_9qm_rqm",
-        /* single_quotes */ "!text/single_9qm_rqm",
         // U+0161: "š" LATIN SMALL LETTER S WITH CARON
         // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
         // U+00DF: "ß" LATIN SMALL LETTER SHARP S
         /* morekeys_s */ "\u0161,\u015B,\u00DF",
+        /* single_quotes */ "!text/single_9qm_rqm",
         /* keyspec_currency */ null,
         /* morekeys_y */ null,
         // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
@@ -2009,9 +2037,10 @@
         /* morekeys_n */ null,
         /* morekeys_c */ null,
         /* double_quotes */ "!text/double_9qm_rqm",
+        /* morekeys_s */ null,
         /* single_quotes */ "!text/single_9qm_rqm",
-        /* morekeys_s ~ */
-        null, null, null, null, null, null, null, null,
+        /* keyspec_currency ~ */
+        null, null, null, null, null, null, null,
         /* ~ morekeys_g */
         /* single_angle_quotes */ "!text/single_raqm_laqm",
         /* double_angle_quotes */ "!text/double_raqm_laqm",
@@ -2031,8 +2060,10 @@
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
-        null, null, null, null,
-        /* ~ morekeys_nordic_row2_11 */
+        null, null, null,
+        /* ~ additional_morekeys_symbols_0 */
+        /* morekeys_tablet_period */ "!text/morekeys_punctuation",
+        /* morekeys_nordic_row2_11 */ null,
         // U+055E: "՞" ARMENIAN QUESTION MARK
         // U+055C: "՜" ARMENIAN EXCLAMATION MARK
         // U+055A: "՚" ARMENIAN APOSTROPHE
@@ -2045,6 +2076,10 @@
         // U+055F: "՟" ARMENIAN ABBREVIATION MARK
         /* morekeys_punctuation */ "!autoColumnOrder!8,\\,,\u055E,\u055C,.,\u055A,\u0559,?,!,\u055D,\u055B,\u058A,\u00BB,\u00AB,\u055F,;,:",
         /* keyspec_tablet_comma */ "\u055D",
+        // U+0589: "։" ARMENIAN FULL STOP
+        /* keyspec_period */ "\u0589",
+        /* morekeys_period */ null,
+        /* keyspec_tablet_period */ "\u0589",
         /* keyspec_swiss_row1_11 ~ */
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
         null, null, null, null, null, null,
@@ -2057,21 +2092,14 @@
         /* keyspec_comma */ "\u055D",
         /* morekeys_tablet_comma */ null,
         /* keyhintlabel_period */ null,
-        /* morekeys_tablet_period */ "!text/morekeys_punctuation",
         // U+055E: "՞" ARMENIAN QUESTION MARK
         // U+00BF: "¿" INVERTED QUESTION MARK
         /* morekeys_question */ "\u055E,\u00BF",
         /* morekeys_h ~ */
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
-        null, null, null, null,
-        /* ~ keyhintlabel_tablet_comma */
-        // U+0589: "։" ARMENIAN FULL STOP
-        /* keyspec_period */ "\u0589",
-        /* morekeys_period */ null,
-        /* keyspec_tablet_period */ "\u0589",
-        /* keyhintlabel_tablet_period ~ */
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
-        null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+        null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+        null, null, null,
         /* ~ morekeys_greater_than */
         // U+055C: "՜" ARMENIAN EXCLAMATION MARK
         // U+00A1: "¡" INVERTED EXCLAMATION MARK
@@ -2123,8 +2151,8 @@
         /* morekeys_n */ null,
         /* morekeys_c */ null,
         /* double_quotes */ "!text/double_9qm_lqm",
-        /* single_quotes */ "!text/single_9qm_lqm",
         /* morekeys_s */ null,
+        /* single_quotes */ "!text/single_9qm_lqm",
         /* keyspec_currency */ null,
         // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
         // U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
@@ -2184,8 +2212,8 @@
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
-        null, null, null, null, null,
-        /* ~ keyspec_tablet_comma */
+        null, null, null, null, null, null, null, null, null,
+        /* ~ keyspec_tablet_period */
         // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
         /* keyspec_swiss_row1_11 */ "\u00FC",
         // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
@@ -2214,15 +2242,15 @@
         null, null, null,
         /* ~ morekeys_c */
         /* double_quotes */ "!text/double_rqm_9qm",
-        /* single_quotes */ "!text/single_rqm_9qm",
         /* morekeys_s */ null,
+        /* single_quotes */ "!text/single_rqm_9qm",
         // U+20AA: "₪" NEW SHEQEL SIGN
         /* keyspec_currency */ "\u20AA",
         /* morekeys_y ~ */
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
-        null, null, null, null, null,
+        null, null, null, null, null, null, null, null, null,
         /* ~ morekeys_swiss_row2_11 */
         // U+2605: "★" BLACK STAR
         /* morekeys_star */ "\u2605",
@@ -2252,7 +2280,7 @@
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
-        null, null, null, null, null, null, null, null,
+        null, null, null, null,
         /* ~ morekeys_currency_dollar */
         // U+00B1: "±" PLUS-MINUS SIGN
         // U+FB29: "﬩" HEBREW LETTER ALTERNATIVE PLUS SIGN
@@ -2273,6 +2301,7 @@
         null, null, null,
         /* ~ morekeys_c */
         /* double_quotes */ "!text/double_9qm_lqm",
+        /* morekeys_s */ null,
         /* single_quotes */ "!text/single_9qm_lqm",
     };
 
@@ -2309,7 +2338,7 @@
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
-        null, null, null, null, null, null, null,
+        null, null, null, null, null, null, null, null, null, null,
         /* ~ morekeys_w */
         // U+0456: "і" CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
         /* morekeys_east_slavic_row2_2 */ "\u0456",
@@ -2325,7 +2354,7 @@
         /* morekeys_cyrillic_i ~ */
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
-        null, null, null, null, null, null, null,
+        null, null, null, null,
         /* ~ keyspec_x */
         // U+04BB: "һ" CYRILLIC SMALL LETTER SHHA
         /* morekeys_east_slavic_row2_11 */ "\u04BB",
@@ -2372,7 +2401,7 @@
         /* keylabel_to_alpha */ "\u0C85\u0C86\u0C87",
         /* morekeys_i ~ */
         null, null, null, null, null, null,
-        /* ~ morekeys_s */
+        /* ~ single_quotes */
         // U+20B9: "₹" INDIAN RUPEE SIGN
         /* keyspec_currency */ "\u20B9",
     };
@@ -2410,7 +2439,7 @@
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
-        null, null, null, null, null, null, null, null,
+        null, null, null, null, null, null, null, null, null, null, null,
         /* ~ morekeys_east_slavic_row2_2 */
         // U+04AF: "ү" CYRILLIC SMALL LETTER STRAIGHT U
         /* morekeys_cyrillic_u */ "\u04AF",
@@ -2433,7 +2462,7 @@
         /* keylabel_to_alpha */ "\u0E81\u0E82\u0E84",
         /* morekeys_i ~ */
         null, null, null, null, null, null,
-        /* ~ morekeys_s */
+        /* ~ single_quotes */
         // U+20AD: "₭" KIP SIGN
         /* keyspec_currency */ "\u20AD",
     };
@@ -2496,12 +2525,12 @@
         // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
         /* morekeys_c */ "\u010D,\u00E7,\u0107",
         /* double_quotes */ "!text/double_9qm_lqm",
-        /* single_quotes */ "!text/single_9qm_lqm",
         // U+0161: "š" LATIN SMALL LETTER S WITH CARON
         // U+00DF: "ß" LATIN SMALL LETTER SHARP S
         // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
         // U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
         /* morekeys_s */ "\u0161,\u00DF,\u015B,\u015F",
+        /* single_quotes */ "!text/single_9qm_lqm",
         /* keyspec_currency */ null,
         // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
         // U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
@@ -2590,12 +2619,12 @@
         // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
         /* morekeys_c */ "\u010D,\u00E7,\u0107",
         /* double_quotes */ "!text/double_9qm_lqm",
-        /* single_quotes */ "!text/single_9qm_lqm",
         // U+0161: "š" LATIN SMALL LETTER S WITH CARON
         // U+00DF: "ß" LATIN SMALL LETTER SHARP S
         // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
         // U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
         /* morekeys_s */ "\u0161,\u00DF,\u015B,\u015F",
+        /* single_quotes */ "!text/single_9qm_lqm",
         /* keyspec_currency */ null,
         // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
         // U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
@@ -2641,9 +2670,10 @@
         null, null, null,
         /* ~ morekeys_c */
         /* double_quotes */ "!text/double_9qm_lqm",
+        /* morekeys_s */ null,
         /* single_quotes */ "!text/single_9qm_lqm",
-        /* morekeys_s ~ */
-        null, null, null, null, null, null, null, null, null, null, null, null,
+        /* keyspec_currency ~ */
+        null, null, null, null, null, null, null, null, null, null, null,
         /* ~ morekeys_k */
         // U+0450: "ѐ" CYRILLIC SMALL LETTER IE WITH GRAVE
         /* morekeys_cyrillic_ie */ "\u0450",
@@ -2652,7 +2682,7 @@
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
-        null, null, null, null, null, null,
+        null, null, null, null, null, null, null, null, null,
         /* ~ morekeys_cyrillic_o */
         // U+045D: "ѝ" CYRILLIC SMALL LETTER I WITH GRAVE
         /* morekeys_cyrillic_i */ "\u045D",
@@ -2676,7 +2706,7 @@
         /* keylabel_to_alpha */ "\u0D05",
         /* morekeys_i ~ */
         null, null, null, null, null, null,
-        /* ~ morekeys_s */
+        /* ~ single_quotes */
         // U+20B9: "₹" INDIAN RUPEE SIGN
         /* keyspec_currency */ "\u20B9",
     };
@@ -2693,7 +2723,7 @@
         /* keylabel_to_alpha */ "\u0410\u0411\u0412",
         /* morekeys_i ~ */
         null, null, null, null, null, null,
-        /* ~ morekeys_s */
+        /* ~ single_quotes */
         // U+20AE: "₮" TUGRIK SIGN
         /* keyspec_currency */ "\u20AE",
     };
@@ -2710,7 +2740,7 @@
         /* keylabel_to_alpha */ "\u0915\u0916\u0917",
         /* morekeys_i ~ */
         null, null, null, null, null, null,
-        /* ~ morekeys_s */
+        /* ~ single_quotes */
         // U+20B9: "₹" INDIAN RUPEE SIGN
         /* keyspec_currency */ "\u20B9",
         /* morekeys_y ~ */
@@ -2765,28 +2795,25 @@
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
-        null, null, null, null,
+        null, null, null, null, null,
         /* ~ morekeys_nordic_row2_11 */
         /* morekeys_punctuation */ "!autoColumnOrder!9,\u104A,.,?,!,#,),(,/,;,...,',@,:,-,\",+,\\%,&",
         // U+104A: "၊" MYANMAR SIGN LITTLE SECTION
         // U+104B: "။" MYANMAR SIGN SECTION
         /* keyspec_tablet_comma */ "\u104A",
+        /* keyspec_period */ "\u104B",
+        /* morekeys_period */ null,
+        /* keyspec_tablet_period */ "\u104B",
         /* keyspec_swiss_row1_11 ~ */
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
         null, null, null, null, null, null, null,
         /* ~ keyspec_comma */
         /* morekeys_tablet_comma */ "\\,",
         /* keyhintlabel_period */ "\u104A",
-        /* morekeys_tablet_period ~ */
-        null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+        /* morekeys_question ~ */
+        null, null, null, null, null, null, null, null, null, null, null, null, null,
         /* ~ keyspec_south_slavic_row3_8 */
         /* morekeys_tablet_punctuation */ "!autoColumnOrder!8,.,',#,),(,/,;,@,...,:,-,\",+,\\%,&",
-        /* keyspec_spanish_row2_10 ~ */
-        null, null, null, null, null, null,
-        /* ~ keyhintlabel_tablet_comma */
-        /* keyspec_period */ "\u104B",
-        /* morekeys_period */ null,
-        /* keyspec_tablet_period */ "\u104B",
     };
 
     /* Locale nb: Norwegian Bokmål */
@@ -2827,9 +2854,10 @@
         null, null, null, null,
         /* ~ morekeys_c */
         /* double_quotes */ "!text/double_9qm_rqm",
+        /* morekeys_s */ null,
         /* single_quotes */ "!text/single_9qm_rqm",
-        /* morekeys_s ~ */
-        null, null, null, null, null, null, null, null, null, null, null, null, null,
+        /* keyspec_currency ~ */
+        null, null, null, null, null, null, null, null, null, null, null, null,
         /* ~ morekeys_cyrillic_ie */
         // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
         /* keyspec_nordic_row1_11 */ "\u00E5",
@@ -2841,8 +2869,8 @@
         /* morekeys_nordic_row2_10 */ "\u00F6",
         /* keyspec_east_slavic_row1_9 ~ */
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
-        null, null, null, null, null, null, null, null, null, null, null,
-        /* ~ additional_morekeys_symbols_0 */
+        null, null, null, null, null, null, null, null, null, null, null, null,
+        /* ~ morekeys_tablet_period */
         // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
         /* morekeys_nordic_row2_11 */ "\u00E4",
     };
@@ -2859,7 +2887,7 @@
         /* keylabel_to_alpha */ "\u0915\u0916\u0917",
         /* morekeys_i ~ */
         null, null, null, null, null, null,
-        /* ~ morekeys_s */
+        /* ~ single_quotes */
         // U+0930/U+0941/U+002E "रु." NEPALESE RUPEE SIGN
         /* keyspec_currency */ "\u0930\u0941.",
         /* morekeys_y ~ */
@@ -2898,6 +2926,14 @@
         /* additional_morekeys_symbols_8 */ "8",
         /* additional_morekeys_symbols_9 */ "9",
         /* additional_morekeys_symbols_0 */ "0",
+        /* morekeys_tablet_period */ "!autoColumnOrder!8,.,\\,,',#,),(,/,;,@,:,-,\",+,\\%,&",
+        /* morekeys_nordic_row2_11 ~ */
+        null, null, null,
+        /* ~ keyspec_tablet_comma */
+        // U+0964: "।" DEVANAGARI DANDA
+        /* keyspec_period */ "\u0964",
+        /* morekeys_period */ "!autoColumnOrder!9,.,\\,,?,!,#,),(,/,;,',@,:,-,\",+,\\%,&",
+        /* keyspec_tablet_period */ "\u0964",
     };
 
     /* Locale nl: Dutch */
@@ -2948,8 +2984,8 @@
         /* morekeys_n */ "\u00F1,\u0144",
         /* morekeys_c */ null,
         /* double_quotes */ "!text/double_9qm_rqm",
-        /* single_quotes */ "!text/single_9qm_rqm",
         /* morekeys_s */ null,
+        /* single_quotes */ "!text/single_9qm_rqm",
         /* keyspec_currency */ null,
         // U+0133: "ij" LATIN SMALL LIGATURE IJ
         /* morekeys_y */ "\u0133",
@@ -2995,11 +3031,11 @@
         // U+010D: "č" LATIN SMALL LETTER C WITH CARON
         /* morekeys_c */ "\u0107,\u00E7,\u010D",
         /* double_quotes */ "!text/double_9qm_rqm",
-        /* single_quotes */ "!text/single_9qm_rqm",
         // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
         // U+00DF: "ß" LATIN SMALL LETTER SHARP S
         // U+0161: "š" LATIN SMALL LETTER S WITH CARON
         /* morekeys_s */ "\u015B,\u00DF,\u0161",
+        /* single_quotes */ "!text/single_9qm_rqm",
         /* keyspec_currency */ null,
         /* morekeys_y */ null,
         // U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE
@@ -3100,12 +3136,12 @@
         /* morekeys_n */ null,
         /* morekeys_c */ null,
         /* double_quotes */ "!text/double_9qm_rqm",
-        /* single_quotes */ "!text/single_9qm_rqm",
         // U+0219: "ș" LATIN SMALL LETTER S WITH COMMA BELOW
         // U+00DF: "ß" LATIN SMALL LETTER SHARP S
         // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
         // U+0161: "š" LATIN SMALL LETTER S WITH CARON
         /* morekeys_s */ "\u0219,\u00DF,\u015B,\u0161",
+        /* single_quotes */ "!text/single_9qm_rqm",
         /* keyspec_currency ~ */
         null, null, null, null,
         /* ~ morekeys_d */
@@ -3127,9 +3163,10 @@
         null, null, null,
         /* ~ morekeys_c */
         /* double_quotes */ "!text/double_9qm_lqm",
+        /* morekeys_s */ null,
         /* single_quotes */ "!text/single_9qm_lqm",
-        /* morekeys_s ~ */
-        null, null, null, null, null, null, null, null, null, null, null, null,
+        /* keyspec_currency ~ */
+        null, null, null, null, null, null, null, null, null, null, null,
         /* ~ morekeys_k */
         // U+0451: "ё" CYRILLIC SMALL LETTER IO
         /* morekeys_cyrillic_ie */ "\u0451",
@@ -3159,7 +3196,7 @@
         /* keylabel_to_alpha */ "\u0D85,\u0D86",
         /* morekeys_i ~ */
         null, null, null, null, null, null,
-        /* ~ morekeys_s */
+        /* ~ single_quotes */
         // U+0DBB/U+0DD4: "රු" SINHALA LETTER RAYANNA/SINHALA VOWEL SIGN KETTI PAA-PILLA
         /* keyspec_currency */ "\u0DBB\u0DD4",
     };
@@ -3222,12 +3259,12 @@
         // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
         /* morekeys_c */ "\u010D,\u00E7,\u0107",
         /* double_quotes */ "!text/double_9qm_lqm",
-        /* single_quotes */ "!text/single_9qm_lqm",
         // U+0161: "š" LATIN SMALL LETTER S WITH CARON
         // U+00DF: "ß" LATIN SMALL LETTER SHARP S
         // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
         // U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
         /* morekeys_s */ "\u0161,\u00DF,\u015B,\u015F",
+        /* single_quotes */ "!text/single_9qm_lqm",
         /* keyspec_currency */ null,
         // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
         // U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
@@ -3268,9 +3305,9 @@
         // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
         /* morekeys_c */ "\u010D,\u0107",
         /* double_quotes */ "!text/double_9qm_lqm",
-        /* single_quotes */ "!text/single_9qm_lqm",
         // U+0161: "š" LATIN SMALL LETTER S WITH CARON
         /* morekeys_s */ "\u0161",
+        /* single_quotes */ "!text/single_9qm_lqm",
         /* keyspec_currency */ null,
         /* morekeys_y */ null,
         // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
@@ -3299,9 +3336,10 @@
         null, null, null,
         /* ~ morekeys_c */
         /* double_quotes */ "!text/double_9qm_lqm",
+        /* morekeys_s */ null,
         /* single_quotes */ "!text/single_9qm_lqm",
-        /* morekeys_s ~ */
-        null, null, null, null, null, null, null, null,
+        /* keyspec_currency ~ */
+        null, null, null, null, null, null, null,
         /* ~ morekeys_g */
         /* single_angle_quotes */ "!text/single_raqm_laqm",
         /* double_angle_quotes */ "!text/double_raqm_laqm",
@@ -3314,7 +3352,7 @@
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
-        null, null, null, null, null, null,
+        null, null, null, null, null, null, null, null, null,
         /* ~ morekeys_cyrillic_o */
         // U+045D: "ѝ" CYRILLIC SMALL LETTER I WITH GRAVE
         /* morekeys_cyrillic_i */ "\u045D",
@@ -3361,11 +3399,11 @@
         // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
         /* morekeys_c */ "\u010D,\u0107,%",
         /* double_quotes */ null,
-        /* single_quotes */ null,
         // U+0161: "š" LATIN SMALL LETTER S WITH CARON
         /* morekeys_s */ "\u0161,%",
-        /* keyspec_currency */ null,
-        /* morekeys_y */ null,
+        /* single_quotes ~ */
+        null, null, null,
+        /* ~ morekeys_y */
         // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
         /* morekeys_z */ "\u017E,%",
         // U+0111: "đ" LATIN SMALL LETTER D WITH STROKE
@@ -3436,12 +3474,12 @@
         // U+010D: "č" LATIN SMALL LETTER C WITH CARON
         /* morekeys_c */ "\u00E7,\u0107,\u010D",
         /* double_quotes */ null,
-        /* single_quotes */ null,
         // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
         // U+0161: "š" LATIN SMALL LETTER S WITH CARON
         // U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
         // U+00DF: "ß" LATIN SMALL LETTER SHARP S
         /* morekeys_s */ "\u015B,\u0161,\u015F,\u00DF",
+        /* single_quotes */ null,
         /* keyspec_currency */ null,
         // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
         // U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
@@ -3476,8 +3514,8 @@
         /* morekeys_nordic_row2_10 */ "\u00F8,\u0153",
         /* keyspec_east_slavic_row1_9 ~ */
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
-        null, null, null, null, null, null, null, null, null, null, null,
-        /* ~ additional_morekeys_symbols_0 */
+        null, null, null, null, null, null, null, null, null, null, null, null,
+        /* ~ morekeys_tablet_period */
         // U+00E6: "æ" LATIN SMALL LETTER AE
         /* morekeys_nordic_row2_11 */ "\u00E6",
     };
@@ -3527,11 +3565,10 @@
         // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
         /* morekeys_c */ "\u00E7",
         /* double_quotes */ null,
-        /* single_quotes */ null,
         // U+00DF: "ß" LATIN SMALL LETTER SHARP S
         /* morekeys_s */ "\u00DF",
-        /* keyspec_currency ~ */
-        null, null, null, null, null, null,
+        /* single_quotes ~ */
+        null, null, null, null, null, null, null,
         /* ~ morekeys_l */
         /* morekeys_g */ "g\'",
     };
@@ -3548,7 +3585,7 @@
         /* keylabel_to_alpha */ "\u0BA4\u0BAE\u0BBF\u0BB4\u0BCD",
         /* morekeys_i ~ */
         null, null, null, null, null, null,
-        /* ~ morekeys_s */
+        /* ~ single_quotes */
         // U+0BF9: "௹" TAMIL RUPEE SIGN
         /* keyspec_currency */ "\u0BF9",
     };
@@ -3565,7 +3602,7 @@
         /* keylabel_to_alpha */ "\u0BA4\u0BAE\u0BBF\u0BB4\u0BCD",
         /* morekeys_i ~ */
         null, null, null, null, null, null,
-        /* ~ morekeys_s */
+        /* ~ single_quotes */
         // U+0DBB/U+0DD4: "රු" SINHALA LETTER RAYANNA/SINHALA VOWEL SIGN KETTI PAA-PILLA
         /* keyspec_currency */ "\u0DBB\u0DD4",
     };
@@ -3594,7 +3631,7 @@
         /* keylabel_to_alpha */ "\u0C05\u0C06\u0C07",
         /* morekeys_i ~ */
         null, null, null, null, null, null,
-        /* ~ morekeys_s */
+        /* ~ single_quotes */
         // U+20B9: "₹" INDIAN RUPEE SIGN
         /* keyspec_currency */ "\u20B9",
     };
@@ -3611,7 +3648,7 @@
         /* keylabel_to_alpha */ "\u0E01\u0E02\u0E04",
         /* morekeys_i ~ */
         null, null, null, null, null, null,
-        /* ~ morekeys_s */
+        /* ~ single_quotes */
         // U+0E3F: "฿" THAI CURRENCY SYMBOL BAHT
         /* keyspec_currency */ "\u0E3F",
     };
@@ -3711,12 +3748,12 @@
         // U+010D: "č" LATIN SMALL LETTER C WITH CARON
         /* morekeys_c */ "\u00E7,\u0107,\u010D",
         /* double_quotes */ null,
-        /* single_quotes */ null,
         // U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
         // U+00DF: "ß" LATIN SMALL LETTER SHARP S
         // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
         // U+0161: "š" LATIN SMALL LETTER S WITH CARON
         /* morekeys_s */ "\u015F,\u00DF,\u015B,\u0161",
+        /* single_quotes */ null,
         /* keyspec_currency */ null,
         // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
         /* morekeys_y */ "\u00FD",
@@ -3743,8 +3780,8 @@
         null, null, null,
         /* ~ morekeys_c */
         /* double_quotes */ "!text/double_9qm_lqm",
-        /* single_quotes */ "!text/single_9qm_lqm",
         /* morekeys_s */ null,
+        /* single_quotes */ "!text/single_9qm_lqm",
         // U+20B4: "₴" HRYVNIA SIGN
         /* keyspec_currency */ "\u20B4",
         /* morekeys_y ~ */
@@ -3764,7 +3801,7 @@
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
-        null, null, null, null, null, null, null,
+        null, null, null, null, null, null, null, null, null, null,
         /* ~ morekeys_w */
         // U+0457: "ї" CYRILLIC SMALL LETTER YI
         /* morekeys_east_slavic_row2_2 */ "\u0457",
@@ -3774,6 +3811,66 @@
         /* morekeys_cyrillic_ghe */ "\u0491",
     };
 
+    /* Locale uz_UZ: Uzbek (Uzbekistan) */
+    private static final String[] TEXTS_uz_UZ = {
+        // This is the same as Turkish
+        // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+        // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+        // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+        /* morekeys_a */ "\u00E2,\u00E4,\u00E1",
+        // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+        // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+        // U+0153: "œ" LATIN SMALL LIGATURE OE
+        // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+        // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+        // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+        // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+        // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
+        /* morekeys_o */ "\u00F6,\u00F4,\u0153,\u00F2,\u00F3,\u00F5,\u00F8,\u014D",
+        // U+0259: "ə" LATIN SMALL LETTER SCHWA
+        // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+        /* morekeys_e */ "\u0259,\u00E9",
+        // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+        // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
+        // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+        // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+        // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
+        /* morekeys_u */ "\u00FC,\u00FB,\u00F9,\u00FA,\u016B",
+        /* keylabel_to_alpha */ null,
+        // U+0131: "ı" LATIN SMALL LETTER DOTLESS I
+        // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+        // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+        // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+        // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+        // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
+        // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
+        /* morekeys_i */ "\u0131,\u00EE,\u00EF,\u00EC,\u00ED,\u012F,\u012B",
+        // U+0148: "ň" LATIN SMALL LETTER N WITH CARON
+        // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+        /* morekeys_n */ "\u0148,\u00F1",
+        // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
+        // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
+        // U+010D: "č" LATIN SMALL LETTER C WITH CARON
+        /* morekeys_c */ "\u00E7,\u0107,\u010D",
+        /* double_quotes */ null,
+        // U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
+        // U+00DF: "ß" LATIN SMALL LETTER SHARP S
+        // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
+        // U+0161: "š" LATIN SMALL LETTER S WITH CARON
+        /* morekeys_s */ "\u015F,\u00DF,\u015B,\u0161",
+        /* single_quotes */ null,
+        /* keyspec_currency */ null,
+        // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
+        /* morekeys_y */ "\u00FD",
+        // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
+        /* morekeys_z */ "\u017E",
+        /* morekeys_d ~ */
+        null, null, null,
+        /* ~ morekeys_l */
+        // U+011F: "ğ" LATIN SMALL LETTER G WITH BREVE
+        /* morekeys_g */ "\u011F",
+    };
+
     /* Locale vi: Vietnamese */
     private static final String[] TEXTS_vi = {
         // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
@@ -3845,7 +3942,7 @@
         /* morekeys_i */ "\u00EC,\u00ED,\u1EC9,\u0129,\u1ECB",
         /* morekeys_n ~ */
         null, null, null, null, null,
-        /* ~ morekeys_s */
+        /* ~ single_quotes */
         // U+20AB: "₫" DONG SIGN
         /* keyspec_currency */ "\u20AB",
         // U+1EF3: "ỳ" LATIN SMALL LETTER Y WITH GRAVE
@@ -3904,7 +4001,6 @@
         // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
         /* morekeys_c */ "\u00E7",
         /* double_quotes */ null,
-        /* single_quotes */ null,
         // U+00DF: "ß" LATIN SMALL LETTER SHARP S
         /* morekeys_s */ "\u00DF",
     };
@@ -3982,7 +4078,6 @@
         // U+010D: "č" LATIN SMALL LETTER C WITH CARON
         /* morekeys_c */ "\u00E7,\u0107,\u0109,\u010B,\u010D",
         /* double_quotes */ null,
-        /* single_quotes */ null,
         // U+00DF: "ß" LATIN SMALL LETTER SHARP S
         // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
         // U+015D: "ŝ" LATIN SMALL LETTER S WITH CIRCUMFLEX
@@ -3990,6 +4085,7 @@
         // U+0161: "š" LATIN SMALL LETTER S WITH CARON
         // U+017F: "ſ" LATIN SMALL LETTER LONG S
         /* morekeys_s */ "\u00DF,\u015B,\u015D,\u015F,\u0161,\u017F",
+        /* single_quotes */ null,
         /* keyspec_currency */ null,
         // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
         // U+0177: "ŷ" LATIN SMALL LETTER Y WITH CIRCUMFLEX
@@ -4034,6 +4130,7 @@
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+        null, null, null,
         /* ~ morekeys_question */
         // U+0125: "ĥ" LATIN SMALL LETTER H WITH CIRCUMFLEX
         /* morekeys_h */ "\u0125",
@@ -4042,7 +4139,7 @@
         /* morekeys_east_slavic_row2_2 ~ */
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
-        null, null, null, null, null,
+        null, null,
         /* ~ morekeys_v */
         // U+0135: "ĵ" LATIN SMALL LETTER J WITH CIRCUMFLEX
         /* morekeys_j */ "\u0135",
@@ -4056,44 +4153,45 @@
         "az_AZ"  , TEXTS_az_AZ, /*  11/ 18 Azerbaijani (Azerbaijan) */
         "be_BY"  , TEXTS_be_BY, /*   9/ 32 Belarusian (Belarus) */
         "bg"     , TEXTS_bg,    /*   2/  9 Bulgarian */
+        "bn_BD"  , TEXTS_bn_BD, /*   2/ 12 Bengali (Bangladesh) */
         "bn_IN"  , TEXTS_bn_IN, /*   2/ 12 Bengali (India) */
-        "ca"     , TEXTS_ca,    /*  11/ 96 Catalan */
+        "ca"     , TEXTS_ca,    /*  11/ 99 Catalan */
         "cs"     , TEXTS_cs,    /*  17/ 21 Czech */
-        "da"     , TEXTS_da,    /*  19/ 54 Danish */
-        "de"     , TEXTS_de,    /*  16/ 62 German */
+        "da"     , TEXTS_da,    /*  19/ 55 Danish */
+        "de"     , TEXTS_de,    /*  16/ 66 German */
         "el"     , TEXTS_el,    /*   1/  5 Greek */
-        "en"     , TEXTS_en,    /*   8/ 11 English */
+        "en"     , TEXTS_en,    /*   8/ 10 English */
         "eo"     , TEXTS_eo,    /*  26/126 Esperanto */
-        "es"     , TEXTS_es,    /*   8/ 55 Spanish */
+        "es"     , TEXTS_es,    /*   8/ 56 Spanish */
         "et_EE"  , TEXTS_et_EE, /*  22/ 27 Estonian (Estonia) */
         "eu_ES"  , TEXTS_eu_ES, /*   7/  8 Basque (Spain) */
         "fa"     , TEXTS_fa,    /*  58/133 Persian */
-        "fi"     , TEXTS_fi,    /*  10/ 54 Finnish */
-        "fr"     , TEXTS_fr,    /*  13/ 62 French */
+        "fi"     , TEXTS_fi,    /*  10/ 55 Finnish */
+        "fr"     , TEXTS_fr,    /*  13/ 66 French */
         "gl_ES"  , TEXTS_gl_ES, /*   7/  8 Gallegan (Spain) */
-        "hi"     , TEXTS_hi,    /*  23/ 53 Hindi */
+        "hi"     , TEXTS_hi,    /*  27/ 60 Hindi */
         "hi_ZZ"  , TEXTS_hi_ZZ, /*   9/118 Hindi (ZZ) */
         "hr"     , TEXTS_hr,    /*   9/ 20 Croatian */
         "hu"     , TEXTS_hu,    /*   9/ 20 Hungarian */
         "hy_AM"  , TEXTS_hy_AM, /*   9/134 Armenian (Armenia) */
         "is"     , TEXTS_is,    /*  10/ 16 Icelandic */
-        "it"     , TEXTS_it,    /*  11/ 62 Italian */
+        "it"     , TEXTS_it,    /*  11/ 66 Italian */
         "iw"     , TEXTS_iw,    /*  20/131 Hebrew */
-        "ka_GE"  , TEXTS_ka_GE, /*   3/ 10 Georgian (Georgia) */
+        "ka_GE"  , TEXTS_ka_GE, /*   3/ 11 Georgian (Georgia) */
         "kk"     , TEXTS_kk,    /*  15/129 Kazakh */
         "km_KH"  , TEXTS_km_KH, /*   2/130 Khmer (Cambodia) */
         "kn_IN"  , TEXTS_kn_IN, /*   2/ 12 Kannada (India) */
-        "ky"     , TEXTS_ky,    /*  10/ 89 Kirghiz */
+        "ky"     , TEXTS_ky,    /*  10/ 92 Kirghiz */
         "lo_LA"  , TEXTS_lo_LA, /*   2/ 12 Lao (Laos) */
         "lt"     , TEXTS_lt,    /*  18/ 22 Lithuanian */
         "lv"     , TEXTS_lv,    /*  18/ 22 Latvian */
-        "mk"     , TEXTS_mk,    /*   9/ 94 Macedonian */
+        "mk"     , TEXTS_mk,    /*   9/ 97 Macedonian */
         "ml_IN"  , TEXTS_ml_IN, /*   2/ 12 Malayalam (India) */
         "mn_MN"  , TEXTS_mn_MN, /*   2/ 12 Mongolian (Mongolia) */
         "mr_IN"  , TEXTS_mr_IN, /*  23/ 53 Marathi (India) */
-        "my_MM"  , TEXTS_my_MM, /*   8/104 Burmese (Myanmar) */
-        "nb"     , TEXTS_nb,    /*  11/ 54 Norwegian Bokmål */
-        "ne_NP"  , TEXTS_ne_NP, /*  23/ 53 Nepali (Nepal) */
+        "my_MM"  , TEXTS_my_MM, /*   8/ 98 Burmese (Myanmar) */
+        "nb"     , TEXTS_nb,    /*  11/ 55 Norwegian Bokmål */
+        "ne_NP"  , TEXTS_ne_NP, /*  27/ 60 Nepali (Nepal) */
         "nl"     , TEXTS_nl,    /*   9/ 13 Dutch */
         "pl"     , TEXTS_pl,    /*  10/ 17 Polish */
         "pt"     , TEXTS_pt,    /*   6/  8 Portuguese */
@@ -4103,9 +4201,9 @@
         "si_LK"  , TEXTS_si_LK, /*   2/ 12 Sinhalese (Sri Lanka) */
         "sk"     , TEXTS_sk,    /*  20/ 22 Slovak */
         "sl"     , TEXTS_sl,    /*   8/ 20 Slovenian */
-        "sr"     , TEXTS_sr,    /*  11/ 94 Serbian */
+        "sr"     , TEXTS_sr,    /*  11/ 97 Serbian */
         "sr_ZZ"  , TEXTS_sr_ZZ, /*  14/118 Serbian (ZZ) */
-        "sv"     , TEXTS_sv,    /*  21/ 54 Swedish */
+        "sv"     , TEXTS_sv,    /*  21/ 55 Swedish */
         "sw"     , TEXTS_sw,    /*   9/ 18 Swahili */
         "ta_IN"  , TEXTS_ta_IN, /*   2/ 12 Tamil (India) */
         "ta_LK"  , TEXTS_ta_LK, /*   2/ 12 Tamil (Sri Lanka) */
@@ -4114,9 +4212,10 @@
         "th"     , TEXTS_th,    /*   2/ 12 Thai */
         "tl"     , TEXTS_tl,    /*   7/  8 Tagalog */
         "tr"     , TEXTS_tr,    /*  11/ 18 Turkish */
-        "uk"     , TEXTS_uk,    /*  11/ 88 Ukrainian */
+        "uk"     , TEXTS_uk,    /*  11/ 91 Ukrainian */
+        "uz_UZ"  , TEXTS_uz_UZ, /*  11/ 18 Uzbek (Uzbekistan) */
         "vi"     , TEXTS_vi,    /*   8/ 15 Vietnamese */
-        "zu"     , TEXTS_zu,    /*   8/ 11 Zulu */
+        "zu"     , TEXTS_zu,    /*   8/ 10 Zulu */
         "zz"     , TEXTS_zz,    /*  19/120 Alphabet */
     };
 
diff --git a/java/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelper.java b/java/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelper.java
index 72ad2bd..21eaed9 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelper.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelper.java
@@ -23,6 +23,7 @@
 
 import java.util.Collections;
 import java.util.List;
+import java.util.Locale;
 
 /**
  * This class determines that the language name on the spacebar should be displayed in what format.
@@ -31,6 +32,7 @@
     public static final int FORMAT_TYPE_NONE = 0;
     public static final int FORMAT_TYPE_LANGUAGE_ONLY = 1;
     public static final int FORMAT_TYPE_FULL_LOCALE = 2;
+    public static final int FORMAT_TYPE_MULTIPLE = 3;
 
     private List<InputMethodSubtype> mEnabledSubtypes = Collections.emptyList();
     private boolean mIsSystemLanguageSameAsInputLanguage;
@@ -43,7 +45,11 @@
         if (mEnabledSubtypes.size() < 2 && mIsSystemLanguageSameAsInputLanguage) {
             return FORMAT_TYPE_NONE;
         }
-        final String keyboardLanguage = SubtypeLocaleUtils.getSubtypeLocale(subtype).getLanguage();
+        final Locale[] locales = subtype.getLocales();
+        if (1 < locales.length) {
+            return FORMAT_TYPE_MULTIPLE;
+        }
+        final String keyboardLanguage = locales[0].getLanguage();
         final String keyboardLayout = SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype);
         int sameLanguageAndLayoutCount = 0;
         for (final InputMethodSubtype ims : mEnabledSubtypes) {
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index 1da33ed..10dea74 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -42,6 +42,8 @@
 import java.util.Locale;
 import java.util.Map;
 
+import javax.annotation.Nonnull;
+
 /**
  * Implements a static, compacted, binary dictionary of standard words.
  */
@@ -199,6 +201,10 @@
             int[] word, int probability, int timestamp);
     private static native boolean removeNgramEntryNative(long dict,
             int[][] prevWordCodePointArrays, boolean[] isBeginningOfSentenceArray, int[] word);
+    // TODO: Rename to updateEntriesForWordWithNgramContextNative.
+    private static native boolean updateCounterNative(long dict,
+            int[][] prevWordCodePointArrays, boolean[] isBeginningOfSentenceArray,
+            int[] word, boolean isValidWord, int count, int timestamp);
     private static native int addMultipleDictionaryEntriesNative(long dict,
             LanguageModelParam[] languageModelParams, int startIndex);
     private static native String getPropertyNative(long dict, String query);
@@ -490,6 +496,24 @@
         return true;
     }
 
+    // Update entries for the word occurrence with the ngramContext.
+    public boolean updateEntriesForWordWithNgramContext(@Nonnull final NgramContext ngramContext,
+            final String word, final boolean isValidWord, final int count, final int timestamp) {
+        if (TextUtils.isEmpty(word)) {
+            return false;
+        }
+        final int[][] prevWordCodePointArrays = new int[ngramContext.getPrevWordCount()][];
+        final boolean[] isBeginningOfSentenceArray = new boolean[ngramContext.getPrevWordCount()];
+        ngramContext.outputToArray(prevWordCodePointArrays, isBeginningOfSentenceArray);
+        final int[] wordCodePoints = StringUtils.toCodePointArray(word);
+        if (!updateCounterNative(mNativeDict, prevWordCodePointArrays,
+                isBeginningOfSentenceArray, wordCodePoints, isValidWord, count, timestamp)) {
+            return false;
+        }
+        mHasUpdated = true;
+        return true;
+    }
+
     @UsedForTesting
     public void addMultipleDictionaryEntries(final LanguageModelParam[] languageModelParams) {
         if (!isValidDictionary()) return;
diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
index 6dc1e82..4a218d5 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
@@ -99,6 +99,30 @@
                     DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS.length);
 
     /**
+     * Returns whether this facilitator is exactly for this list of locales.
+     * @param locales the list of locales to test against
+     * @return true if this facilitator handles exactly this list of locales, false otherwise
+     */
+    public boolean isForLocales(final Locale[] locales) {
+        if (locales.length != mDictionaryGroups.length) {
+            return false;
+        }
+        for (final Locale locale : locales) {
+            boolean found = false;
+            for (final DictionaryGroup group : mDictionaryGroups) {
+                if (locale.equals(group.mLocale)) {
+                    found = true;
+                    break;
+                }
+            }
+            if (!found) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
      * A group of dictionaries that work together for a single language.
      */
     private static class DictionaryGroup {
@@ -199,6 +223,18 @@
         return mDictionaryGroups[0].mLocale;
     }
 
+    /**
+     * Returns the primary locale among all currently active locales. BE CAREFUL using this.
+     *
+     * DO NOT USE THIS just because it's convenient. Use it when it's correct, for example when
+     * choosing what dictionary to put a word in, or when changing the capitalization of a typed
+     * string.
+     * @return the primary active locale
+     */
+    public Locale getPrimaryLocale() {
+        return mDictionaryGroups[0].mLocale;
+    }
+
     private static ExpandableBinaryDictionary getSubDict(final String dictType,
             final Context context, final Locale locale, final File dictFile,
             final String dictNamePrefix) {
@@ -380,33 +416,38 @@
     }
 
     @UsedForTesting
-    public void resetDictionariesForTesting(final Context context, final Locale locale,
+    public void resetDictionariesForTesting(final Context context, final Locale[] locales,
             final ArrayList<String> dictionaryTypes, final HashMap<String, File> dictionaryFiles,
             final Map<String, Map<String, String>> additionalDictAttributes) {
         Dictionary mainDictionary = null;
         final Map<String, ExpandableBinaryDictionary> subDicts = new HashMap<>();
 
-        for (final String dictType : dictionaryTypes) {
-            if (dictType.equals(Dictionary.TYPE_MAIN)) {
-                mainDictionary = DictionaryFactory.createMainDictionaryFromManager(context, locale);
-            } else {
-                final File dictFile = dictionaryFiles.get(dictType);
-                final ExpandableBinaryDictionary dict = getSubDict(
-                        dictType, context, locale, dictFile, "" /* dictNamePrefix */);
-                if (additionalDictAttributes.containsKey(dictType)) {
-                    dict.clearAndFlushDictionaryWithAdditionalAttributes(
-                            additionalDictAttributes.get(dictType));
+        final DictionaryGroup[] dictionaryGroups = new DictionaryGroup[locales.length];
+        for (int i = 0; i < locales.length; ++i) {
+            final Locale locale = locales[i];
+            for (final String dictType : dictionaryTypes) {
+                if (dictType.equals(Dictionary.TYPE_MAIN)) {
+                    mainDictionary = DictionaryFactory.createMainDictionaryFromManager(context,
+                            locale);
+                } else {
+                    final File dictFile = dictionaryFiles.get(dictType);
+                    final ExpandableBinaryDictionary dict = getSubDict(
+                            dictType, context, locale, dictFile, "" /* dictNamePrefix */);
+                    if (additionalDictAttributes.containsKey(dictType)) {
+                        dict.clearAndFlushDictionaryWithAdditionalAttributes(
+                                additionalDictAttributes.get(dictType));
+                    }
+                    if (dict == null) {
+                        throw new RuntimeException("Unknown dictionary type: " + dictType);
+                    }
+                    dict.reloadDictionaryIfRequired();
+                    dict.waitAllTasksForTests();
+                    subDicts.put(dictType, dict);
                 }
-                if (dict == null) {
-                    throw new RuntimeException("Unknown dictionary type: " + dictType);
-                }
-                dict.reloadDictionaryIfRequired();
-                dict.waitAllTasksForTests();
-                subDicts.put(dictType, dict);
             }
+            dictionaryGroups[i] = new DictionaryGroup(locale, mainDictionary, subDicts);
         }
-        mDictionaryGroups = new DictionaryGroup[] {
-                new DictionaryGroup(locale, mainDictionary, subDicts) };
+        mDictionaryGroups = dictionaryGroups;
     }
 
     public void closeDictionaries() {
diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
index 1bdadc3..d24f80a 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
@@ -46,6 +46,8 @@
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
+import javax.annotation.Nonnull;
+
 /**
  * Abstract base class for an expandable dictionary that can be created and updated dynamically
  * during runtime. When updated it automatically generates a new binary dictionary to handle future
@@ -292,13 +294,9 @@
         }
     }
 
-    /**
-     * Adds unigram information of a word to the dictionary. May overwrite an existing entry.
-     */
-    public void addUnigramEntryWithCheckingDistracter(final String word, final int frequency,
-            final String shortcutTarget, final int shortcutFreq, final boolean isNotAWord,
-            final boolean isBlacklisted, final int timestamp,
-            final DistracterFilter distracterFilter) {
+    private void updateDictionaryWithWriteLockIfWordIsNotADistracter(
+            @Nonnull final Runnable updateTask,
+            @Nonnull final String word, @Nonnull final DistracterFilter distracterFilter) {
         reloadDictionaryIfRequired();
         asyncPreCheckAndExecuteTaskWithWriteLock(
                 new Callable<Boolean>() {
@@ -315,12 +313,27 @@
                             return;
                         }
                         runGCIfRequiredLocked(true /* mindsBlockByGC */);
-                        addUnigramLocked(word, frequency, shortcutTarget, shortcutFreq,
-                                isNotAWord, isBlacklisted, timestamp);
+                        updateTask.run();
                     }
                 });
     }
 
+    /**
+     * Adds unigram information of a word to the dictionary. May overwrite an existing entry.
+     */
+    public void addUnigramEntryWithCheckingDistracter(final String word, final int frequency,
+            final String shortcutTarget, final int shortcutFreq, final boolean isNotAWord,
+            final boolean isBlacklisted, final int timestamp,
+            @Nonnull final DistracterFilter distracterFilter) {
+        updateDictionaryWithWriteLockIfWordIsNotADistracter(new Runnable() {
+            @Override
+            public void run() {
+                addUnigramLocked(word, frequency, shortcutTarget, shortcutFreq,
+                        isNotAWord, isBlacklisted, timestamp);
+            }
+        }, word, distracterFilter);
+    }
+
     protected void addUnigramLocked(final String word, final int frequency,
             final String shortcutTarget, final int shortcutFreq, final boolean isNotAWord,
             final boolean isBlacklisted, final int timestamp) {
@@ -354,7 +367,7 @@
     /**
      * Adds n-gram information of a word to the dictionary. May overwrite an existing entry.
      */
-    public void addNgramEntry(final NgramContext ngramContext, final String word,
+    public void addNgramEntry(@Nonnull final NgramContext ngramContext, final String word,
             final int frequency, final int timestamp) {
         reloadDictionaryIfRequired();
         asyncExecuteTaskWithWriteLock(new Runnable() {
@@ -369,7 +382,7 @@
         });
     }
 
-    protected void addNgramEntryLocked(final NgramContext ngramContext, final String word,
+    protected void addNgramEntryLocked(@Nonnull final NgramContext ngramContext, final String word,
             final int frequency, final int timestamp) {
         if (!mBinaryDictionary.addNgramEntry(ngramContext, word, frequency, timestamp)) {
             if (DEBUG) {
@@ -383,7 +396,8 @@
      * Dynamically remove the n-gram entry in the dictionary.
      */
     @UsedForTesting
-    public void removeNgramDynamically(final NgramContext ngramContext, final String word) {
+    public void removeNgramDynamically(@Nonnull final NgramContext ngramContext,
+            final String word) {
         reloadDictionaryIfRequired();
         asyncExecuteTaskWithWriteLock(new Runnable() {
             @Override
@@ -402,6 +416,26 @@
         });
     }
 
+    /**
+     * Update dictionary for the word with the ngramContext if the word is not a distracter.
+     */
+    public void updateEntriesForWordWithCheckingDistracter(@Nonnull final NgramContext ngramContext,
+            final String word, final boolean isValidWord, final int count, final int timestamp,
+            @Nonnull final DistracterFilter distracterFilter) {
+        updateDictionaryWithWriteLockIfWordIsNotADistracter(new Runnable() {
+            @Override
+            public void run() {
+                if (!mBinaryDictionary.updateEntriesForWordWithNgramContext(ngramContext, word,
+                        isValidWord, count, timestamp)) {
+                    if (DEBUG) {
+                        Log.e(TAG, "Cannot update counter. word: " + word
+                                + " context: "+ ngramContext.toString());
+                    }
+                }
+            }
+        }, word, distracterFilter);
+    }
+
     public interface AddMultipleDictionaryEntriesCallback {
         public void onFinished();
     }
@@ -410,7 +444,7 @@
      * Dynamically add multiple entries to the dictionary.
      */
     public void addMultipleDictionaryEntriesDynamically(
-            final ArrayList<LanguageModelParam> languageModelParams,
+            @Nonnull final ArrayList<LanguageModelParam> languageModelParams,
             final AddMultipleDictionaryEntriesCallback callback) {
         reloadDictionaryIfRequired();
         asyncExecuteTaskWithWriteLock(new Runnable() {
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 5aae010..64e65ef 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -84,12 +84,12 @@
 import com.android.inputmethod.latin.settings.SettingsValues;
 import com.android.inputmethod.latin.suggestions.SuggestionStripView;
 import com.android.inputmethod.latin.suggestions.SuggestionStripViewAccessor;
+import com.android.inputmethod.latin.touchinputconsumer.GestureConsumer;
 import com.android.inputmethod.latin.utils.ApplicationUtils;
 import com.android.inputmethod.latin.utils.CapsModeUtils;
 import com.android.inputmethod.latin.utils.CoordinateUtils;
 import com.android.inputmethod.latin.utils.CursorAnchorInfoUtils;
 import com.android.inputmethod.latin.utils.DialogUtils;
-import com.android.inputmethod.latin.utils.DistracterFilterCheckingExactMatchesAndSuggestions;
 import com.android.inputmethod.latin.utils.ImportantNoticeUtils;
 import com.android.inputmethod.latin.utils.IntentUtils;
 import com.android.inputmethod.latin.utils.JniUtils;
@@ -102,6 +102,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Locale;
 import java.util.concurrent.TimeUnit;
@@ -118,12 +119,10 @@
     private static boolean DEBUG = false;
 
     private static final int EXTENDED_TOUCHABLE_REGION_HEIGHT = 100;
-
-    private static final int PENDING_IMS_CALLBACK_DURATION = 800;
-
-    private static final int DELAY_WAIT_FOR_DICTIONARY_LOAD = 2000; // 2s
-
     private static final int PERIOD_FOR_AUDIO_AND_HAPTIC_FEEDBACK_IN_KEY_REPEAT = 2;
+    private static final int PENDING_IMS_CALLBACK_DURATION_MILLIS = 800;
+    private static final long DELAY_WAIT_FOR_DICTIONARY_LOAD_MILLIS = TimeUnit.SECONDS.toMillis(2);
+    private static final long DELAY_DEALLOCATE_MEMORY_MILLIS = TimeUnit.SECONDS.toMillis(10);
 
     /**
      * The name of the scheme used by the Package Manager to warn of a new package installation,
@@ -177,6 +176,8 @@
 
     private final boolean mIsHardwareAcceleratedDrawingEnabled;
 
+    private GestureConsumer mGestureConsumer = GestureConsumer.NULL_GESTURE_CONSUMER;
+
     public final UIHandler mHandler = new UIHandler(this);
 
     public static final class UIHandler extends LeakGuardHandlerWrapper<LatinIME> {
@@ -189,8 +190,9 @@
         private static final int MSG_UPDATE_TAIL_BATCH_INPUT_COMPLETED = 6;
         private static final int MSG_RESET_CACHES = 7;
         private static final int MSG_WAIT_FOR_DICTIONARY_LOAD = 8;
+        private static final int MSG_DEALLOCATE_MEMORY = 9;
         // Update this when adding new messages
-        private static final int MSG_LAST = MSG_WAIT_FOR_DICTIONARY_LOAD;
+        private static final int MSG_LAST = MSG_DEALLOCATE_MEMORY;
 
         private static final int ARG1_NOT_GESTURE_INPUT = 0;
         private static final int ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 1;
@@ -247,19 +249,20 @@
             case MSG_RESUME_SUGGESTIONS:
                 latinIme.mInputLogic.restartSuggestionsOnWordTouchedByCursor(
                         latinIme.mSettings.getCurrent(),
-                        msg.arg1 == ARG1_TRUE /* shouldIncludeResumedWordInSuggestions */,
                         latinIme.mKeyboardSwitcher.getCurrentKeyboardScriptId());
                 break;
             case MSG_REOPEN_DICTIONARIES:
                 // We need to re-evaluate the currently composing word in case the script has
                 // changed.
                 postWaitForDictionaryLoad();
-                latinIme.resetSuggest();
+                latinIme.resetDictionaryFacilitatorIfNecessary();
                 break;
             case MSG_UPDATE_TAIL_BATCH_INPUT_COMPLETED:
+                final SuggestedWords suggestedWords = (SuggestedWords) msg.obj;
                 latinIme.mInputLogic.onUpdateTailBatchInputCompleted(
                         latinIme.mSettings.getCurrent(),
-                        (SuggestedWords) msg.obj, latinIme.mKeyboardSwitcher);
+                        suggestedWords, latinIme.mKeyboardSwitcher);
+                latinIme.onTailBatchInputResultShown(suggestedWords);
                 break;
             case MSG_RESET_CACHES:
                 final SettingsValues settingsValues = latinIme.mSettings.getCurrent();
@@ -276,6 +279,9 @@
             case MSG_WAIT_FOR_DICTIONARY_LOAD:
                 Log.i(TAG, "Timeout waiting for dictionary load");
                 break;
+            case MSG_DEALLOCATE_MEMORY:
+                latinIme.deallocateMemory();
+                break;
             }
         }
 
@@ -288,8 +294,7 @@
             sendMessage(obtainMessage(MSG_REOPEN_DICTIONARIES));
         }
 
-        public void postResumeSuggestions(final boolean shouldIncludeResumedWordInSuggestions,
-                final boolean shouldDelay) {
+        public void postResumeSuggestions(final boolean shouldDelay) {
             final LatinIME latinIme = getOwnerInstance();
             if (latinIme == null) {
                 return;
@@ -299,13 +304,10 @@
             }
             removeMessages(MSG_RESUME_SUGGESTIONS);
             if (shouldDelay) {
-                sendMessageDelayed(obtainMessage(MSG_RESUME_SUGGESTIONS,
-                        shouldIncludeResumedWordInSuggestions ? ARG1_TRUE : ARG1_FALSE,
-                        0 /* ignored */), mDelayInMillisecondsToUpdateSuggestions);
+                sendMessageDelayed(obtainMessage(MSG_RESUME_SUGGESTIONS),
+                        mDelayInMillisecondsToUpdateSuggestions);
             } else {
-                sendMessage(obtainMessage(MSG_RESUME_SUGGESTIONS,
-                        shouldIncludeResumedWordInSuggestions ? ARG1_TRUE : ARG1_FALSE,
-                        0 /* ignored */));
+                sendMessage(obtainMessage(MSG_RESUME_SUGGESTIONS));
             }
         }
 
@@ -317,7 +319,7 @@
 
         public void postWaitForDictionaryLoad() {
             sendMessageDelayed(obtainMessage(MSG_WAIT_FOR_DICTIONARY_LOAD),
-                    DELAY_WAIT_FOR_DICTIONARY_LOAD);
+                    DELAY_WAIT_FOR_DICTIONARY_LOAD_MILLIS);
         }
 
         public void cancelWaitForDictionaryLoad() {
@@ -346,6 +348,19 @@
                     mDelayInMillisecondsToUpdateShiftState);
         }
 
+        public void postDeallocateMemory() {
+            sendMessageDelayed(obtainMessage(MSG_DEALLOCATE_MEMORY),
+                    DELAY_DEALLOCATE_MEMORY_MILLIS);
+        }
+
+        public void cancelDeallocateMemory() {
+            removeMessages(MSG_DEALLOCATE_MEMORY);
+        }
+
+        public boolean hasPendingDeallocateMemory() {
+            return hasMessages(MSG_DEALLOCATE_MEMORY);
+        }
+
         @UsedForTesting
         public void removeAllMessages() {
             for (int i = 0; i <= MSG_LAST; ++i) {
@@ -443,7 +458,7 @@
                     mPendingSuccessiveImsCallback = false;
                     resetPendingImsCallback();
                     sendMessageDelayed(obtainMessage(MSG_PENDING_IMS_CALLBACK),
-                            PENDING_IMS_CALLBACK_DURATION);
+                            PENDING_IMS_CALLBACK_DURATION_MILLIS);
                 }
                 final LatinIME latinIme = getOwnerInstance();
                 if (latinIme != null) {
@@ -451,6 +466,7 @@
                     latinIme.onStartInputViewInternal(editorInfo, restarting);
                     mAppliedEditorInfo = editorInfo;
                 }
+                cancelDeallocateMemory();
             }
         }
 
@@ -464,6 +480,9 @@
                     latinIme.onFinishInputViewInternal(finishingInput);
                     mAppliedEditorInfo = null;
                 }
+                if (!hasPendingDeallocateMemory()) {
+                    postDeallocateMemory();
+                }
             }
         }
 
@@ -542,9 +561,10 @@
         mHandler.onCreate();
         DEBUG = DebugFlags.DEBUG_ENABLED;
 
-        // TODO: Resolve mutual dependencies of {@link #loadSettings()} and {@link #initSuggest()}.
+        // TODO: Resolve mutual dependencies of {@link #loadSettings()} and
+        // {@link #resetDictionaryFacilitatorIfNecessary()}.
         loadSettings();
-        resetSuggest();
+        resetDictionaryFacilitatorIfNecessary();
 
         // Register to receive ringer mode change and network state change.
         // Also receive installation and removal of a dictionary pack.
@@ -574,18 +594,19 @@
     // Has to be package-visible for unit tests
     @UsedForTesting
     void loadSettings() {
-        final Locale locale = mSubtypeSwitcher.getCurrentSubtypeLocale();
+        final Locale[] locales = mSubtypeSwitcher.getCurrentSubtypeLocales();
         final EditorInfo editorInfo = getCurrentInputEditorInfo();
         final InputAttributes inputAttributes = new InputAttributes(
                 editorInfo, isFullscreenMode(), getPackageName());
-        mSettings.loadSettings(this, locale, inputAttributes);
+        // TODO: pass the array instead
+        mSettings.loadSettings(this, locales[0], inputAttributes);
         final SettingsValues currentSettingsValues = mSettings.getCurrent();
         AudioAndHapticFeedbackManager.getInstance().onSettingsChanged(currentSettingsValues);
         // This method is called on startup and language switch, before the new layout has
         // been displayed. Opening dictionaries never affects responsivity as dictionaries are
         // asynchronously loaded.
         if (!mHandler.hasPendingReopenDictionaries()) {
-            resetSuggestForLocale(locale);
+            resetDictionaryFacilitatorForLocale(locales);
         }
         mDictionaryFacilitator.updateEnabledSubtypes(mRichImm.getMyEnabledInputMethodSubtypeList(
                 true /* allowsImplicitlySelectedSubtypes */));
@@ -622,37 +643,39 @@
         }
         if (mHandler.hasPendingWaitForDictionaryLoad()) {
             mHandler.cancelWaitForDictionaryLoad();
-            mHandler.postResumeSuggestions(true /* shouldIncludeResumedWordInSuggestions */,
-                    false /* shouldDelay */);
+            mHandler.postResumeSuggestions(false /* shouldDelay */);
         }
     }
 
-    private void resetSuggest() {
-        final Locale switcherSubtypeLocale = mSubtypeSwitcher.getCurrentSubtypeLocale();
-        final String switcherLocaleStr = switcherSubtypeLocale.toString();
-        final Locale subtypeLocale;
-        if (TextUtils.isEmpty(switcherLocaleStr)) {
+    private void resetDictionaryFacilitatorIfNecessary() {
+        final Locale[] subtypeSwitcherLocales = mSubtypeSwitcher.getCurrentSubtypeLocales();
+        if (mDictionaryFacilitator.isForLocales(subtypeSwitcherLocales)) {
+            return;
+        }
+        final Locale[] subtypeLocales;
+        if (0 == subtypeSwitcherLocales.length) {
             // This happens in very rare corner cases - for example, immediately after a switch
             // to LatinIME has been requested, about a frame later another switch happens. In this
             // case, we are about to go down but we still don't know it, however the system tells
-            // us there is no current subtype so the locale is the empty string. Take the best
-            // possible guess instead -- it's bound to have no consequences, and we have no way
-            // of knowing anyway.
+            // us there is no current subtype.
             Log.e(TAG, "System is reporting no current subtype.");
-            subtypeLocale = getResources().getConfiguration().locale;
+            subtypeLocales = new Locale[] { getResources().getConfiguration().locale };
         } else {
-            subtypeLocale = switcherSubtypeLocale;
+            subtypeLocales = subtypeSwitcherLocales;
         }
-        resetSuggestForLocale(subtypeLocale);
+        resetDictionaryFacilitatorForLocale(subtypeLocales);
     }
 
     /**
-     * Reset suggest by loading dictionaries for the locale and the current settings values.
+     * Reset the facilitator by loading dictionaries for the locales and the current settings values
      *
-     * @param locale the locale
+     * @param locales the locales
      */
-    private void resetSuggestForLocale(final Locale locale) {
+    // TODO: make sure the current settings always have the right locales, and read from them
+    private void resetDictionaryFacilitatorForLocale(final Locale[] locales) {
         final SettingsValues settingsValues = mSettings.getCurrent();
+        // TODO: pass the array instead
+        final Locale locale = locales[0];
         mDictionaryFacilitator.resetDictionaries(this /* context */, locale,
                 settingsValues.mUseContactsDict, settingsValues.mUsePersonalizedDicts,
                 false /* forceReloadMainDictionary */, this);
@@ -729,6 +752,7 @@
     public void setInputView(final View view) {
         super.setInputView(view);
         mInputView = view;
+        updateSoftInputWindowLayoutParameters();
         mSuggestionStripView = (SuggestionStripView)view.findViewById(R.id.suggestion_strip_view);
         if (hasSuggestionStripView()) {
             mSuggestionStripView.setListener(this, view);
@@ -750,12 +774,12 @@
         if (prevExtractEditText == nextExtractEditText) {
             return;
         }
-        if (ProductionFlags.ENABLE_CURSOR_ANCHOR_INFO_CALLBACK && prevExtractEditText != null) {
+        if (prevExtractEditText != null) {
             prevExtractEditText.getViewTreeObserver().removeOnPreDrawListener(
                     mExtractTextViewPreDrawListener);
         }
         mExtractEditText = nextExtractEditText;
-        if (ProductionFlags.ENABLE_CURSOR_ANCHOR_INFO_CALLBACK && mExtractEditText != null) {
+        if (mExtractEditText != null) {
             mExtractEditText.getViewTreeObserver().addOnPreDrawListener(
                     mExtractTextViewPreDrawListener);
         }
@@ -771,8 +795,7 @@
             };
 
     private void onExtractTextViewPreDraw() {
-        if (!ProductionFlags.ENABLE_CURSOR_ANCHOR_INFO_CALLBACK || !isFullscreenMode()
-                || mExtractEditText == null) {
+        if (!isFullscreenMode() || mExtractEditText == null) {
             return;
         }
         final CursorAnchorInfo info = CursorAnchorInfoUtils.getCursorAnchorInfo(mExtractEditText);
@@ -801,6 +824,7 @@
         StatsUtils.onFinishInputView();
         mHandler.onFinishInputView(finishingInput);
         mStatsUtilsManager.onFinishInputView();
+        mGestureConsumer = GestureConsumer.NULL_GESTURE_CONSUMER;
     }
 
     @Override
@@ -826,6 +850,9 @@
     @SuppressWarnings("deprecation")
     private void onStartInputViewInternal(final EditorInfo editorInfo, final boolean restarting) {
         super.onStartInputView(editorInfo, restarting);
+        // Switch to the null consumer to handle cases leading to early exit below, for which we
+        // also wouldn't be consuming gesture data.
+        mGestureConsumer = GestureConsumer.NULL_GESTURE_CONSUMER;
         mRichImm.clearSubtypeCaches();
         final KeyboardSwitcher switcher = mKeyboardSwitcher;
         switcher.updateKeyboardTheme();
@@ -869,6 +896,12 @@
             return;
         }
 
+        // Update to a gesture consumer with the current editor and IME state.
+        mGestureConsumer = GestureConsumer.newInstance(editorInfo,
+                mInputLogic.getPrivateCommandPerformer(),
+                Arrays.asList(mSubtypeSwitcher.getCurrentSubtypeLocales()),
+                switcher.getKeyboard());
+
         // Forward this event to the accessibility utilities, if enabled.
         final AccessibilityUtils accessUtils = AccessibilityUtils.getInstance();
         if (accessUtils.isTouchExplorationEnabled()) {
@@ -907,12 +940,7 @@
             mInputLogic.startInput(mSubtypeSwitcher.getCombiningRulesExtraValueOfCurrentSubtype(),
                     currentSettingsValues);
 
-            // Note: the following does a round-trip IPC on the main thread: be careful
-            final Locale currentLocale = mSubtypeSwitcher.getCurrentSubtypeLocale();
-            if (null != currentLocale && !currentLocale.equals(suggest.getLocale())) {
-                // TODO: Do this automatically.
-                resetSuggest();
-            }
+            resetDictionaryFacilitatorIfNecessary();
 
             // TODO[IL]: Can the following be moved to InputLogic#startInput?
             if (!mInputLogic.mConnection.resetCachesUponCursorMoveAndReturnSuccess(
@@ -926,11 +954,12 @@
                 // mLastSelection{Start,End} are reset later in this method, no need to do it here
                 needToCallLoadKeyboardLater = true;
             } else {
-                // When rotating, initialSelStart and initialSelEnd sometimes are lying. Make a best
-                // effort to work around this bug.
+                // When rotating, and when input is starting again in a field from where the focus
+                // didn't move (the keyboard having been closed with the back key),
+                // initialSelStart and initialSelEnd sometimes are lying. Make a best effort to
+                // work around this bug.
                 mInputLogic.mConnection.tryFixLyingCursorPosition();
-                mHandler.postResumeSuggestions(true /* shouldIncludeResumedWordInSuggestions */,
-                        true /* shouldDelay */);
+                mHandler.postResumeSuggestions(true /* shouldDelay */);
                 needToCallLoadKeyboardLater = false;
             }
         } else {
@@ -1016,13 +1045,16 @@
     }
 
     private void cleanupInternalStateForFinishInput() {
-        mKeyboardSwitcher.deallocateMemory();
         // Remove pending messages related to update suggestions
         mHandler.cancelUpdateSuggestionStrip();
         // Should do the following in onFinishInputInternal but until JB MR2 it's not called :(
         mInputLogic.finishInput();
     }
 
+    protected void deallocateMemory() {
+        mKeyboardSwitcher.deallocateMemory();
+    }
+
     @Override
     public void onUpdateSelection(final int oldSelStart, final int oldSelEnd,
             final int newSelStart, final int newSelEnd,
@@ -1049,7 +1081,7 @@
     // We cannot mark this method as @Override until new SDK becomes publicly available.
     // @Override
     public void onUpdateCursorAnchorInfo(final CursorAnchorInfo info) {
-        if (!ProductionFlags.ENABLE_CURSOR_ANCHOR_INFO_CALLBACK || isFullscreenMode()) {
+        if (isFullscreenMode()) {
             return;
         }
         mInputLogic.onUpdateCursorAnchorInfo(CursorAnchorInfoCompatWrapper.fromObject(info));
@@ -1137,6 +1169,10 @@
     @Override
     public void onComputeInsets(final InputMethodService.Insets outInsets) {
         super.onComputeInsets(outInsets);
+        // This method may be called before {@link #setInputView(View)}.
+        if (mInputView == null) {
+            return;
+        }
         final SettingsValues settingsValues = mSettings.getCurrent();
         final View visibleKeyboardView = mKeyboardSwitcher.getVisibleKeyboardView();
         if (visibleKeyboardView == null || !hasSuggestionStripView()) {
@@ -1171,12 +1207,15 @@
         outInsets.visibleTopInsets = visibleTopY;
     }
 
-    public void startShowingInputView() {
+    public void startShowingInputView(final boolean needsToLoadKeyboard) {
         mIsExecutingStartShowingInputView = true;
         // This {@link #showWindow(boolean)} will eventually call back
         // {@link #onEvaluateInputViewShown()}.
         showWindow(true /* showInput */);
         mIsExecutingStartShowingInputView = false;
+        if (needsToLoadKeyboard) {
+            loadKeyboard();
+        }
     }
 
     public void stopShowingInputView() {
@@ -1184,6 +1223,14 @@
     }
 
     @Override
+    public boolean onShowInputRequested(final int flags, final boolean configChange) {
+        if (Settings.getInstance().getCurrent().mHasHardwareKeyboard) {
+            return true;
+        }
+        return super.onShowInputRequested(flags, configChange);
+    }
+
+    @Override
     public boolean onEvaluateInputViewShown() {
         if (mIsExecutingStartShowingInputView) {
             return true;
@@ -1213,8 +1260,14 @@
 
     @Override
     public void updateFullscreenMode() {
+        super.updateFullscreenMode();
+        mInputLogic.onUpdateFullscreenMode(isFullscreenMode());
+        updateSoftInputWindowLayoutParameters();
+    }
+
+    private void updateSoftInputWindowLayoutParameters() {
         // Override layout parameters to expand {@link SoftInputWindow} to the entire screen.
-        // See {@link InputMethodService#setinputView(View) and
+        // See {@link InputMethodService#setinputView(View)} and
         // {@link SoftInputWindow#updateWidthHeight(WindowManager.LayoutParams)}.
         final Window window = getWindow().getWindow();
         ViewLayoutUtils.updateLayoutHeightOf(window, LayoutParams.MATCH_PARENT);
@@ -1233,8 +1286,6 @@
             ViewLayoutUtils.updateLayoutGravityOf(inputArea, Gravity.BOTTOM);
             ViewLayoutUtils.updateLayoutHeightOf(mInputView, layoutHeight);
         }
-        super.updateFullscreenMode();
-        mInputLogic.onUpdateFullscreenMode(isFullscreenMode());
     }
 
     private int getCurrentAutoCapsState() {
@@ -1403,6 +1454,9 @@
     @Override
     public void onStartBatchInput() {
         mInputLogic.onStartBatchInput(mSettings.getCurrent(), mKeyboardSwitcher, mHandler);
+        mGestureConsumer.onGestureStarted(
+                Arrays.asList(mSubtypeSwitcher.getCurrentSubtypeLocales()),
+                mKeyboardSwitcher.getKeyboard());
     }
 
     @Override
@@ -1413,11 +1467,25 @@
     @Override
     public void onEndBatchInput(final InputPointers batchPointers) {
         mInputLogic.onEndBatchInput(batchPointers);
+        mGestureConsumer.onGestureCompleted(batchPointers);
     }
 
     @Override
     public void onCancelBatchInput() {
         mInputLogic.onCancelBatchInput(mHandler);
+        mGestureConsumer.onGestureCanceled();
+    }
+
+    /**
+     * To be called after the InputLogic has gotten a chance to act on the on-device decoding
+     * for the full gesture, possibly updating the TextView to reflect the first decoding.
+     * <p>
+     * This method must be run on the UI Thread.
+     * @param suggestedWords On-device decoding for the full gesture.
+     */
+    public void onTailBatchInputResultShown(final SuggestedWords suggestedWords) {
+        mGestureConsumer.onImeSuggestionsProcessed(suggestedWords,
+                mInputLogic.getComposingStart(), mInputLogic.getComposingLength());
     }
 
     // This method must run on the UI Thread.
@@ -1511,7 +1579,7 @@
                 // We should clear the contextual strip if there is no suggestion from dictionaries.
                 || noSuggestionsFromDictionaries) {
             mSuggestionStripView.setSuggestions(suggestedWords,
-                    SubtypeLocaleUtils.isRtlLanguage(mSubtypeSwitcher.getCurrentSubtype()));
+                    mSubtypeSwitcher.getCurrentSubtype().isRtlSubtype());
         }
     }
 
@@ -1559,7 +1627,7 @@
         }
         final String wordToShow;
         if (CapsModeUtils.isAutoCapsMode(mInputLogic.mLastComposedWord.mCapitalizedMode)) {
-            wordToShow = word.toLowerCase(mSubtypeSwitcher.getCurrentSubtypeLocale());
+            wordToShow = word.toLowerCase(mDictionaryFacilitator.getPrimaryLocale());
         } else {
             wordToShow = word;
         }
@@ -1845,7 +1913,7 @@
 
     public void dumpDictionaryForDebug(final String dictName) {
         if (mDictionaryFacilitator.getLocale() == null) {
-            resetSuggest();
+            resetDictionaryFacilitatorIfNecessary();
         }
         mDictionaryFacilitator.dumpDictionaryForDebug(dictName);
     }
diff --git a/java/src/com/android/inputmethod/latin/NgramContext.java b/java/src/com/android/inputmethod/latin/NgramContext.java
index c35c6e2..a02531c 100644
--- a/java/src/com/android/inputmethod/latin/NgramContext.java
+++ b/java/src/com/android/inputmethod/latin/NgramContext.java
@@ -158,19 +158,20 @@
         }
     }
 
-    public NgramContext getTrimmedNgramContext(final int maxPrevWordCount) {
-        final int newSize = Math.min(maxPrevWordCount, mPrevWordsCount);
-        return new NgramContext(this /* prevWordsInfo */, newSize);
-    }
-
     public int getPrevWordCount() {
         return mPrevWordsCount;
     }
 
     @Override
     public int hashCode() {
-        // Just for having equals().
-        return mPrevWordsInfo[0].hashCode();
+        int hashValue = 0;
+        for (final WordInfo wordInfo : mPrevWordsInfo) {
+            if (wordInfo == null || !WordInfo.EMPTY_WORD_INFO.equals(wordInfo)) {
+                break;
+            }
+            hashValue ^= wordInfo.hashCode();
+        }
+        return hashValue;
     }
 
     @Override
diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java
index 7507061..62a258b 100644
--- a/java/src/com/android/inputmethod/latin/RichInputConnection.java
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -19,6 +19,7 @@
 import android.graphics.Color;
 import android.inputmethodservice.InputMethodService;
 import android.os.Build;
+import android.os.Bundle;
 import android.text.SpannableStringBuilder;
 import android.text.Spanned;
 import android.text.TextUtils;
@@ -33,6 +34,7 @@
 import android.view.inputmethod.InputMethodManager;
 
 import com.android.inputmethod.compat.InputConnectionCompatUtils;
+import com.android.inputmethod.latin.inputlogic.PrivateCommandPerformer;
 import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
 import com.android.inputmethod.latin.utils.CapsModeUtils;
 import com.android.inputmethod.latin.utils.DebugLogUtils;
@@ -52,7 +54,7 @@
  * all the time to find out what text is in the buffer, when we need it to determine caps mode
  * for example.
  */
-public final class RichInputConnection {
+public final class RichInputConnection implements PrivateCommandPerformer {
     private static final String TAG = RichInputConnection.class.getSimpleName();
     private static final boolean DBG = false;
     private static final boolean DEBUG_PREVIOUS_TEXT = false;
@@ -849,8 +851,9 @@
 
     /**
      * Try to get the text from the editor to expose lies the framework may have been
-     * telling us. Concretely, when the device rotates, the frameworks tells us about where the
-     * cursor used to be initially in the editor at the time it first received the focus; this
+     * telling us. Concretely, when the device rotates and when the keyboard reopens in the same
+     * text field after having been closed with the back key, the frameworks tells us about where
+     * the cursor used to be initially in the editor at the time it first received the focus; this
      * may be completely different from the place it is upon rotation. Since we don't have any
      * means to get the real value, try at least to ask the text view for some characters and
      * detect the most damaging cases: when the cursor position is declared to be much smaller
@@ -859,7 +862,20 @@
     public void tryFixLyingCursorPosition() {
         final CharSequence textBeforeCursor = getTextBeforeCursor(
                 Constants.EDITOR_CONTENTS_CACHE_SIZE, 0);
-        if (null == textBeforeCursor) {
+        final CharSequence selectedText = mIC.getSelectedText(0 /* flags */);
+        if (null == textBeforeCursor ||
+                (!TextUtils.isEmpty(selectedText) && mExpectedSelEnd == mExpectedSelStart)) {
+            // If textBeforeCursor is null, we have no idea what kind of text field we have or if
+            // thinking about the "cursor position" actually makes any sense. In this case we
+            // remember a meaningless cursor position. Contrast this with an empty string, which is
+            // valid and should mean the cursor is at the start of the text.
+            // Also, if we expect we don't have a selection but we DO have non-empty selected text,
+            // then the framework lied to us about the cursor position. In this case, we should just
+            // revert to the most basic behavior possible for the next action (backspace in
+            // particular comes to mind), so we remember a meaningless cursor position which should
+            // result in degraded behavior from the next input.
+            // Interestingly, in either case, chances are any action the user takes next will result
+            // in a call to onUpdateSelection, which should set things right.
             mExpectedSelStart = mExpectedSelEnd = Constants.NOT_A_CURSOR_POSITION;
         } else {
             final int textLength = textBeforeCursor.length();
@@ -882,6 +898,15 @@
         }
     }
 
+    @Override
+    public boolean performPrivateCommand(final String action, final Bundle data) {
+        mIC = mParent.getCurrentInputConnection();
+        if (mIC == null) {
+            return false;
+        }
+        return mIC.performPrivateCommand(action, data);
+    }
+
     public int getExpectedSelectionStart() {
         return mExpectedSelStart;
     }
diff --git a/java/src/com/android/inputmethod/latin/RichInputMethodSubtype.java b/java/src/com/android/inputmethod/latin/RichInputMethodSubtype.java
index 0b08c48..8d05553 100644
--- a/java/src/com/android/inputmethod/latin/RichInputMethodSubtype.java
+++ b/java/src/com/android/inputmethod/latin/RichInputMethodSubtype.java
@@ -114,10 +114,13 @@
         return "Multi-lingual subtype: " + mSubtype.toString() + ", " + Arrays.toString(mLocales);
     }
 
-    // TODO: remove this method! We can always have several locales. Multi-lingual input will only
-    // be done when this method is gone.
-    public String getLocale() {
-        return mSubtype.getLocale();
+    public Locale[] getLocales() {
+        return mLocales;
+    }
+
+    public boolean isRtlSubtype() {
+        // The subtype is considered RTL if the language of the main subtype is RTL.
+        return SubtypeLocaleUtils.isRtlLanguage(mLocales[0]);
     }
 
     // TODO: remove this method
diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
index 45d67ff..c339e96 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -170,15 +170,21 @@
             Log.w(TAG, "onSubtypeChanged: " + newSubtype.getNameForLogging());
         }
 
-        final Locale newLocale = SubtypeLocaleUtils.getSubtypeLocale(newSubtype);
-        final Locale systemLocale = mResources.getConfiguration().locale;
-        final boolean sameLocale = systemLocale.equals(newLocale);
-        final boolean sameLanguage = systemLocale.getLanguage().equals(newLocale.getLanguage());
-        final boolean implicitlyEnabled = mRichImm
-                .checkIfSubtypeBelongsToThisImeAndImplicitlyEnabled(newSubtype.getRawSubtype());
-        mLanguageOnSpacebarHelper.updateIsSystemLanguageSameAsInputLanguage(
-                sameLocale || (sameLanguage && implicitlyEnabled));
-
+        final Locale[] newLocales = newSubtype.getLocales();
+        if (newLocales.length > 1) {
+            // In multi-locales mode, the system language is never the same as the input language
+            // because there is no single input language.
+            mLanguageOnSpacebarHelper.updateIsSystemLanguageSameAsInputLanguage(false);
+        } else {
+            final Locale newLocale = newLocales[0];
+            final Locale systemLocale = mResources.getConfiguration().locale;
+            final boolean sameLocale = systemLocale.equals(newLocale);
+            final boolean sameLanguage = systemLocale.getLanguage().equals(newLocale.getLanguage());
+            final boolean implicitlyEnabled = mRichImm
+                    .checkIfSubtypeBelongsToThisImeAndImplicitlyEnabled(newSubtype.getRawSubtype());
+            mLanguageOnSpacebarHelper.updateIsSystemLanguageSameAsInputLanguage(
+                    sameLocale || (sameLanguage && implicitlyEnabled));
+        }
         updateShortcutIME();
     }
 
@@ -284,11 +290,11 @@
         sForcedSubtypeForTesting = new RichInputMethodSubtype(subtype);
     }
 
-    public Locale getCurrentSubtypeLocale() {
+    public Locale[] getCurrentSubtypeLocales() {
         if (null != sForcedSubtypeForTesting) {
-            return LocaleUtils.constructLocaleFromString(sForcedSubtypeForTesting.getLocale());
+            return sForcedSubtypeForTesting.getLocales();
         }
-        return SubtypeLocaleUtils.getSubtypeLocale(getCurrentSubtype());
+        return getCurrentSubtype().getLocales();
     }
 
     public RichInputMethodSubtype getCurrentSubtype() {
diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java
index e6fd43a..4665764 100644
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java
@@ -113,6 +113,19 @@
     }
 
     /**
+     * Get suggested word to show as suggestions to UI.
+     *
+     * @param shouldShowLxxSuggestionUi true if showing suggestion UI introduced in LXX and later.
+     * @return the count of suggested word to show as suggestions to UI.
+     */
+    public int getWordCountToShow(final boolean shouldShowLxxSuggestionUi) {
+        if (isPrediction() || !shouldShowLxxSuggestionUi) {
+            return size();
+        }
+        return size() - /* typed word */ 1;
+    }
+
+    /**
      * Get suggested word at <code>index</code>.
      * @param index The index of the suggested word.
      * @return The suggested word.
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index f85b34b..157bd15 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -128,8 +128,7 @@
      * Number of keystrokes in the composing word.
      * @return the number of keystrokes
      */
-    // This may be made public if need be, but right now it's not used anywhere
-    /* package for tests */ int size() {
+    public int size() {
         return mCodePointSize;
     }
 
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index 27af161..07bfd0d 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -53,7 +53,6 @@
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
 import com.android.inputmethod.latin.WordComposer;
 import com.android.inputmethod.latin.define.DebugFlags;
-import com.android.inputmethod.latin.define.ProductionFlags;
 import com.android.inputmethod.latin.settings.SettingsValues;
 import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion;
 import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
@@ -169,14 +168,11 @@
             mInputLogicHandler.reset();
         }
 
-        if (ProductionFlags.ENABLE_CURSOR_ANCHOR_INFO_CALLBACK) {
-            // AcceptTypedWord feature relies on CursorAnchorInfo.
-            if (settingsValues.mShouldShowUiToAcceptTypedWord) {
-                mConnection.requestCursorUpdates(true /* enableMonitor */,
-                        true /* requestImmediateCallback */);
-            }
-            mTextDecorator.reset();
+        if (settingsValues.mShouldShowLxxSuggestionUi) {
+            mConnection.requestCursorUpdates(true /* enableMonitor */,
+                    true /* requestImmediateCallback */);
         }
+        mTextDecorator.reset();
     }
 
     /**
@@ -435,8 +431,7 @@
         // removed.
         mConnection.removeBackgroundColorFromHighlightedTextIfNecessary();
         // We moved the cursor. If we are touching a word, we need to resume suggestion.
-        mLatinIME.mHandler.postResumeSuggestions(false /* shouldIncludeResumedWordInSuggestions */,
-                true /* shouldDelay */);
+        mLatinIME.mHandler.postResumeSuggestions(true /* shouldDelay */);
         // Stop the last recapitalization, if started.
         mRecapitalizeStatus.stop();
         return true;
@@ -1127,19 +1122,21 @@
                 StatsUtils.onBackspaceSelectedText(numCharsDeleted);
             } else {
                 // There is no selection, just delete one character.
-                if (Constants.NOT_A_CURSOR_POSITION == mConnection.getExpectedSelectionEnd()) {
-                    // This should never happen.
-                    Log.e(TAG, "Backspace when we don't know the selection position");
-                }
-                if (inputTransaction.mSettingsValues.isBeforeJellyBean() ||
-                        inputTransaction.mSettingsValues.mInputAttributes.isTypeNull()) {
-                    // There are two possible reasons to send a key event: either the field has
+                if (inputTransaction.mSettingsValues.isBeforeJellyBean()
+                        || inputTransaction.mSettingsValues.mInputAttributes.isTypeNull()
+                        || Constants.NOT_A_CURSOR_POSITION
+                                == mConnection.getExpectedSelectionEnd()) {
+                    // There are three possible reasons to send a key event: either the field has
                     // type TYPE_NULL, in which case the keyboard should send events, or we are
-                    // running in backward compatibility mode. Before Jelly bean, the keyboard
-                    // would simulate a hardware keyboard event on pressing enter or delete. This
-                    // is bad for many reasons (there are race conditions with commits) but some
-                    // applications are relying on this behavior so we continue to support it for
-                    // older apps, so we retain this behavior if the app has target SDK < JellyBean.
+                    // running in backward compatibility mode, or we don't know the cursor position.
+                    // Before Jelly bean, the keyboard would simulate a hardware keyboard event on
+                    // pressing enter or delete. This is bad for many reasons (there are race
+                    // conditions with commits) but some applications are relying on this behavior
+                    // so we continue to support it for older apps, so we retain this behavior if
+                    // the app has target SDK < JellyBean.
+                    // As for the case where we don't know the cursor position, it can happen
+                    // because of bugs in the framework. But the framework should know, so the next
+                    // best thing is to leave it to whatever it thinks is best.
                     sendDownUpKeyEvent(KeyEvent.KEYCODE_DEL);
                     int totalDeletedLength = 1;
                     if (mDeleteCount > Constants.DELETE_ACCELERATE_AT) {
@@ -1184,7 +1181,7 @@
                     && !mConnection.isCursorFollowedByWordCharacter(
                             inputTransaction.mSettingsValues.mSpacingAndPunctuations)) {
                 restartSuggestionsOnWordTouchedByCursor(inputTransaction.mSettingsValues,
-                        true /* shouldIncludeResumedWordInSuggestions */, currentKeyboardScriptId);
+                        currentKeyboardScriptId);
             }
         }
     }
@@ -1440,12 +1437,10 @@
      * do nothing.
      *
      * @param settingsValues the current values of the settings.
-     * @param shouldIncludeResumedWordInSuggestions whether to include the word on which we resume
      *   suggestions in the suggestion list.
      */
     // TODO: make this private.
     public void restartSuggestionsOnWordTouchedByCursor(final SettingsValues settingsValues,
-            final boolean shouldIncludeResumedWordInSuggestions,
             // TODO: remove this argument, put it into settingsValues
             final int currentKeyboardScriptId) {
         // HACK: We may want to special-case some apps that exhibit bad behavior in case of
@@ -1493,13 +1488,6 @@
         if (numberOfCharsInWordBeforeCursor > expectedCursorPosition) return;
         final ArrayList<SuggestedWordInfo> suggestions = new ArrayList<>();
         final String typedWord = range.mWord.toString();
-        if (shouldIncludeResumedWordInSuggestions) {
-            suggestions.add(new SuggestedWordInfo(typedWord,
-                    SuggestedWords.MAX_SUGGESTIONS + 1,
-                    SuggestedWordInfo.KIND_TYPED, Dictionary.DICTIONARY_USER_TYPED,
-                    SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
-                    SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */));
-        }
         if (!isResumableWord(settingsValues, typedWord)) {
             mSuggestionStripViewAccessor.setNeutralSuggestionStrip();
             return;
@@ -1532,7 +1520,7 @@
         mConnection.maybeMoveTheCursorAroundAndRestoreToWorkaroundABug();
         mConnection.setComposingRegion(expectedCursorPosition - numberOfCharsInWordBeforeCursor,
                 expectedCursorPosition + range.getNumberOfCharsInWordAfterCursor());
-        if (suggestions.size() <= (shouldIncludeResumedWordInSuggestions ? 1 : 0)) {
+        if (suggestions.size() <= 0) {
             // If there weren't any suggestion spans on this word, suggestions#size() will be 1
             // if shouldIncludeResumedWordInSuggestions is true, 0 otherwise. In this case, we
             // have no useful suggestions, so we will try to compute some for it instead.
@@ -1542,8 +1530,7 @@
                         public void onGetSuggestedWords(
                                 final SuggestedWords suggestedWordsIncludingTypedWord) {
                             final SuggestedWords suggestedWords;
-                            if (suggestedWordsIncludingTypedWord.size() > 1
-                                    && !shouldIncludeResumedWordInSuggestions) {
+                            if (suggestedWordsIncludingTypedWord.size() > 1) {
                                 // We were able to compute new suggestions for this word.
                                 // Remove the typed word, since we don't want to display it in this
                                 // case. The #getSuggestedWordsExcludingTypedWordForRecorrection()
@@ -1591,6 +1578,10 @@
         final String committedWordString = committedWord.toString();
         final int cancelLength = committedWord.length();
         final String separatorString = mLastComposedWord.mSeparatorString;
+        // If our separator is a space, we won't actually commit it,
+        // but set the space state to PHANTOM so that a space will be inserted
+        // on the next keypress
+        final boolean usePhantomSpace = separatorString.equals(Constants.STRING_SPACE);
         // We want java chars, not codepoints for the following.
         final int separatorLength = separatorString.length();
         // TODO: should we check our saved separator against the actual contents of the text view?
@@ -1611,7 +1602,8 @@
         if (!TextUtils.isEmpty(committedWord)) {
             mDictionaryFacilitator.removeWordFromPersonalizedDicts(committedWordString);
         }
-        final String stringToCommit = originallyTypedWord + separatorString;
+        final String stringToCommit = originallyTypedWord +
+                (usePhantomSpace ? "" : separatorString);
         final SpannableString textToCommit = new SpannableString(stringToCommit);
         if (committedWord instanceof SpannableString) {
             final SpannableString committedWordWithSuggestionSpans = (SpannableString)committedWord;
@@ -1644,8 +1636,10 @@
                 }
             }
             // Add the suggestion list to the list of suggestions.
-            textToCommit.setSpan(new SuggestionSpan(inputTransaction.mSettingsValues.mLocale,
-                    suggestions.toArray(new String[suggestions.size()]), 0 /* flags */),
+            textToCommit.setSpan(new SuggestionSpan(mLatinIME /* context */,
+                    inputTransaction.mSettingsValues.mLocale,
+                    suggestions.toArray(new String[suggestions.size()]), 0 /* flags */,
+                    null /* notificationTargetClass */),
                     0 /* start */, lastCharIndex /* end */, 0 /* flags */);
         }
 
@@ -1663,6 +1657,9 @@
             } else {
                 mConnection.commitText(textToCommit, 1);
             }
+            if (usePhantomSpace) {
+                mSpaceState = SpaceState.PHANTOM;
+            }
         } else {
             // For languages without spaces, we revert the typed string but the cursor is flush
             // with the typed word, so we need to resume suggestions right away.
@@ -2186,10 +2183,7 @@
         }
         mConnection.tryFixLyingCursorPosition();
         if (tryResumeSuggestions) {
-            // This is triggered when starting input anew, so we want to include the resumed
-            // word in suggestions.
-            handler.postResumeSuggestions(true /* shouldIncludeResumedWordInSuggestions */,
-                    true /* shouldDelay */);
+            handler.postResumeSuggestions(true /* shouldDelay */);
         }
         return true;
     }
@@ -2265,6 +2259,47 @@
         mConnection.setComposingText(composingTextToBeSet, newCursorPosition);
     }
 
+    /**
+     * Gets an object allowing private IME commands to be sent to the
+     * underlying editor.
+     * @return An object for sending private commands to the underlying editor.
+     */
+    public PrivateCommandPerformer getPrivateCommandPerformer() {
+        return mConnection;
+    }
+
+    /**
+     * Gets the expected index of the first char of the composing span within the editor's text.
+     * Returns a negative value in case there appears to be no valid composing span.
+     *
+     * @see #getComposingLength()
+     * @see RichInputConnection#hasSelection()
+     * @see RichInputConnection#isCursorPositionKnown()
+     * @see RichInputConnection#getExpectedSelectionStart()
+     * @see RichInputConnection#getExpectedSelectionEnd()
+     * @return The expected index in Java chars of the first char of the composing span.
+     */
+    // TODO: try and see if we can get rid of this method. Ideally the users of this class should
+    // never need to know this.
+    public int getComposingStart() {
+        if (!mConnection.isCursorPositionKnown() || mConnection.hasSelection()) {
+            return -1;
+        }
+        return mConnection.getExpectedSelectionStart() - mWordComposer.size();
+    }
+
+    /**
+     * Gets the expected length in Java chars of the composing span.
+     * May be 0 if there is no valid composing span.
+     * @see #getComposingStart()
+     * @return The expected length of the composing span.
+     */
+    // TODO: try and see if we can get rid of this method. Ideally the users of this class should
+    // never need to know this.
+    public int getComposingLength() {
+        return mWordComposer.size();
+    }
+
     //////////////////////////////////////////////////////////////////////////////////////////////
     // Following methods are tentatively placed in this class for the integration with
     // TextDecorator.
@@ -2316,7 +2351,7 @@
             // We cannot help in this case because we are heavily relying on this new API.
             return false;
         }
-        if (!settingsValues.mShouldShowUiToAcceptTypedWord) {
+        if (!settingsValues.mShouldShowLxxSuggestionUi) {
             return false;
         }
         if (TextUtils.isEmpty(lastComposedWord.mTypedWord)) {
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/PrivateCommandPerformer.java b/java/src/com/android/inputmethod/latin/inputlogic/PrivateCommandPerformer.java
new file mode 100644
index 0000000..42eaa9c
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/inputlogic/PrivateCommandPerformer.java
@@ -0,0 +1,40 @@
+/*
+ * 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.inputlogic;
+
+import android.os.Bundle;
+
+/**
+ * Provides an interface matching
+ * {@link android.view.inputmethod.InputConnection#performPrivateCommand(String,Bundle)}.
+ */
+public interface PrivateCommandPerformer {
+    /**
+     * API to send private commands from an input method to its connected
+     * editor. This can be used to provide domain-specific features that are
+     * only known between certain input methods and their clients.
+     *
+     * @param action Name of the command to be performed. This must be a scoped
+     *            name, i.e. prefixed with a package name you own, so that
+     *            different developers will not create conflicting commands.
+     * @param data Any data to include with the command.
+     * @return true if the command was sent (regardless of whether the
+     * associated editor understood it), false if the input connection is no
+     * longer valid.
+     */
+    boolean performPrivateCommand(String action, Bundle data);
+}
diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
index 2661d5d..34edfa0 100644
--- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
+++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
@@ -163,13 +163,15 @@
     static final int NOT_A_VERSION_NUMBER = -1;
 
     // These MUST have the same values as the relevant constants in format_utils.h.
-    // From version 4 on, we use version * 100 + revision as a version number. That allows
+    // From version 2.01 on, we use version * 100 + revision as a version number. That allows
     // us to change the format during development while having testing devices remove
     // older files with each upgrade, while still having a readable versioning scheme.
     // When we bump up the dictionary format version, we should update
     // ExpandableDictionary.needsToMigrateDictionary() and
     // ExpandableDictionary.matchesExpectedBinaryDictFormatVersionForThisType().
     public static final int VERSION2 = 2;
+    public static final int VERSION201 = 201;
+    public static final int MINIMUM_SUPPORTED_VERSION_OF_CODE_POINT_TABLE = VERSION201;
     // Dictionary version used for testing.
     public static final int VERSION4_ONLY_FOR_TESTING = 399;
     public static final int VERSION401 = 401;
diff --git a/java/src/com/android/inputmethod/latin/makedict/NgramProperty.java b/java/src/com/android/inputmethod/latin/makedict/NgramProperty.java
new file mode 100644
index 0000000..99e0e27
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/makedict/NgramProperty.java
@@ -0,0 +1,26 @@
+package com.android.inputmethod.latin.makedict;
+
+import com.android.inputmethod.latin.NgramContext;
+
+public class NgramProperty {
+    public final WeightedString mTargetWord;
+    public final NgramContext mNgramContext;
+
+    public NgramProperty(final WeightedString targetWord, final NgramContext ngramContext) {
+        mTargetWord = targetWord;
+        mNgramContext = ngramContext;
+    }
+
+    @Override
+    public int hashCode() {
+        return mTargetWord.hashCode() ^ mNgramContext.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) return true;
+        if (!(o instanceof NgramProperty)) return false;
+        final NgramProperty n = (NgramProperty)o;
+        return mTargetWord.equals(n.mTargetWord) && mNgramContext.equals(n.mNgramContext);
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/makedict/WordProperty.java b/java/src/com/android/inputmethod/latin/makedict/WordProperty.java
index cd78e22..a180d06 100644
--- a/java/src/com/android/inputmethod/latin/makedict/WordProperty.java
+++ b/java/src/com/android/inputmethod/latin/makedict/WordProperty.java
@@ -18,6 +18,8 @@
 
 import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.latin.BinaryDictionary;
+import com.android.inputmethod.latin.NgramContext;
+import com.android.inputmethod.latin.NgramContext.WordInfo;
 import com.android.inputmethod.latin.utils.CombinedFormatUtils;
 import com.android.inputmethod.latin.utils.StringUtils;
 
@@ -33,16 +35,17 @@
     public final String mWord;
     public final ProbabilityInfo mProbabilityInfo;
     public final ArrayList<WeightedString> mShortcutTargets;
-    public final ArrayList<WeightedString> mBigrams;
+    public final ArrayList<NgramProperty> mNgrams;
     // TODO: Support mIsBeginningOfSentence.
     public final boolean mIsBeginningOfSentence;
     public final boolean mIsNotAWord;
     public final boolean mIsBlacklistEntry;
     public final boolean mHasShortcuts;
-    public final boolean mHasBigrams;
+    public final boolean mHasNgrams;
 
     private int mHashCode = 0;
 
+    // TODO: Support n-gram.
     @UsedForTesting
     public WordProperty(final String word, final ProbabilityInfo probabilityInfo,
             final ArrayList<WeightedString> shortcutTargets,
@@ -51,11 +54,21 @@
         mWord = word;
         mProbabilityInfo = probabilityInfo;
         mShortcutTargets = shortcutTargets;
-        mBigrams = bigrams;
+        if (null == bigrams) {
+            mNgrams = null;
+        } else {
+            mNgrams = new ArrayList<>();
+            final NgramContext ngramContext = new NgramContext(new WordInfo(mWord));
+            if (bigrams != null) {
+                for (final WeightedString bigramTarget : bigrams) {
+                    mNgrams.add(new NgramProperty(bigramTarget, ngramContext));
+                }
+            }
+        }
         mIsBeginningOfSentence = false;
         mIsNotAWord = isNotAWord;
         mIsBlacklistEntry = isBlacklistEntry;
-        mHasBigrams = bigrams != null && !bigrams.isEmpty();
+        mHasNgrams = bigrams != null && !bigrams.isEmpty();
         mHasShortcuts = shortcutTargets != null && !shortcutTargets.isEmpty();
     }
 
@@ -78,20 +91,26 @@
         mWord = StringUtils.getStringFromNullTerminatedCodePointArray(codePoints);
         mProbabilityInfo = createProbabilityInfoFromArray(probabilityInfo);
         mShortcutTargets = new ArrayList<>();
-        mBigrams = new ArrayList<>();
+        final ArrayList<NgramProperty> ngrams = new ArrayList<>();
         mIsBeginningOfSentence = isBeginningOfSentence;
         mIsNotAWord = isNotAWord;
         mIsBlacklistEntry = isBlacklisted;
         mHasShortcuts = hasShortcuts;
-        mHasBigrams = hasBigram;
+        mHasNgrams = hasBigram;
 
-        final int bigramTargetCount = bigramTargets.size();
-        for (int i = 0; i < bigramTargetCount; i++) {
-            final String bigramTargetString =
+        final int relatedNgramCount = bigramTargets.size();
+        final WordInfo currentWordInfo =
+                mIsBeginningOfSentence ? WordInfo.BEGINNING_OF_SENTENCE : new WordInfo(mWord);
+        final NgramContext ngramContext = new NgramContext(currentWordInfo);
+        for (int i = 0; i < relatedNgramCount; i++) {
+            final String ngramTargetString =
                     StringUtils.getStringFromNullTerminatedCodePointArray(bigramTargets.get(i));
-            mBigrams.add(new WeightedString(bigramTargetString,
-                    createProbabilityInfoFromArray(bigramProbabilityInfo.get(i))));
+            final WeightedString ngramTarget = new WeightedString(ngramTargetString,
+                    createProbabilityInfoFromArray(bigramProbabilityInfo.get(i)));
+            // TODO: Support n-gram.
+            ngrams.add(new NgramProperty(ngramTarget, ngramContext));
         }
+        mNgrams = ngrams.isEmpty() ? null : ngrams;
 
         final int shortcutTargetCount = shortcutTargets.size();
         for (int i = 0; i < shortcutTargetCount; i++) {
@@ -102,6 +121,20 @@
         }
     }
 
+    // TODO: Remove
+    public ArrayList<WeightedString> getBigrams() {
+        if (null == mNgrams) {
+            return null;
+        }
+        final ArrayList<WeightedString> bigrams = new ArrayList<>();
+        for (final NgramProperty ngram : mNgrams) {
+            if (ngram.mNgramContext.getPrevWordCount() == 1) {
+                bigrams.add(ngram.mTargetWord);
+            }
+        }
+        return bigrams;
+    }
+
     public int getProbability() {
         return mProbabilityInfo.mProbability;
     }
@@ -110,8 +143,8 @@
         return Arrays.hashCode(new Object[] {
                 word.mWord,
                 word.mProbabilityInfo,
-                word.mShortcutTargets.hashCode(),
-                word.mBigrams.hashCode(),
+                word.mShortcutTargets,
+                word.mNgrams,
                 word.mIsNotAWord,
                 word.mIsBlacklistEntry
         });
@@ -142,9 +175,16 @@
         if (!(o instanceof WordProperty)) return false;
         WordProperty w = (WordProperty)o;
         return mProbabilityInfo.equals(w.mProbabilityInfo) && mWord.equals(w.mWord)
-                && mShortcutTargets.equals(w.mShortcutTargets) && mBigrams.equals(w.mBigrams)
+                && mShortcutTargets.equals(w.mShortcutTargets) && equals(mNgrams, w.mNgrams)
                 && mIsNotAWord == w.mIsNotAWord && mIsBlacklistEntry == w.mIsBlacklistEntry
-                && mHasBigrams == w.mHasBigrams && mHasShortcuts && w.mHasBigrams;
+                && mHasNgrams == w.mHasNgrams && mHasShortcuts && w.mHasNgrams;
+    }
+
+    private <T> boolean equals(final ArrayList<T> a, final ArrayList<T> b) {
+        if (null == a) {
+            return null == b;
+        }
+        return a.equals(b);
     }
 
     @Override
diff --git a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java
index d616846..5976154 100644
--- a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java
@@ -65,34 +65,7 @@
         if (word.length() > Constants.DICTIONARY_MAX_WORD_LENGTH) {
             return;
         }
-        final int frequency = isValid ?
-                FREQUENCY_FOR_WORDS_IN_DICTS : FREQUENCY_FOR_WORDS_NOT_IN_DICTS;
-        userHistoryDictionary.addUnigramEntryWithCheckingDistracter(word, frequency,
-                null /* shortcutTarget */, 0 /* shortcutFreq */, false /* isNotAWord */,
-                false /* isBlacklisted */, timestamp, distracterFilter);
-
-        final boolean isBeginningOfSentenceContext = ngramContext.isBeginningOfSentenceContext();
-        final NgramContext ngramContextToBeSaved =
-                ngramContext.getTrimmedNgramContext(SUPPORTED_NGRAM - 1);
-        for (int i = 0; i < ngramContextToBeSaved.getPrevWordCount(); i++) {
-            final CharSequence prevWord = ngramContextToBeSaved.getNthPrevWord(1 /* n */);
-            if (prevWord == null || (prevWord.length() > Constants.DICTIONARY_MAX_WORD_LENGTH)) {
-                return;
-            }
-            // Do not insert a word as a bigram of itself
-            if (i == 0 && TextUtils.equals(word, prevWord)) {
-                return;
-            }
-            if (isBeginningOfSentenceContext) {
-                // Beginning-of-Sentence n-gram entry is added as an n-gram entry of an OOV word.
-                userHistoryDictionary.addNgramEntry(
-                        ngramContextToBeSaved.getTrimmedNgramContext(i + 1), word,
-                        FREQUENCY_FOR_WORDS_NOT_IN_DICTS, timestamp);
-            } else {
-                userHistoryDictionary.addNgramEntry(
-                        ngramContextToBeSaved.getTrimmedNgramContext(i + 1), word, frequency,
-                        timestamp);
-            }
-        }
+        userHistoryDictionary.updateEntriesForWordWithCheckingDistracter(ngramContext, word,
+                isValid, 1 /* count */, timestamp, distracterFilter);
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java
index 2174f52..c633fc1 100644
--- a/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java
@@ -33,7 +33,6 @@
 import android.support.v4.view.ViewCompat;
 import android.text.TextUtils;
 import android.util.Log;
-import android.util.Pair;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuInflater;
@@ -80,25 +79,26 @@
             "is_subtype_enabler_notification_dialog_open";
     private static final String KEY_SUBTYPE_FOR_SUBTYPE_ENABLER = "subtype_for_subtype_enabler";
 
-    static final class SubtypeLocaleItem extends Pair<String, String>
-            implements Comparable<SubtypeLocaleItem> {
-        public SubtypeLocaleItem(final String localeString, final String displayName) {
-            super(localeString, displayName);
+    static final class SubtypeLocaleItem implements Comparable<SubtypeLocaleItem> {
+        public final String mLocaleString;
+        private final String mDisplayName;
+
+        public SubtypeLocaleItem(final InputMethodSubtype subtype) {
+            mLocaleString = subtype.getLocale();
+            mDisplayName = SubtypeLocaleUtils.getSubtypeLocaleDisplayNameInSystemLocale(
+                    mLocaleString);
         }
 
-        public SubtypeLocaleItem(final String localeString) {
-            this(localeString,
-                    SubtypeLocaleUtils.getSubtypeLocaleDisplayNameInSystemLocale(localeString));
-        }
-
+        // {@link ArrayAdapter<T>} that hosts the instance of this class needs {@link #toString()}
+        // to get display name.
         @Override
         public String toString() {
-            return second;
+            return mDisplayName;
         }
 
         @Override
         public int compareTo(final SubtypeLocaleItem o) {
-            return first.compareTo(o.first);
+            return mLocaleString.compareTo(o.mLocaleString);
         }
     }
 
@@ -121,32 +121,28 @@
                             SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype)));
                 }
                 if (InputMethodSubtypeCompatUtils.isAsciiCapable(subtype)) {
-                    items.add(createItem(context, subtype.getLocale()));
+                    items.add(new SubtypeLocaleItem(subtype));
                 }
             }
             // TODO: Should filter out already existing combinations of locale and layout.
             addAll(items);
         }
-
-        public static SubtypeLocaleItem createItem(final Context context,
-                final String localeString) {
-            if (localeString.equals(SubtypeLocaleUtils.NO_LANGUAGE)) {
-                final String displayName = context.getString(R.string.subtype_no_language);
-                return new SubtypeLocaleItem(localeString, displayName);
-            }
-            return new SubtypeLocaleItem(localeString);
-        }
     }
 
-    static final class KeyboardLayoutSetItem extends Pair<String, String> {
+    static final class KeyboardLayoutSetItem {
+        public final String mLayoutName;
+        private final String mDisplayName;
+
         public KeyboardLayoutSetItem(final InputMethodSubtype subtype) {
-            super(SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype),
-                    SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(subtype));
+            mLayoutName = SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype);
+            mDisplayName = SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(subtype);
         }
 
+        // {@link ArrayAdapter<T>} that hosts the instance of this class needs {@link #toString()}
+        // to get display name.
         @Override
         public String toString() {
-            return second;
+            return mDisplayName;
         }
     }
 
@@ -255,7 +251,6 @@
 
         @Override
         protected void onPrepareDialogBuilder(final AlertDialog.Builder builder) {
-            final Context context = builder.getContext();
             builder.setCancelable(true).setOnCancelListener(this);
             if (isIncomplete()) {
                 builder.setPositiveButton(R.string.add, this)
@@ -264,8 +259,7 @@
                 builder.setPositiveButton(R.string.save, this)
                         .setNeutralButton(android.R.string.cancel, this)
                         .setNegativeButton(R.string.remove, this);
-                final SubtypeLocaleItem localeItem = SubtypeLocaleAdapter.createItem(
-                        context, mSubtype.getLocale());
+                final SubtypeLocaleItem localeItem = new SubtypeLocaleItem(mSubtype);
                 final KeyboardLayoutSetItem layoutItem = new KeyboardLayoutSetItem(mSubtype);
                 setSpinnerPosition(mSubtypeLocaleSpinner, localeItem);
                 setSpinnerPosition(mKeyboardLayoutSetSpinner, layoutItem);
@@ -303,7 +297,7 @@
                         (KeyboardLayoutSetItem) mKeyboardLayoutSetSpinner.getSelectedItem();
                 final InputMethodSubtype subtype =
                         AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
-                                locale.first, layout.first);
+                                locale.mLocaleString, layout.mLayoutName);
                 setSubtype(subtype);
                 notifyChanged();
                 if (isEditing) {
@@ -469,8 +463,6 @@
                 KEY_IS_SUBTYPE_ENABLER_NOTIFICATION_DIALOG_OPEN)) {
             mSubtypePreferenceKeyForSubtypeEnabler = savedInstanceState.getString(
                     KEY_SUBTYPE_FOR_SUBTYPE_ENABLER);
-            final SubtypePreference subtypePref = (SubtypePreference)findPreference(
-                    mSubtypePreferenceKeyForSubtypeEnabler);
             mSubtypeEnablerNotificationDialog = createDialog();
             mSubtypeEnablerNotificationDialog.show();
         }
diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java
index 48f4c75..091ca43 100644
--- a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java
+++ b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java
@@ -21,8 +21,8 @@
     public static final String PREF_FORCE_NON_DISTINCT_MULTITOUCH = "force_non_distinct_multitouch";
     public static final String PREF_FORCE_PHYSICAL_KEYBOARD_SPECIAL_KEY =
             "force_physical_keyboard_special_key";
-    public static final String PREF_SHOW_UI_TO_ACCEPT_TYPED_WORD =
-            "pref_show_ui_to_accept_typed_word";
+    public static final String PREF_SHOULD_SHOW_LXX_SUGGESTION_UI =
+            "pref_should_show_lxx_suggestion_ui";
     public static final String PREF_HAS_CUSTOM_KEY_PREVIEW_ANIMATION_PARAMS =
             "pref_has_custom_key_preview_animation_params";
     public static final String PREF_KEY_PREVIEW_SHOW_UP_START_X_SCALE =
diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java
index 5640e20..e9f8d45 100644
--- a/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java
@@ -56,8 +56,8 @@
         super.onCreate(icicle);
         addPreferencesFromResource(R.xml.prefs_screen_debug);
 
-        if (!Settings.HAS_UI_TO_ACCEPT_TYPED_WORD) {
-            removePreference(DebugSettings.PREF_SHOW_UI_TO_ACCEPT_TYPED_WORD);
+        if (!Settings.SHOULD_SHOW_LXX_SUGGESTION_UI) {
+            removePreference(DebugSettings.PREF_SHOULD_SHOW_LXX_SUGGESTION_UI);
         }
 
         mReadExternalDictionaryPref = findPreference(PREF_READ_EXTERNAL_DICTIONARY);
diff --git a/java/src/com/android/inputmethod/latin/settings/Settings.java b/java/src/com/android/inputmethod/latin/settings/Settings.java
index a171fc3..84596b4 100644
--- a/java/src/com/android/inputmethod/latin/settings/Settings.java
+++ b/java/src/com/android/inputmethod/latin/settings/Settings.java
@@ -32,6 +32,7 @@
 import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils;
 import com.android.inputmethod.latin.utils.ResourceUtils;
 import com.android.inputmethod.latin.utils.RunInLocale;
+import com.android.inputmethod.latin.utils.StatsUtils;
 import com.android.inputmethod.latin.utils.StringUtils;
 
 import java.util.Collections;
@@ -76,7 +77,7 @@
             BuildCompatUtils.EFFECTIVE_SDK_INT <= Build.VERSION_CODES.KITKAT;
     public static final boolean ENABLE_SHOW_LANGUAGE_SWITCH_KEY_SETTINGS =
             BuildCompatUtils.EFFECTIVE_SDK_INT <= Build.VERSION_CODES.KITKAT;
-    public static final boolean HAS_UI_TO_ACCEPT_TYPED_WORD =
+    public static final boolean SHOULD_SHOW_LXX_SUGGESTION_UI =
             BuildCompatUtils.EFFECTIVE_SDK_INT >= BuildCompatUtils.VERSION_CODES_LXX;
     public static final String PREF_SHOW_LANGUAGE_SWITCH_KEY =
             "pref_show_language_switch_key";
@@ -169,6 +170,7 @@
                 return;
             }
             loadSettings(mContext, mSettingsValues.mLocale, mSettingsValues.mInputAttributes);
+            StatsUtils.onLoadSettings(mSettingsValues);
         } finally {
             mSettingsValuesLock.unlock();
         }
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
index 99f761c..ce8a0ab 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
@@ -80,7 +80,7 @@
     public final boolean mPhraseGestureEnabled;
     public final int mKeyLongpressTimeout;
     public final boolean mEnableMetricsLogging;
-    public final boolean mShouldShowUiToAcceptTypedWord;
+    public final boolean mShouldShowLxxSuggestionUi;
     // Use split layout for keyboard.
     public final boolean mIsSplitKeyboardEnabled;
     public final int mScreenMetrics;
@@ -163,8 +163,8 @@
         mIsSplitKeyboardEnabled = prefs.getBoolean(Settings.PREF_ENABLE_SPLIT_KEYBOARD, false);
         mScreenMetrics = res.getInteger(R.integer.config_screen_metrics);
 
-        mShouldShowUiToAcceptTypedWord = Settings.HAS_UI_TO_ACCEPT_TYPED_WORD
-                && prefs.getBoolean(DebugSettings.PREF_SHOW_UI_TO_ACCEPT_TYPED_WORD, true);
+        mShouldShowLxxSuggestionUi = Settings.SHOULD_SHOW_LXX_SUGGESTION_UI
+                && prefs.getBoolean(DebugSettings.PREF_SHOULD_SHOW_LXX_SUGGESTION_UI, true);
         // Compute other readable settings
         mKeyLongpressTimeout = Settings.readKeyLongpressTimeout(prefs, res);
         mKeypressVibrationDuration = Settings.readKeypressVibrationDuration(prefs, res);
diff --git a/java/src/com/android/inputmethod/latin/setup/SetupActivity.java b/java/src/com/android/inputmethod/latin/setup/SetupActivity.java
index b770ea5..7607429 100644
--- a/java/src/com/android/inputmethod/latin/setup/SetupActivity.java
+++ b/java/src/com/android/inputmethod/latin/setup/SetupActivity.java
@@ -17,12 +17,8 @@
 package com.android.inputmethod.latin.setup;
 
 import android.app.Activity;
-import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
-import android.provider.Settings;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodManager;
 
 public final class SetupActivity extends Activity {
     @Override
diff --git a/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java b/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java
index e455e53..54562f3 100644
--- a/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java
+++ b/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java
@@ -46,6 +46,8 @@
 public final class SetupWizardActivity extends Activity implements View.OnClickListener {
     static final String TAG = SetupWizardActivity.class.getSimpleName();
 
+    // For debugging purpose.
+    private static final boolean FORCE_TO_SHOW_WELCOME_SCREEN = false;
     private static final boolean ENABLE_WELCOME_VIDEO = true;
 
     private InputMethodManager mImm;
@@ -304,6 +306,9 @@
 
     private int determineSetupStepNumber() {
         mHandler.cancelPollingImeSettings();
+        if (FORCE_TO_SHOW_WELCOME_SCREEN) {
+            return STEP_1;
+        }
         if (!UncachedInputMethodManagerUtils.isThisImeEnabled(this, mImm)) {
             return STEP_1;
         }
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SentenceLevelAdapter.java b/java/src/com/android/inputmethod/latin/spellcheck/SentenceLevelAdapter.java
index 51c4b1e..9ddee86 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/SentenceLevelAdapter.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/SentenceLevelAdapter.java
@@ -145,9 +145,8 @@
         int wordEnd = wordIterator.getEndOfWord(originalText, wordStart);
         while (wordStart <= end && wordEnd != -1 && wordStart != -1) {
             if (wordEnd >= start && wordEnd > wordStart) {
-                CharSequence subSequence = originalText.subSequence(wordStart, wordEnd).toString();
-                final TextInfo ti = TextInfoCompatUtils.newInstance(subSequence, 0,
-                        subSequence.length(), cookie, subSequence.hashCode());
+                final TextInfo ti = TextInfoCompatUtils.newInstance(originalText, wordStart,
+                        wordEnd, cookie, originalText.subSequence(wordStart, wordEnd).hashCode());
                 wordItems.add(new SentenceWordItem(ti, wordStart, wordEnd));
             }
             wordStart = wordIterator.getBeginningOfNextWord(originalText, wordEnd);
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
index 1e8df89..d559399 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
@@ -60,6 +60,9 @@
 
 import java.util.ArrayList;
 
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
 final class SuggestionStripLayoutHelper {
     private static final int DEFAULT_SUGGESTIONS_COUNT_IN_STRIP = 3;
     private static final float DEFAULT_CENTER_SUGGESTION_PERCENTILE = 0.40f;
@@ -213,15 +216,14 @@
             return word;
         }
 
-        final int len = word.length();
         final Spannable spannedWord = new SpannableString(word);
         final int options = mSuggestionStripOptions;
         if ((isAutoCorrection && (options & AUTO_CORRECT_BOLD) != 0)
                 || (isTypedWordValid && (options & VALID_TYPED_WORD_BOLD) != 0)) {
-            spannedWord.setSpan(BOLD_SPAN, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+            addStyleSpan(spannedWord, BOLD_SPAN);
         }
         if (isAutoCorrection && (options & AUTO_CORRECT_UNDERLINE) != 0) {
-            spannedWord.setSpan(UNDERLINE_SPAN, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+            addStyleSpan(spannedWord, UNDERLINE_SPAN);
         }
         return spannedWord;
     }
@@ -238,9 +240,9 @@
         final SettingsValues settingsValues = Settings.getInstance().getCurrent();
         final boolean shouldOmitTypedWord = shouldOmitTypedWord(suggestedWords.mInputStyle,
                 settingsValues.mGestureFloatingPreviewTextEnabled,
-                settingsValues.mShouldShowUiToAcceptTypedWord);
+                settingsValues.mShouldShowLxxSuggestionUi);
         return getPositionInSuggestionStrip(indexInSuggestedWords, suggestedWords.mWillAutoCorrect,
-                settingsValues.mShouldShowUiToAcceptTypedWord && shouldOmitTypedWord,
+                settingsValues.mShouldShowLxxSuggestionUi && shouldOmitTypedWord,
                 mCenterPositionInStrip, mTypedWordPositionWhenAutocorrect);
     }
 
@@ -365,17 +367,19 @@
                     (PunctuationSuggestions)suggestedWords, stripView);
         }
 
+        final int wordCountToShow = suggestedWords.getWordCountToShow(
+                Settings.getInstance().getCurrent().mShouldShowLxxSuggestionUi);
         final int startIndexOfMoreSuggestions = setupWordViewsAndReturnStartIndexOfMoreSuggestions(
                 suggestedWords, mSuggestionsCountInStrip);
         final TextView centerWordView = mWordViews.get(mCenterPositionInStrip);
         final int stripWidth = stripView.getWidth();
         final int centerWidth = getSuggestionWidth(mCenterPositionInStrip, stripWidth);
-        if (suggestedWords.size() == 1 || getTextScaleX(centerWordView.getText(), centerWidth,
+        if (wordCountToShow == 1 || getTextScaleX(centerWordView.getText(), centerWidth,
                 centerWordView.getPaint()) < MIN_TEXT_XSCALE) {
             // Layout only the most relevant suggested word at the center of the suggestion strip
             // by consolidating all slots in the strip.
             final int countInStrip = 1;
-            mMoreSuggestionsAvailable = (suggestedWords.size() > countInStrip);
+            mMoreSuggestionsAvailable = (wordCountToShow > countInStrip);
             layoutWord(mCenterPositionInStrip, stripWidth - mPadding);
             stripView.addView(centerWordView);
             setLayoutWeight(centerWordView, 1.0f, ViewGroup.LayoutParams.MATCH_PARENT);
@@ -387,7 +391,7 @@
         }
 
         final int countInStrip = mSuggestionsCountInStrip;
-        mMoreSuggestionsAvailable = (suggestedWords.size() > countInStrip);
+        mMoreSuggestionsAvailable = (wordCountToShow > countInStrip);
         int x = 0;
         for (int positionInStrip = 0; positionInStrip < countInStrip; positionInStrip++) {
             if (positionInStrip != 0) {
@@ -442,10 +446,11 @@
         // {@link StyleSpan} in a content description may cause an issue of TTS/TalkBack.
         // Use a simple {@link String} to avoid the issue.
         wordView.setContentDescription(TextUtils.isEmpty(word) ? null : word.toString());
-        final CharSequence text = getEllipsizedText(word, width, wordView.getPaint());
-        final float scaleX = getTextScaleX(word, width, wordView.getPaint());
+        final CharSequence text = getEllipsizedTextWithSettingScaleX(
+                word, width, wordView.getPaint());
+        final float scaleX = wordView.getTextScaleX();
         wordView.setText(text); // TextView.setText() resets text scale x to 1.0.
-        wordView.setTextScaleX(Math.max(scaleX, MIN_TEXT_XSCALE));
+        wordView.setTextScaleX(scaleX);
         // A <code>wordView</code> should be disabled when <code>word</code> is empty in order to
         // make it unclickable.
         // With accessibility touch exploration on, <code>wordView</code> should be enabled even
@@ -550,7 +555,7 @@
 
     public void layoutAddToDictionaryHint(final String word, final ViewGroup addToDictionaryStrip) {
         final boolean shouldShowUiToAcceptTypedWord = Settings.getInstance().getCurrent()
-                .mShouldShowUiToAcceptTypedWord;
+                .mShouldShowLxxSuggestionUi;
         final int stripWidth = addToDictionaryStrip.getWidth();
         final int width = shouldShowUiToAcceptTypedWord ? stripWidth
                 : stripWidth - mDividerWidth - mPadding * 2;
@@ -558,7 +563,8 @@
         final TextView wordView = (TextView)addToDictionaryStrip.findViewById(R.id.word_to_save);
         wordView.setTextColor(mColorTypedWord);
         final int wordWidth = (int)(width * mCenterSuggestionWeight);
-        final CharSequence wordToSave = getEllipsizedText(word, wordWidth, wordView.getPaint());
+        final CharSequence wordToSave = getEllipsizedTextWithSettingScaleX(
+                word, wordWidth, wordView.getPaint());
         final float wordScaleX = wordView.getTextScaleX();
         wordView.setText(wordToSave);
         wordView.setTextScaleX(wordScaleX);
@@ -592,7 +598,7 @@
         }
         hintView.setTextColor(mColorAutoCorrect);
         final float hintScaleX = getTextScaleX(hintText, hintWidth, hintView.getPaint());
-        hintView.setText(hintText);
+        hintView.setText(hintText); // TextView.setText() resets text scale x to 1.0.
         hintView.setTextScaleX(hintScaleX);
         setLayoutWeight(hintView, hintWeight, ViewGroup.LayoutParams.MATCH_PARENT);
     }
@@ -604,8 +610,7 @@
         final int width = titleView.getWidth() - titleView.getPaddingLeft()
                 - titleView.getPaddingRight();
         titleView.setTextColor(mColorAutoCorrect);
-        titleView.setText(importantNoticeTitle);
-        titleView.setTextScaleX(1.0f); // Reset textScaleX.
+        titleView.setText(importantNoticeTitle); // TextView.setText() resets text scale x to 1.0.
         final float titleScaleX = getTextScaleX(importantNoticeTitle, width, titleView.getPaint());
         titleView.setTextScaleX(titleScaleX);
     }
@@ -620,18 +625,19 @@
         }
     }
 
-    private static float getTextScaleX(final CharSequence text, final int maxWidth,
+    private static float getTextScaleX(@Nullable final CharSequence text, final int maxWidth,
             final TextPaint paint) {
         paint.setTextScaleX(1.0f);
         final int width = getTextWidth(text, paint);
         if (width <= maxWidth || maxWidth <= 0) {
             return 1.0f;
         }
-        return maxWidth / (float)width;
+        return maxWidth / (float) width;
     }
 
-    private static CharSequence getEllipsizedText(final CharSequence text, final int maxWidth,
-            final TextPaint paint) {
+    @Nullable
+    private static CharSequence getEllipsizedTextWithSettingScaleX(
+            @Nullable final CharSequence text, final int maxWidth, @Nonnull final TextPaint paint) {
         if (text == null) {
             return null;
         }
@@ -641,62 +647,63 @@
             return text;
         }
 
-        // Note that TextUtils.ellipsize() use text-x-scale as 1.0 if ellipsize is needed. To
-        // get squeezed and ellipsized text, passes enlarged width (maxWidth / MIN_TEXT_XSCALE).
-        final float upscaledWidth = maxWidth / MIN_TEXT_XSCALE;
-        CharSequence ellipsized = TextUtils.ellipsize(
-                text, paint, upscaledWidth, TextUtils.TruncateAt.MIDDLE);
-        // For an unknown reason, ellipsized seems to return a text that does indeed fit inside the
-        // passed width according to paint.measureText, but not according to paint.getTextWidths.
-        // But when rendered, the text seems to actually take up as many pixels as returned by
-        // paint.getTextWidths, hence problem.
-        // To save this case, we compare the measured size of the new text, and if it's too much,
-        // try it again removing the difference. This may still give a text too long by one or
-        // two pixels so we take an additional 2 pixels cushion and call it a day.
-        // TODO: figure out why getTextWidths and measureText don't agree with each other, and
-        // remove the following code.
-        final float ellipsizedTextWidth = getTextWidth(ellipsized, paint);
-        if (upscaledWidth <= ellipsizedTextWidth) {
-            ellipsized = TextUtils.ellipsize(
-                    text, paint, upscaledWidth - (ellipsizedTextWidth - upscaledWidth) - 2,
-                    TextUtils.TruncateAt.MIDDLE);
-        }
+        // <code>text</code> must be ellipsized with minimum text scale x.
         paint.setTextScaleX(MIN_TEXT_XSCALE);
-        return ellipsized;
+        final boolean hasBoldStyle = hasStyleSpan(text, BOLD_SPAN);
+        final boolean hasUnderlineStyle = hasStyleSpan(text, UNDERLINE_SPAN);
+        // TextUtils.ellipsize erases any span object existed after ellipsized point.
+        // We have to restore these spans afterward.
+        final CharSequence ellipsizedText = TextUtils.ellipsize(
+                text, paint, maxWidth, TextUtils.TruncateAt.MIDDLE);
+        if (!hasBoldStyle && !hasUnderlineStyle) {
+            return ellipsizedText;
+        }
+        final Spannable spannableText = (ellipsizedText instanceof Spannable)
+                ? (Spannable)ellipsizedText : new SpannableString(ellipsizedText);
+        if (hasBoldStyle) {
+            addStyleSpan(spannableText, BOLD_SPAN);
+        }
+        if (hasUnderlineStyle) {
+            addStyleSpan(spannableText, UNDERLINE_SPAN);
+        }
+        return spannableText;
     }
 
-    private static int getTextWidth(final CharSequence text, final TextPaint paint) {
+    private static boolean hasStyleSpan(@Nullable final CharSequence text,
+            final CharacterStyle style) {
+        if (text instanceof Spanned) {
+            return ((Spanned)text).getSpanStart(style) >= 0;
+        }
+        return false;
+    }
+
+    private static void addStyleSpan(@Nonnull final Spannable text, final CharacterStyle style) {
+        text.removeSpan(style);
+        text.setSpan(style, 0, text.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+    }
+
+    private static int getTextWidth(@Nullable final CharSequence text, final TextPaint paint) {
         if (TextUtils.isEmpty(text)) {
             return 0;
         }
+        final int length = text.length();
+        final float[] widths = new float[length];
+        final int count;
         final Typeface savedTypeface = paint.getTypeface();
-        paint.setTypeface(getTextTypeface(text));
-        final int len = text.length();
-        final float[] widths = new float[len];
-        final int count = paint.getTextWidths(text, 0, len, widths);
+        try {
+            paint.setTypeface(getTextTypeface(text));
+            count = paint.getTextWidths(text, 0, length, widths);
+        } finally {
+            paint.setTypeface(savedTypeface);
+        }
         int width = 0;
         for (int i = 0; i < count; i++) {
             width += Math.round(widths[i] + 0.5f);
         }
-        paint.setTypeface(savedTypeface);
         return width;
     }
 
-    private static Typeface getTextTypeface(final CharSequence text) {
-        if (!(text instanceof SpannableString)) {
-            return Typeface.DEFAULT;
-        }
-
-        final SpannableString ss = (SpannableString)text;
-        final StyleSpan[] styles = ss.getSpans(0, text.length(), StyleSpan.class);
-        if (styles.length == 0) {
-            return Typeface.DEFAULT;
-        }
-
-        if (styles[0].getStyle() == Typeface.BOLD) {
-            return Typeface.DEFAULT_BOLD;
-        }
-        // TODO: BOLD_ITALIC, ITALIC case?
-        return Typeface.DEFAULT;
+    private static Typeface getTextTypeface(@Nullable final CharSequence text) {
+        return hasStyleSpan(text, BOLD_SPAN) ? Typeface.DEFAULT_BOLD : Typeface.DEFAULT;
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
index b421a7e..e40fd88 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
@@ -400,6 +400,7 @@
         if (mStripVisibilityGroup.isShowingImportantNoticeStrip()) {
             return false;
         }
+        // Detecting sliding up finger to show {@link MoreSuggestionsView}.
         if (!mMoreSuggestionsView.isShowingInParent()) {
             mLastX = (int)me.getX();
             mLastY = (int)me.getY();
@@ -439,6 +440,11 @@
 
     @Override
     public boolean onTouchEvent(final MotionEvent me) {
+        if (!mMoreSuggestionsView.isShowingInParent()) {
+            // Ignore any touch event while more suggestions panel hasn't been shown.
+            // Detecting sliding up is done at {@link #onInterceptTouchEvent}.
+            return true;
+        }
         // In the sliding input mode. {@link MotionEvent} should be forwarded to
         // {@link MoreSuggestionsView}.
         final int index = me.getActionIndex();
diff --git a/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java b/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java
index 61292fc..fb36b7c 100644
--- a/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java
@@ -17,6 +17,7 @@
 package com.android.inputmethod.latin.utils;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Map;
 import java.util.TreeMap;
@@ -40,4 +41,13 @@
         }
         return list;
     }
+
+    /**
+     * Tests whether c contains no elements, true if c is null or c is empty.
+     * @param c Collection to test.
+     * @return Whether c contains no elements.
+     */
+    public static boolean isNullOrEmpty(final Collection c) {
+        return c == null || c.isEmpty();
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/utils/CombinedFormatUtils.java b/java/src/com/android/inputmethod/latin/utils/CombinedFormatUtils.java
index 34f59e8..7e8e559 100644
--- a/java/src/com/android/inputmethod/latin/utils/CombinedFormatUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/CombinedFormatUtils.java
@@ -67,7 +67,7 @@
             builder.append("," + BLACKLISTED_TAG + "=true");
         }
         builder.append("\n");
-        if (wordProperty.mShortcutTargets != null) {
+        if (wordProperty.mHasShortcuts) {
             for (final WeightedString shortcutTarget : wordProperty.mShortcutTargets) {
                 builder.append("  " + SHORTCUT_TAG + "=" + shortcutTarget.mWord);
                 builder.append(",");
@@ -75,8 +75,9 @@
                 builder.append("\n");
             }
         }
-        if (wordProperty.mBigrams != null) {
-            for (final WeightedString bigram : wordProperty.mBigrams) {
+        if (wordProperty.mHasNgrams) {
+            // TODO: Support ngram.
+            for (final WeightedString bigram : wordProperty.getBigrams()) {
                 builder.append("  " + BIGRAM_TAG + "=" + bigram.mWord);
                 builder.append(",");
                 builder.append(formatProbabilityInfo(bigram.mProbabilityInfo));
diff --git a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java
index 5a7f766..61661cd 100644
--- a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java
@@ -293,13 +293,6 @@
         return LocaleUtils.constructLocaleFromString(localeString);
     }
 
-    // TODO: remove this. When RichInputMethodSubtype#getLocale is removed we can do away with this
-    // method at the same time.
-    public static Locale getSubtypeLocale(final RichInputMethodSubtype subtype) {
-        final String localeString = subtype.getLocale();
-        return LocaleUtils.constructLocaleFromString(localeString);
-    }
-
     public static String getKeyboardLayoutSetDisplayName(final InputMethodSubtype subtype) {
         final String layoutName = getKeyboardLayoutSetName(subtype);
         return getKeyboardLayoutSetDisplayName(layoutName);
@@ -348,10 +341,6 @@
         return Arrays.binarySearch(SORTED_RTL_LANGUAGES, language) >= 0;
     }
 
-    public static boolean isRtlLanguage(final RichInputMethodSubtype subtype) {
-        return isRtlLanguage(getSubtypeLocale(subtype));
-    }
-
     public static String getCombiningRulesExtraValue(final InputMethodSubtype subtype) {
         return subtype.getExtraValueOf(Constants.Subtype.ExtraValue.COMBINING_RULES);
     }
diff --git a/native/jni/Android.mk b/native/jni/Android.mk
index 3a2073f..6003a6f 100644
--- a/native/jni/Android.mk
+++ b/native/jni/Android.mk
@@ -48,7 +48,7 @@
 
 ifeq ($(FLAG_DO_PROFILE), true)
     $(warning Making profiling version of native library)
-    LOCAL_CFLAGS += -DFLAG_DO_PROFILE -funwind-tables -fno-inline
+    LOCAL_CFLAGS += -DFLAG_DO_PROFILE -funwind-tables
 else # FLAG_DO_PROFILE
 ifeq ($(FLAG_DBG), true)
     $(warning Making debug version of native library)
diff --git a/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp
index f88d37e..80419b3 100644
--- a/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp
+++ b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp
@@ -25,13 +25,13 @@
 
 namespace latinime {
 
-static jlong latinime_Keyboard_setProximityInfo(JNIEnv *env, jclass clazz, jstring localeJStr,
+static jlong latinime_Keyboard_setProximityInfo(JNIEnv *env, jclass clazz,
         jint displayWidth, jint displayHeight, jint gridWidth, jint gridHeight,
         jint mostCommonkeyWidth, jint mostCommonkeyHeight, jintArray proximityChars, jint keyCount,
         jintArray keyXCoordinates, jintArray keyYCoordinates, jintArray keyWidths,
         jintArray keyHeights, jintArray keyCharCodes, jfloatArray sweetSpotCenterXs,
         jfloatArray sweetSpotCenterYs, jfloatArray sweetSpotRadii) {
-    ProximityInfo *proximityInfo = new ProximityInfo(env, localeJStr, displayWidth, displayHeight,
+    ProximityInfo *proximityInfo = new ProximityInfo(env, displayWidth, displayHeight,
             gridWidth, gridHeight, mostCommonkeyWidth, mostCommonkeyHeight, proximityChars,
             keyCount, keyXCoordinates, keyYCoordinates, keyWidths, keyHeights, keyCharCodes,
             sweetSpotCenterXs, sweetSpotCenterYs, sweetSpotRadii);
@@ -46,7 +46,7 @@
 static const JNINativeMethod sMethods[] = {
     {
         const_cast<char *>("setProximityInfoNative"),
-        const_cast<char *>("(Ljava/lang/String;IIIIII[II[I[I[I[I[I[F[F[F)J"),
+        const_cast<char *>("(IIIIII[II[I[I[I[I[I[F[F[F)J"),
         reinterpret_cast<void *>(latinime_Keyboard_setProximityInfo)
     },
     {
diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
index 19aeb28..76c7fdd 100644
--- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
+++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
@@ -252,6 +252,9 @@
     } else {
         dictionary->getPredictions(&prevWordsInfo, &suggestionResults);
     }
+    if (DEBUG_DICT) {
+        suggestionResults.dumpSuggestions();
+    }
     suggestionResults.outputSuggestions(env, outSuggestionCount, outCodePointsArray,
             outScoresArray, outSpaceIndicesArray, outTypesArray,
             outAutoCommitFirstWordConfidenceArray, inOutWeightOfLangModelVsSpatialModel);
@@ -373,7 +376,8 @@
     }
     // Use 1 for count to indicate the word has inputted.
     const UnigramProperty unigramProperty(isBeginningOfSentence, isNotAWord,
-            isBlacklisted, probability, timestamp, 0 /* level */, 1 /* count */, &shortcuts);
+            isBlacklisted, probability, HistoricalInfo(timestamp, 0 /* level */, 1 /* count */),
+            std::move(shortcuts));
     return dictionary->addUnigramEntry(CodePointArrayView(codePoints, codePointCount),
             &unigramProperty);
 }
@@ -405,7 +409,7 @@
     env->GetIntArrayRegion(word, 0, wordLength, wordCodePoints);
     // Use 1 for count to indicate the ngram has inputted.
     const NgramProperty ngramProperty(CodePointArrayView(wordCodePoints, wordLength).toVector(),
-            probability, timestamp, 0 /* level */, 1 /* count */);
+            probability, HistoricalInfo(timestamp, 0 /* level */, 1 /* count */));
     return dictionary->addNgramEntry(&prevWordsInfo, &ngramProperty);
 }
 
@@ -426,6 +430,25 @@
             CodePointArrayView(wordCodePoints, codePointCount));
 }
 
+static bool latinime_BinaryDictionary_updateCounter(JNIEnv *env, jclass clazz, jlong dict,
+        jobjectArray prevWordCodePointArrays, jbooleanArray isBeginningOfSentenceArray,
+        jintArray word, jboolean isValidWord, jint count, jint timestamp) {
+    Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
+    if (!dictionary) {
+        return false;
+    }
+    const PrevWordsInfo prevWordsInfo = JniDataUtils::constructPrevWordsInfo(env,
+            prevWordCodePointArrays, isBeginningOfSentenceArray,
+            env->GetArrayLength(prevWordCodePointArrays));
+    jsize codePointCount = env->GetArrayLength(word);
+    int wordCodePoints[codePointCount];
+    env->GetIntArrayRegion(word, 0, codePointCount, wordCodePoints);
+    const HistoricalInfo historicalInfo(timestamp, 0 /* level */, count);
+    return dictionary->updateCounter(&prevWordsInfo,
+            CodePointArrayView(wordCodePoints, codePointCount), isValidWord == JNI_TRUE,
+            historicalInfo);
+}
+
 // Returns how many language model params are processed.
 static int latinime_BinaryDictionary_addMultipleDictionaryEntries(JNIEnv *env, jclass clazz,
         jlong dict, jobjectArray languageModelParams, jint startIndex) {
@@ -494,8 +517,8 @@
         }
         // Use 1 for count to indicate the word has inputted.
         const UnigramProperty unigramProperty(false /* isBeginningOfSentence */, isNotAWord,
-                isBlacklisted, unigramProbability, timestamp, 0 /* level */, 1 /* count */,
-                &shortcuts);
+                isBlacklisted, unigramProbability,
+                HistoricalInfo(timestamp, 0 /* level */, 1 /* count */), std::move(shortcuts));
         dictionary->addUnigramEntry(CodePointArrayView(word1CodePoints, word1Length),
                 &unigramProperty);
         if (word0) {
@@ -503,7 +526,7 @@
             // Use 1 for count to indicate the bigram has inputted.
             const NgramProperty ngramProperty(
                     CodePointArrayView(word1CodePoints, word1Length).toVector(),
-                    bigramProbability, timestamp, 0 /* level */, 1 /* count */);
+                    bigramProbability, HistoricalInfo(timestamp, 0 /* level */, 1 /* count */));
             const PrevWordsInfo prevWordsInfo(word0CodePoints, word0Length,
                     false /* isBeginningOfSentence */);
             dictionary->addNgramEntry(&prevWordsInfo, &ngramProperty);
@@ -724,6 +747,11 @@
         reinterpret_cast<void *>(latinime_BinaryDictionary_removeNgramEntry)
     },
     {
+        const_cast<char *>("updateCounterNative"),
+        const_cast<char *>("(J[[I[Z[IZII)Z"),
+        reinterpret_cast<void *>(latinime_BinaryDictionary_updateCounter)
+    },
+    {
         const_cast<char *>("addMultipleDictionaryEntriesNative"),
         const_cast<char *>(
                 "(J[Lcom/android/inputmethod/latin/utils/LanguageModelParam;I)I"),
diff --git a/native/jni/src/defines.h b/native/jni/src/defines.h
index e55c9eb..8851185 100644
--- a/native/jni/src/defines.h
+++ b/native/jni/src/defines.h
@@ -119,7 +119,7 @@
         const int probability) {
     static char charBuf[50];
     const int N = intArrayToCharArray(word, length, charBuf, NELEMS(charBuf));
-    if (N > 1) {
+    if (N > 0) {
         AKLOGI("%2d [ %s ] (%d)", rank, charBuf, probability);
     }
 }
diff --git a/native/jni/src/suggest/core/dictionary/dictionary.cpp b/native/jni/src/suggest/core/dictionary/dictionary.cpp
index c3f4229..7a69d3c 100644
--- a/native/jni/src/suggest/core/dictionary/dictionary.cpp
+++ b/native/jni/src/suggest/core/dictionary/dictionary.cpp
@@ -55,9 +55,6 @@
     suggest->getSuggestions(proximityInfo, traverseSession, xcoordinates,
             ycoordinates, times, pointerIds, inputCodePoints, inputSize,
             weightOfLangModelVsSpatialModel, outSuggestionResults);
-    if (DEBUG_DICT) {
-        outSuggestionResults->dumpSuggestions();
-    }
 }
 
 Dictionary::NgramListenerForPrediction::NgramListenerForPrediction(
@@ -155,6 +152,14 @@
     return mDictionaryStructureWithBufferPolicy->removeNgramEntry(prevWordsInfo, codePoints);
 }
 
+bool Dictionary::updateCounter(const PrevWordsInfo *const prevWordsInfo,
+        const CodePointArrayView codePoints, const bool isValidWord,
+        const HistoricalInfo historicalInfo) {
+    TimeKeeper::setCurrentTime();
+    return mDictionaryStructureWithBufferPolicy->updateCounter(prevWordsInfo, codePoints,
+            isValidWord, historicalInfo);
+}
+
 bool Dictionary::flush(const char *const filePath) {
     TimeKeeper::setCurrentTime();
     return mDictionaryStructureWithBufferPolicy->flush(filePath);
diff --git a/native/jni/src/suggest/core/dictionary/dictionary.h b/native/jni/src/suggest/core/dictionary/dictionary.h
index 09f8eac..a58dbfb 100644
--- a/native/jni/src/suggest/core/dictionary/dictionary.h
+++ b/native/jni/src/suggest/core/dictionary/dictionary.h
@@ -22,6 +22,7 @@
 #include "defines.h"
 #include "jni.h"
 #include "suggest/core/dictionary/ngram_listener.h"
+#include "suggest/core/dictionary/property/historical_info.h"
 #include "suggest/core/dictionary/property/word_property.h"
 #include "suggest/core/policy/dictionary_header_structure_policy.h"
 #include "suggest/core/policy/dictionary_structure_with_buffer_policy.h"
@@ -90,6 +91,10 @@
     bool removeNgramEntry(const PrevWordsInfo *const prevWordsInfo,
             const CodePointArrayView codePoints);
 
+    bool updateCounter(const PrevWordsInfo *const prevWordsInfo,
+            const CodePointArrayView codePoints, const bool isValidWord,
+            const HistoricalInfo historicalInfo);
+
     bool flush(const char *const filePath);
 
     bool flushWithGC(const char *const filePath);
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/historical_info.h b/native/jni/src/suggest/core/dictionary/property/historical_info.h
similarity index 85%
rename from native/jni/src/suggest/policyimpl/dictionary/utils/historical_info.h
rename to native/jni/src/suggest/core/dictionary/property/historical_info.h
index 428ca86..5ed9ebf 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/historical_info.h
+++ b/native/jni/src/suggest/core/dictionary/property/historical_info.h
@@ -34,7 +34,7 @@
         return mTimestamp != NOT_A_TIMESTAMP;
     }
 
-    int getTimeStamp() const {
+    int getTimestamp() const {
         return mTimestamp;
     }
 
@@ -47,12 +47,12 @@
     }
 
  private:
-    // Copy constructor is public to use this class as a type of return value.
-    DISALLOW_ASSIGNMENT_OPERATOR(HistoricalInfo);
+    // Default copy constructor and assign operator are used for using in std::vector.
 
-    const int mTimestamp;
-    const int mLevel;
-    const int mCount;
+    // TODO: Make members const.
+    int mTimestamp;
+    int mLevel;
+    int mCount;
 };
 } // namespace latinime
 #endif /* LATINIME_HISTORICAL_INFO_H */
diff --git a/native/jni/src/suggest/core/dictionary/property/ngram_property.h b/native/jni/src/suggest/core/dictionary/property/ngram_property.h
index 5d822fa..dce4600 100644
--- a/native/jni/src/suggest/core/dictionary/property/ngram_property.h
+++ b/native/jni/src/suggest/core/dictionary/property/ngram_property.h
@@ -20,15 +20,16 @@
 #include <vector>
 
 #include "defines.h"
+#include "suggest/core/dictionary/property/historical_info.h"
 
 namespace latinime {
 
 class NgramProperty {
  public:
     NgramProperty(const std::vector<int> &&targetCodePoints, const int probability,
-            const int timestamp, const int level, const int count)
+            const HistoricalInfo historicalInfo)
             : mTargetCodePoints(std::move(targetCodePoints)), mProbability(probability),
-              mTimestamp(timestamp), mLevel(level), mCount(count) {}
+              mHistoricalInfo(historicalInfo) {}
 
     const std::vector<int> *getTargetCodePoints() const {
         return &mTargetCodePoints;
@@ -38,16 +39,8 @@
         return mProbability;
     }
 
-    int getTimestamp() const {
-        return mTimestamp;
-    }
-
-    int getLevel() const {
-        return mLevel;
-    }
-
-    int getCount() const {
-        return mCount;
+    const HistoricalInfo getHistoricalInfo() const {
+        return mHistoricalInfo;
     }
 
  private:
@@ -57,9 +50,7 @@
     // TODO: Make members const.
     std::vector<int> mTargetCodePoints;
     int mProbability;
-    int mTimestamp;
-    int mLevel;
-    int mCount;
+    HistoricalInfo mHistoricalInfo;
 };
 } // namespace latinime
 #endif // LATINIME_NGRAM_PROPERTY_H
diff --git a/native/jni/src/suggest/core/dictionary/property/unigram_property.h b/native/jni/src/suggest/core/dictionary/property/unigram_property.h
index b7e7d66..d1f0ab4 100644
--- a/native/jni/src/suggest/core/dictionary/property/unigram_property.h
+++ b/native/jni/src/suggest/core/dictionary/property/unigram_property.h
@@ -20,6 +20,7 @@
 #include <vector>
 
 #include "defines.h"
+#include "suggest/core/dictionary/property/historical_info.h"
 
 namespace latinime {
 
@@ -50,15 +51,21 @@
 
     UnigramProperty()
             : mRepresentsBeginningOfSentence(false), mIsNotAWord(false), mIsBlacklisted(false),
-              mProbability(NOT_A_PROBABILITY), mTimestamp(NOT_A_TIMESTAMP), mLevel(0), mCount(0),
-              mShortcuts() {}
+              mProbability(NOT_A_PROBABILITY), mHistoricalInfo(), mShortcuts() {}
 
     UnigramProperty(const bool representsBeginningOfSentence, const bool isNotAWord,
-            const bool isBlacklisted, const int probability, const int timestamp, const int level,
-            const int count, const std::vector<ShortcutProperty> *const shortcuts)
+            const bool isBlacklisted, const int probability, const HistoricalInfo historicalInfo,
+            const std::vector<ShortcutProperty> &&shortcuts)
             : mRepresentsBeginningOfSentence(representsBeginningOfSentence),
               mIsNotAWord(isNotAWord), mIsBlacklisted(isBlacklisted), mProbability(probability),
-              mTimestamp(timestamp), mLevel(level), mCount(count), mShortcuts(*shortcuts) {}
+              mHistoricalInfo(historicalInfo), mShortcuts(std::move(shortcuts)) {}
+
+    // Without shortcuts.
+    UnigramProperty(const bool representsBeginningOfSentence, const bool isNotAWord,
+            const bool isBlacklisted, const int probability, const HistoricalInfo historicalInfo)
+            : mRepresentsBeginningOfSentence(representsBeginningOfSentence),
+              mIsNotAWord(isNotAWord), mIsBlacklisted(isBlacklisted), mProbability(probability),
+              mHistoricalInfo(historicalInfo), mShortcuts() {}
 
     bool representsBeginningOfSentence() const {
         return mRepresentsBeginningOfSentence;
@@ -85,16 +92,8 @@
         return mProbability;
     }
 
-    int getTimestamp() const {
-        return mTimestamp;
-    }
-
-    int getLevel() const {
-        return mLevel;
-    }
-
-    int getCount() const {
-        return mCount;
+    const HistoricalInfo getHistoricalInfo() const {
+        return mHistoricalInfo;
     }
 
     const std::vector<ShortcutProperty> &getShortcuts() const {
@@ -110,10 +109,7 @@
     bool mIsNotAWord;
     bool mIsBlacklisted;
     int mProbability;
-    // Historical information
-    int mTimestamp;
-    int mLevel;
-    int mCount;
+    HistoricalInfo mHistoricalInfo;
     std::vector<ShortcutProperty> mShortcuts;
 };
 } // namespace latinime
diff --git a/native/jni/src/suggest/core/dictionary/property/word_property.cpp b/native/jni/src/suggest/core/dictionary/property/word_property.cpp
index e3406fa..caac8fe 100644
--- a/native/jni/src/suggest/core/dictionary/property/word_property.cpp
+++ b/native/jni/src/suggest/core/dictionary/property/word_property.cpp
@@ -17,6 +17,7 @@
 #include "suggest/core/dictionary/property/word_property.h"
 
 #include "utils/jni_data_utils.h"
+#include "suggest/core/dictionary/property/historical_info.h"
 
 namespace latinime {
 
@@ -31,8 +32,9 @@
             !mNgrams.empty(), mUnigramProperty.hasShortcuts(),
             mUnigramProperty.representsBeginningOfSentence()};
     env->SetBooleanArrayRegion(outFlags, 0 /* start */, NELEMS(flags), flags);
-    int probabilityInfo[] = {mUnigramProperty.getProbability(), mUnigramProperty.getTimestamp(),
-            mUnigramProperty.getLevel(), mUnigramProperty.getCount()};
+    const HistoricalInfo &historicalInfo = mUnigramProperty.getHistoricalInfo();
+    int probabilityInfo[] = {mUnigramProperty.getProbability(), historicalInfo.getTimestamp(),
+            historicalInfo.getLevel(), historicalInfo.getCount()};
     env->SetIntArrayRegion(outProbabilityInfo, 0 /* start */, NELEMS(probabilityInfo),
             probabilityInfo);
 
@@ -51,10 +53,10 @@
                 false /* needsNullTermination */);
         env->CallBooleanMethod(outBigramTargets, addMethodId, bigramWord1CodePointArray);
         env->DeleteLocalRef(bigramWord1CodePointArray);
-
+        const HistoricalInfo &ngramHistoricalInfo = ngramProperty.getHistoricalInfo();
         int bigramProbabilityInfo[] = {ngramProperty.getProbability(),
-                ngramProperty.getTimestamp(), ngramProperty.getLevel(),
-                ngramProperty.getCount()};
+                ngramHistoricalInfo.getTimestamp(), ngramHistoricalInfo.getLevel(),
+                ngramHistoricalInfo.getCount()};
         jintArray bigramProbabilityInfoArray = env->NewIntArray(NELEMS(bigramProbabilityInfo));
         env->SetIntArrayRegion(bigramProbabilityInfoArray, 0 /* start */,
                 NELEMS(bigramProbabilityInfo), bigramProbabilityInfo);
diff --git a/native/jni/src/suggest/core/layout/additional_proximity_chars.cpp b/native/jni/src/suggest/core/layout/additional_proximity_chars.cpp
index 34b8b37..8b39f7d 100644
--- a/native/jni/src/suggest/core/layout/additional_proximity_chars.cpp
+++ b/native/jni/src/suggest/core/layout/additional_proximity_chars.cpp
@@ -19,7 +19,7 @@
 namespace latinime {
 // TODO: Stop using hardcoded additional proximity characters.
 // TODO: Have proximity character informations in each language's binary dictionary.
-const char *AdditionalProximityChars::LOCALE_EN_US = "en";
+const int AdditionalProximityChars::LOCALE_EN_US[LOCALE_EN_US_SIZE] = { 'e', 'n' };
 
 const int AdditionalProximityChars::EN_US_ADDITIONAL_A[EN_US_ADDITIONAL_A_SIZE] = {
     'e', 'i', 'o', 'u'
diff --git a/native/jni/src/suggest/core/layout/additional_proximity_chars.h b/native/jni/src/suggest/core/layout/additional_proximity_chars.h
index a88fd6c..2260be9 100644
--- a/native/jni/src/suggest/core/layout/additional_proximity_chars.h
+++ b/native/jni/src/suggest/core/layout/additional_proximity_chars.h
@@ -18,6 +18,7 @@
 #define LATINIME_ADDITIONAL_PROXIMITY_CHARS_H
 
 #include <cstring>
+#include <vector>
 
 #include "defines.h"
 
@@ -26,7 +27,8 @@
 class AdditionalProximityChars {
  private:
     DISALLOW_IMPLICIT_CONSTRUCTORS(AdditionalProximityChars);
-    static const char *LOCALE_EN_US;
+    static const int LOCALE_EN_US_SIZE = 2;
+    static const int LOCALE_EN_US[LOCALE_EN_US_SIZE];
     static const int EN_US_ADDITIONAL_A_SIZE = 4;
     static const int EN_US_ADDITIONAL_A[];
     static const int EN_US_ADDITIONAL_E_SIZE = 4;
@@ -38,15 +40,22 @@
     static const int EN_US_ADDITIONAL_U_SIZE = 4;
     static const int EN_US_ADDITIONAL_U[];
 
-    AK_FORCE_INLINE static bool isEnLocale(const char *localeStr) {
-        const size_t LOCALE_EN_US_SIZE = strlen(LOCALE_EN_US);
-        return localeStr && strlen(localeStr) >= LOCALE_EN_US_SIZE
-                && strncmp(localeStr, LOCALE_EN_US, LOCALE_EN_US_SIZE) == 0;
+    AK_FORCE_INLINE static bool isEnLocale(const std::vector<int> *locale) {
+        const int NCHARS = NELEMS(LOCALE_EN_US);
+        if (locale->size() < NCHARS) {
+            return false;
+        }
+        for (int i = 0; i < NCHARS; ++i) {
+            if ((*locale)[i] != LOCALE_EN_US[i]) {
+                return false;
+            }
+        }
+        return true;
     }
 
  public:
-    static int getAdditionalCharsSize(const char *const localeStr, const int c) {
-        if (!isEnLocale(localeStr)) {
+    static int getAdditionalCharsSize(const std::vector<int> *locale, const int c) {
+        if (!isEnLocale(locale)) {
             return 0;
         }
         switch (c) {
@@ -65,8 +74,8 @@
         }
     }
 
-    static const int *getAdditionalChars(const char *const localeStr, const int c) {
-        if (!isEnLocale(localeStr)) {
+    static const int *getAdditionalChars(const std::vector<int> *locale, const int c) {
+        if (!isEnLocale(locale)) {
             return 0;
         }
         switch (c) {
diff --git a/native/jni/src/suggest/core/layout/proximity_info.cpp b/native/jni/src/suggest/core/layout/proximity_info.cpp
index 4c75a18..933a5e1 100644
--- a/native/jni/src/suggest/core/layout/proximity_info.cpp
+++ b/native/jni/src/suggest/core/layout/proximity_info.cpp
@@ -49,13 +49,13 @@
     }
 }
 
-ProximityInfo::ProximityInfo(JNIEnv *env, const jstring localeJStr,
-        const int keyboardWidth, const int keyboardHeight, const int gridWidth,
-        const int gridHeight, const int mostCommonKeyWidth, const int mostCommonKeyHeight,
-        const jintArray proximityChars, const int keyCount, const jintArray keyXCoordinates,
-        const jintArray keyYCoordinates, const jintArray keyWidths, const jintArray keyHeights,
-        const jintArray keyCharCodes, const jfloatArray sweetSpotCenterXs,
-        const jfloatArray sweetSpotCenterYs, const jfloatArray sweetSpotRadii)
+ProximityInfo::ProximityInfo(JNIEnv *env, const int keyboardWidth, const int keyboardHeight,
+        const int gridWidth, const int gridHeight, const int mostCommonKeyWidth,
+        const int mostCommonKeyHeight, const jintArray proximityChars, const int keyCount,
+        const jintArray keyXCoordinates, const jintArray keyYCoordinates,
+        const jintArray keyWidths, const jintArray keyHeights, const jintArray keyCharCodes,
+        const jfloatArray sweetSpotCenterXs, const jfloatArray sweetSpotCenterYs,
+        const jfloatArray sweetSpotRadii)
         : GRID_WIDTH(gridWidth), GRID_HEIGHT(gridHeight), MOST_COMMON_KEY_WIDTH(mostCommonKeyWidth),
           MOST_COMMON_KEY_WIDTH_SQUARE(mostCommonKeyWidth * mostCommonKeyWidth),
           NORMALIZED_SQUARED_MOST_COMMON_KEY_HYPOTENUSE(1.0f +
@@ -82,13 +82,6 @@
     if (DEBUG_PROXIMITY_INFO) {
         AKLOGI("Create proximity info array %d", proximityCharsLength);
     }
-    const jsize localeCStrUtf8Length = env->GetStringUTFLength(localeJStr);
-    if (localeCStrUtf8Length >= MAX_LOCALE_STRING_LENGTH) {
-        AKLOGI("Locale string length too long: length=%d", localeCStrUtf8Length);
-        ASSERT(false);
-    }
-    memset(mLocaleStr, 0, sizeof(mLocaleStr));
-    env->GetStringUTFRegion(localeJStr, 0, env->GetStringLength(localeJStr), mLocaleStr);
     safeGetOrFillZeroIntArrayRegion(env, proximityChars, proximityCharsLength,
             mProximityCharsArray);
     safeGetOrFillZeroIntArrayRegion(env, keyXCoordinates, KEY_COUNT, mKeyXCoordinates);
diff --git a/native/jni/src/suggest/core/layout/proximity_info.h b/native/jni/src/suggest/core/layout/proximity_info.h
index d4e4537..f7c9076 100644
--- a/native/jni/src/suggest/core/layout/proximity_info.h
+++ b/native/jni/src/suggest/core/layout/proximity_info.h
@@ -18,6 +18,7 @@
 #define LATINIME_PROXIMITY_INFO_H
 
 #include <unordered_map>
+#include <vector>
 
 #include "defines.h"
 #include "jni.h"
@@ -27,9 +28,9 @@
 
 class ProximityInfo {
  public:
-    ProximityInfo(JNIEnv *env, const jstring localeJStr,
-            const int keyboardWidth, const int keyboardHeight, const int gridWidth,
-            const int gridHeight, const int mostCommonKeyWidth, const int mostCommonKeyHeight,
+    ProximityInfo(JNIEnv *env, const int keyboardWidth, const int keyboardHeight,
+            const int gridWidth, const int gridHeight,
+            const int mostCommonKeyWidth, const int mostCommonKeyHeight,
             const jintArray proximityChars, const int keyCount, const jintArray keyXCoordinates,
             const jintArray keyYCoordinates, const jintArray keyWidths, const jintArray keyHeights,
             const jintArray keyCharCodes, const jfloatArray sweetSpotCenterXs,
@@ -71,11 +72,11 @@
 
     AK_FORCE_INLINE void initializeProximities(const int *const inputCodes,
             const int *const inputXCoordinates, const int *const inputYCoordinates,
-            const int inputSize, int *allInputCodes) const {
+            const int inputSize, int *allInputCodes, const std::vector<int> *locale) const {
         ProximityInfoUtils::initializeProximities(inputCodes, inputXCoordinates, inputYCoordinates,
                 inputSize, mKeyXCoordinates, mKeyYCoordinates, mKeyWidths, mKeyHeights,
                 mProximityCharsArray, CELL_HEIGHT, CELL_WIDTH, GRID_WIDTH, MOST_COMMON_KEY_WIDTH,
-                KEY_COUNT, mLocaleStr, &mLowerCodePointToKeyMap, allInputCodes);
+                KEY_COUNT, locale, &mLowerCodePointToKeyMap, allInputCodes);
     }
 
     AK_FORCE_INLINE int getKeyIndexOf(const int c) const {
@@ -103,9 +104,6 @@
     const int KEYBOARD_HEIGHT;
     const float KEYBOARD_HYPOTENUSE;
     const bool HAS_TOUCH_POSITION_CORRECTION_DATA;
-    // Assuming locale strings such as en_US, sr-Latn etc.
-    static const int MAX_LOCALE_STRING_LENGTH = 10;
-    char mLocaleStr[MAX_LOCALE_STRING_LENGTH];
     int *mProximityCharsArray;
     int mKeyXCoordinates[MAX_KEY_COUNT_IN_A_KEYBOARD];
     int mKeyYCoordinates[MAX_KEY_COUNT_IN_A_KEYBOARD];
diff --git a/native/jni/src/suggest/core/layout/proximity_info_state.cpp b/native/jni/src/suggest/core/layout/proximity_info_state.cpp
index 91469e2..d43a002 100644
--- a/native/jni/src/suggest/core/layout/proximity_info_state.cpp
+++ b/native/jni/src/suggest/core/layout/proximity_info_state.cpp
@@ -42,7 +42,7 @@
 void ProximityInfoState::initInputParams(const int pointerId, const float maxPointToKeyLength,
         const ProximityInfo *proximityInfo, const int *const inputCodes, const int inputSize,
         const int *const xCoordinates, const int *const yCoordinates, const int *const times,
-        const int *const pointerIds, const bool isGeometric) {
+        const int *const pointerIds, const bool isGeometric, const std::vector<int> *locale) {
     ASSERT(isGeometric || (inputSize < MAX_WORD_LENGTH));
     mIsContinuousSuggestionPossible = (mHasBeenUpdatedByGeometricInput != isGeometric) ?
             false : ProximityInfoStateUtils::checkAndReturnIsContinuousSuggestionPossible(
@@ -66,7 +66,7 @@
 
     if (!isGeometric && pointerId == 0) {
         mProximityInfo->initializeProximities(inputCodes, xCoordinates, yCoordinates,
-                inputSize, mInputProximities);
+                inputSize, mInputProximities, locale);
     }
 
     ///////////////////////
diff --git a/native/jni/src/suggest/core/layout/proximity_info_state.h b/native/jni/src/suggest/core/layout/proximity_info_state.h
index e6180fe..a2d6635 100644
--- a/native/jni/src/suggest/core/layout/proximity_info_state.h
+++ b/native/jni/src/suggest/core/layout/proximity_info_state.h
@@ -37,7 +37,8 @@
     void initInputParams(const int pointerId, const float maxPointToKeyLength,
             const ProximityInfo *proximityInfo, const int *const inputCodes,
             const int inputSize, const int *xCoordinates, const int *yCoordinates,
-            const int *const times, const int *const pointerIds, const bool isGeometric);
+            const int *const times, const int *const pointerIds, const bool isGeometric,
+            const std::vector<int> *locale);
 
     /////////////////////////////////////////
     // Defined here                        //
diff --git a/native/jni/src/suggest/core/layout/proximity_info_utils.h b/native/jni/src/suggest/core/layout/proximity_info_utils.h
index 178aada..79d0615 100644
--- a/native/jni/src/suggest/core/layout/proximity_info_utils.h
+++ b/native/jni/src/suggest/core/layout/proximity_info_utils.h
@@ -19,6 +19,7 @@
 
 #include <cmath>
 #include <unordered_map>
+#include <vector>
 
 #include "defines.h"
 #include "suggest/core/layout/additional_proximity_chars.h"
@@ -51,7 +52,7 @@
             const int *const keyYCoordinates, const int *const keyWidths, const int *keyHeights,
             const int *const proximityCharsArray, const int cellHeight, const int cellWidth,
             const int gridWidth, const int mostCommonKeyWidth, const int keyCount,
-            const char *const localeStr,
+            const std::vector<int> *locale,
             const std::unordered_map<int, int> *const codeToKeyMap, int *inputProximities) {
         // Initialize
         // - mInputCodes
@@ -64,7 +65,7 @@
             int *proximities = &inputProximities[i * MAX_PROXIMITY_CHARS_SIZE];
             calculateProximities(keyXCoordinates, keyYCoordinates, keyWidths, keyHeights,
                     proximityCharsArray, cellHeight, cellWidth, gridWidth, mostCommonKeyWidth,
-                    keyCount, x, y, primaryKey, localeStr, codeToKeyMap, proximities);
+                    keyCount, x, y, primaryKey, locale, codeToKeyMap, proximities);
         }
 
         if (DEBUG_PROXIMITY_CHARS) {
@@ -143,7 +144,7 @@
             const int *const keyYCoordinates, const int *const keyWidths, const int *keyHeights,
             const int *const proximityCharsArray, const int cellHeight, const int cellWidth,
             const int gridWidth, const int mostCommonKeyWidth, const int keyCount,
-            const int x, const int y, const int primaryKey, const char *const localeStr,
+            const int x, const int y, const int primaryKey, const std::vector<int> *locale,
             const std::unordered_map<int, int> *const codeToKeyMap, int *proximities) {
         const int mostCommonKeyWidthSquare = mostCommonKeyWidth * mostCommonKeyWidth;
         int insertPos = 0;
@@ -177,7 +178,7 @@
                 }
             }
             const int additionalProximitySize =
-                    AdditionalProximityChars::getAdditionalCharsSize(localeStr, primaryKey);
+                    AdditionalProximityChars::getAdditionalCharsSize(locale, primaryKey);
             if (additionalProximitySize > 0) {
                 proximities[insertPos++] = ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE;
                 if (insertPos >= MAX_PROXIMITY_CHARS_SIZE) {
@@ -188,7 +189,7 @@
                 }
 
                 const int *additionalProximityChars =
-                        AdditionalProximityChars::getAdditionalChars(localeStr, primaryKey);
+                        AdditionalProximityChars::getAdditionalChars(locale, primaryKey);
                 for (int j = 0; j < additionalProximitySize; ++j) {
                     const int ac = additionalProximityChars[j];
                     int k = 0;
diff --git a/native/jni/src/suggest/core/policy/dictionary_structure_with_buffer_policy.h b/native/jni/src/suggest/core/policy/dictionary_structure_with_buffer_policy.h
index f4b9798..6624b79 100644
--- a/native/jni/src/suggest/core/policy/dictionary_structure_with_buffer_policy.h
+++ b/native/jni/src/suggest/core/policy/dictionary_structure_with_buffer_policy.h
@@ -21,6 +21,7 @@
 
 #include "defines.h"
 #include "suggest/core/dictionary/binary_dictionary_shortcut_iterator.h"
+#include "suggest/core/dictionary/property/historical_info.h"
 #include "suggest/core/dictionary/property/word_property.h"
 #include "suggest/core/dictionary/word_attributes.h"
 #include "utils/int_array_view.h"
@@ -87,6 +88,11 @@
     virtual bool removeNgramEntry(const PrevWordsInfo *const prevWordsInfo,
             const CodePointArrayView wordCodePoints) = 0;
 
+    // Returns whether the update was success or not.
+    virtual bool updateCounter(const PrevWordsInfo *const prevWordsInfo,
+            const CodePointArrayView wordCodePoints, const bool isValidWord,
+            const HistoricalInfo historicalInfo) = 0;
+
     // Returns whether the flush was success or not.
     virtual bool flush(const char *const filePath) = 0;
 
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 4d7505a..b4d01d0 100644
--- a/native/jni/src/suggest/core/session/dic_traverse_session.cpp
+++ b/native/jni/src/suggest/core/session/dic_traverse_session.cpp
@@ -69,8 +69,12 @@
     for (int i = 0; i < maxPointerCount; ++i) {
         mProximityInfoStates[i].initInputParams(i, maxSpatialDistance, getProximityInfo(),
                 inputCodePoints, inputSize, inputXs, inputYs, times, pointerIds,
-                maxPointerCount == MAX_POINTER_COUNT_G
-                /* TODO: this is a hack. fix proximity info state */);
+                // Right now the line below is trying to figure out whether this is a gesture by
+                // looking at the pointer count and assuming whatever is above the cutoff is
+                // a gesture and whatever is below is type. This is hacky and incorrect, we
+                // should pass the correct information instead.
+                maxPointerCount == MAX_POINTER_COUNT_G,
+                getDictionaryStructurePolicy()->getHeaderStructurePolicy()->getLocale());
         mInputSize += mProximityInfoStates[i].size();
     }
 }
diff --git a/native/jni/src/suggest/core/session/prev_words_info.h b/native/jni/src/suggest/core/session/prev_words_info.h
index 02e82a8..553d5ad 100644
--- a/native/jni/src/suggest/core/session/prev_words_info.h
+++ b/native/jni/src/suggest/core/session/prev_words_info.h
@@ -33,7 +33,7 @@
         clear();
     }
 
-    PrevWordsInfo(PrevWordsInfo &&prevWordsInfo)
+    PrevWordsInfo(const PrevWordsInfo &prevWordsInfo)
             : mPrevWordCount(prevWordsInfo.mPrevWordCount) {
         for (size_t i = 0; i < mPrevWordCount; ++i) {
             mPrevWordCodePointCount[i] = prevWordsInfo.mPrevWordCodePointCount[i];
@@ -73,6 +73,16 @@
         mIsBeginningOfSentence[0] = isBeginningOfSentence;
     }
 
+    size_t getPrevWordCount() const {
+        return mPrevWordCount;
+    }
+
+    // TODO: Remove.
+    const PrevWordsInfo getTrimmedPrevWordsInfo(const size_t maxPrevWordCount) const {
+        return PrevWordsInfo(mPrevWordCodePoints, mPrevWordCodePointCount, mIsBeginningOfSentence,
+                std::min(mPrevWordCount, maxPrevWordCount));
+    }
+
     bool isValid() const {
         if (mPrevWordCodePointCount[0] > 0) {
             return true;
@@ -112,7 +122,7 @@
     }
 
  private:
-    DISALLOW_COPY_AND_ASSIGN(PrevWordsInfo);
+    DISALLOW_ASSIGNMENT_OPERATOR(PrevWordsInfo);
 
     static int getWordId(const DictionaryStructureWithBufferPolicy *const dictStructurePolicy,
             const int *const wordCodePoints, const int wordCodePointCount,
diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp
index 6ed65d9..4c4dfc5 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp
@@ -35,23 +35,15 @@
 // count.
 const char *const HeaderPolicy::HAS_HISTORICAL_INFO_KEY = "HAS_HISTORICAL_INFO";
 const char *const HeaderPolicy::LOCALE_KEY = "locale"; // match Java declaration
-const char *const HeaderPolicy::FORGETTING_CURVE_OCCURRENCES_TO_LEVEL_UP_KEY =
-        "FORGETTING_CURVE_OCCURRENCES_TO_LEVEL_UP";
 const char *const HeaderPolicy::FORGETTING_CURVE_PROBABILITY_VALUES_TABLE_ID_KEY =
         "FORGETTING_CURVE_PROBABILITY_VALUES_TABLE_ID";
-const char *const HeaderPolicy::FORGETTING_CURVE_DURATION_TO_LEVEL_DOWN_IN_SECONDS_KEY =
-        "FORGETTING_CURVE_DURATION_TO_LEVEL_DOWN_IN_SECONDS";
 
 const char *const HeaderPolicy::MAX_UNIGRAM_COUNT_KEY = "MAX_UNIGRAM_COUNT";
 const char *const HeaderPolicy::MAX_BIGRAM_COUNT_KEY = "MAX_BIGRAM_COUNT";
 
 const int HeaderPolicy::DEFAULT_MULTIPLE_WORDS_DEMOTION_RATE = 100;
 const float HeaderPolicy::MULTIPLE_WORD_COST_MULTIPLIER_SCALE = 100.0f;
-const int HeaderPolicy::DEFAULT_FORGETTING_CURVE_OCCURRENCES_TO_LEVEL_UP = 2;
 const int HeaderPolicy::DEFAULT_FORGETTING_CURVE_PROBABILITY_VALUES_TABLE_ID = 3;
-// 30 days
-const int HeaderPolicy::DEFAULT_FORGETTING_CURVE_DURATION_TO_LEVEL_DOWN_IN_SECONDS =
-        30 * 24 * 60 * 60;
 
 const int HeaderPolicy::DEFAULT_MAX_UNIGRAM_COUNT = 10000;
 const int HeaderPolicy::DEFAULT_MAX_BIGRAM_COUNT = 10000;
diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h
index daf40d4..bc8eade 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h
@@ -53,15 +53,9 @@
                       EXTENDED_REGION_SIZE_KEY, 0 /* defaultValue */)),
               mHasHistoricalInfoOfWords(HeaderReadWriteUtils::readBoolAttributeValue(
                       &mAttributeMap, HAS_HISTORICAL_INFO_KEY, false /* defaultValue */)),
-              mForgettingCurveOccurrencesToLevelUp(HeaderReadWriteUtils::readIntAttributeValue(
-                      &mAttributeMap, FORGETTING_CURVE_OCCURRENCES_TO_LEVEL_UP_KEY,
-                      DEFAULT_FORGETTING_CURVE_OCCURRENCES_TO_LEVEL_UP)),
               mForgettingCurveProbabilityValuesTableId(HeaderReadWriteUtils::readIntAttributeValue(
                       &mAttributeMap, FORGETTING_CURVE_PROBABILITY_VALUES_TABLE_ID_KEY,
                       DEFAULT_FORGETTING_CURVE_PROBABILITY_VALUES_TABLE_ID)),
-              mForgettingCurveDurationToLevelDown(HeaderReadWriteUtils::readIntAttributeValue(
-                      &mAttributeMap, FORGETTING_CURVE_DURATION_TO_LEVEL_DOWN_IN_SECONDS_KEY,
-                      DEFAULT_FORGETTING_CURVE_DURATION_TO_LEVEL_DOWN_IN_SECONDS)),
               mMaxUnigramCount(HeaderReadWriteUtils::readIntAttributeValue(
                       &mAttributeMap, MAX_UNIGRAM_COUNT_KEY, DEFAULT_MAX_UNIGRAM_COUNT)),
               mMaxBigramCount(HeaderReadWriteUtils::readIntAttributeValue(
@@ -86,15 +80,9 @@
               mUnigramCount(0), mBigramCount(0), mExtendedRegionSize(0),
               mHasHistoricalInfoOfWords(HeaderReadWriteUtils::readBoolAttributeValue(
                       &mAttributeMap, HAS_HISTORICAL_INFO_KEY, false /* defaultValue */)),
-              mForgettingCurveOccurrencesToLevelUp(HeaderReadWriteUtils::readIntAttributeValue(
-                      &mAttributeMap, FORGETTING_CURVE_OCCURRENCES_TO_LEVEL_UP_KEY,
-                      DEFAULT_FORGETTING_CURVE_OCCURRENCES_TO_LEVEL_UP)),
               mForgettingCurveProbabilityValuesTableId(HeaderReadWriteUtils::readIntAttributeValue(
                       &mAttributeMap, FORGETTING_CURVE_PROBABILITY_VALUES_TABLE_ID_KEY,
                       DEFAULT_FORGETTING_CURVE_PROBABILITY_VALUES_TABLE_ID)),
-              mForgettingCurveDurationToLevelDown(HeaderReadWriteUtils::readIntAttributeValue(
-                      &mAttributeMap, FORGETTING_CURVE_DURATION_TO_LEVEL_DOWN_IN_SECONDS_KEY,
-                      DEFAULT_FORGETTING_CURVE_DURATION_TO_LEVEL_DOWN_IN_SECONDS)),
               mMaxUnigramCount(HeaderReadWriteUtils::readIntAttributeValue(
                       &mAttributeMap, MAX_UNIGRAM_COUNT_KEY, DEFAULT_MAX_UNIGRAM_COUNT)),
               mMaxBigramCount(HeaderReadWriteUtils::readIntAttributeValue(
@@ -113,12 +101,8 @@
               mUnigramCount(headerPolicy->mUnigramCount), mBigramCount(headerPolicy->mBigramCount),
               mExtendedRegionSize(headerPolicy->mExtendedRegionSize),
               mHasHistoricalInfoOfWords(headerPolicy->mHasHistoricalInfoOfWords),
-              mForgettingCurveOccurrencesToLevelUp(
-                      headerPolicy->mForgettingCurveOccurrencesToLevelUp),
               mForgettingCurveProbabilityValuesTableId(
                       headerPolicy->mForgettingCurveProbabilityValuesTableId),
-              mForgettingCurveDurationToLevelDown(
-                      headerPolicy->mForgettingCurveDurationToLevelDown),
               mMaxUnigramCount(headerPolicy->mMaxUnigramCount),
               mMaxBigramCount(headerPolicy->mMaxBigramCount),
               mCodePointTable(headerPolicy->mCodePointTable) {}
@@ -130,8 +114,7 @@
               mRequiresGermanUmlautProcessing(false), mIsDecayingDict(false),
               mDate(0), mLastDecayedTime(0), mUnigramCount(0), mBigramCount(0),
               mExtendedRegionSize(0), mHasHistoricalInfoOfWords(false),
-              mForgettingCurveOccurrencesToLevelUp(0), mForgettingCurveProbabilityValuesTableId(0),
-              mForgettingCurveDurationToLevelDown(0), mMaxUnigramCount(0), mMaxBigramCount(0),
+              mForgettingCurveProbabilityValuesTableId(0), mMaxUnigramCount(0), mMaxBigramCount(0),
               mCodePointTable(nullptr) {}
 
     ~HeaderPolicy() {}
@@ -217,18 +200,10 @@
         return &mAttributeMap;
     }
 
-    AK_FORCE_INLINE int getForgettingCurveOccurrencesToLevelUp() const {
-        return mForgettingCurveOccurrencesToLevelUp;
-    }
-
     AK_FORCE_INLINE int getForgettingCurveProbabilityValuesTableId() const {
         return mForgettingCurveProbabilityValuesTableId;
     }
 
-    AK_FORCE_INLINE int getForgettingCurveDurationToLevelDown() const {
-        return mForgettingCurveDurationToLevelDown;
-    }
-
     AK_FORCE_INLINE int getMaxUnigramCount() const {
         return mMaxUnigramCount;
     }
@@ -280,9 +255,7 @@
     static const char *const MAX_BIGRAM_COUNT_KEY;
     static const int DEFAULT_MULTIPLE_WORDS_DEMOTION_RATE;
     static const float MULTIPLE_WORD_COST_MULTIPLIER_SCALE;
-    static const int DEFAULT_FORGETTING_CURVE_OCCURRENCES_TO_LEVEL_UP;
     static const int DEFAULT_FORGETTING_CURVE_PROBABILITY_VALUES_TABLE_ID;
-    static const int DEFAULT_FORGETTING_CURVE_DURATION_TO_LEVEL_DOWN_IN_SECONDS;
     static const int DEFAULT_MAX_UNIGRAM_COUNT;
     static const int DEFAULT_MAX_BIGRAM_COUNT;
 
@@ -300,9 +273,7 @@
     const int mBigramCount;
     const int mExtendedRegionSize;
     const bool mHasHistoricalInfoOfWords;
-    const int mForgettingCurveOccurrencesToLevelUp;
     const int mForgettingCurveProbabilityValuesTableId;
-    const int mForgettingCurveDurationToLevelDown;
     const int mMaxUnigramCount;
     const int mMaxBigramCount;
     const int *const mCodePointTable;
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/bigram/ver4_bigram_list_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/bigram/ver4_bigram_list_policy.cpp
index c639770..bc0f47f 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/bigram/ver4_bigram_list_policy.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/bigram/ver4_bigram_list_policy.cpp
@@ -267,8 +267,7 @@
         const NgramProperty *const ngramProperty) const {
     // TODO: Consolidate historical info and probability.
     if (mHeaderPolicy->hasHistoricalInfoOfWords()) {
-        const HistoricalInfo historicalInfoForUpdate(ngramProperty->getTimestamp(),
-                ngramProperty->getLevel(), ngramProperty->getCount());
+        const HistoricalInfo &historicalInfoForUpdate = ngramProperty->getHistoricalInfo();
         const HistoricalInfo updatedHistoricalInfo =
                 ForgettingCurveUtils::createUpdatedHistoricalInfo(
                         originalBigramEntry->getHistoricalInfo(), ngramProperty->getProbability(),
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/bigram_dict_content.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/bigram_dict_content.cpp
index e2dd93c..9e1adff 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/bigram_dict_content.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/bigram_dict_content.cpp
@@ -83,10 +83,10 @@
     }
     if (mHasHistoricalInfo) {
         const HistoricalInfo *const historicalInfo = bigramEntryToWrite->getHistoricalInfo();
-        if (!bigramListBuffer->writeUintAndAdvancePosition(historicalInfo->getTimeStamp(),
+        if (!bigramListBuffer->writeUintAndAdvancePosition(historicalInfo->getTimestamp(),
                 Ver4DictConstants::TIME_STAMP_FIELD_SIZE, entryWritingPos)) {
             AKLOGE("Cannot write bigram timestamps. pos: %d, timestamp: %d", *entryWritingPos,
-                    historicalInfo->getTimeStamp());
+                    historicalInfo->getTimestamp());
             return false;
         }
         if (!bigramListBuffer->writeUintAndAdvancePosition(historicalInfo->getLevel(),
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/bigram_entry.h b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/bigram_entry.h
index 40968b4..480095a 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/bigram_entry.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/bigram_entry.h
@@ -25,8 +25,8 @@
 #define LATINIME_BACKWARD_V402_BIGRAM_ENTRY_H
 
 #include "defines.h"
+#include "suggest/core/dictionary/property/historical_info.h"
 #include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_constants.h"
-#include "suggest/policyimpl/dictionary/utils/historical_info.h"
 
 namespace latinime {
 namespace backward {
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/probability_dict_content.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/probability_dict_content.cpp
index c671647..ef6166f 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/probability_dict_content.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/probability_dict_content.cpp
@@ -74,8 +74,8 @@
                 return false;
             }
             writingPos += getEntrySize();
-            mSize++;
         }
+        mSize = terminalId + 1;
     }
     return writeEntry(probabilityEntry, entryPos);
 }
@@ -100,7 +100,6 @@
 bool ProbabilityDictContent::runGC(
         const TerminalPositionLookupTable::TerminalIdMap *const terminalIdMap,
         const ProbabilityDictContent *const originalProbabilityDictContent) {
-    mSize = 0;
     for (TerminalPositionLookupTable::TerminalIdMap::const_iterator it = terminalIdMap->begin();
             it != terminalIdMap->end(); ++it) {
         const ProbabilityEntry probabilityEntry =
@@ -109,7 +108,6 @@
             AKLOGE("Cannot set probability entry in runGC. terminalId: %d", it->second);
             return false;
         }
-        mSize++;
     }
     return true;
 }
@@ -147,7 +145,7 @@
     }
     if (mHasHistoricalInfo) {
         const HistoricalInfo *const historicalInfo = probabilityEntry->getHistoricalInfo();
-        if (!bufferToWrite->writeUintAndAdvancePosition(historicalInfo->getTimeStamp(),
+        if (!bufferToWrite->writeUintAndAdvancePosition(historicalInfo->getTimestamp(),
                 Ver4DictConstants::TIME_STAMP_FIELD_SIZE, &writingPos)) {
             AKLOGE("Cannot write timestamp in probability dict content. pos: %d", writingPos);
             return false;
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/probability_entry.h b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/probability_entry.h
index 8ccfa33..4111a49 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/probability_entry.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/probability_entry.h
@@ -25,8 +25,8 @@
 #define LATINIME_BACKWARD_V402_PROBABILITY_ENTRY_H
 
 #include "defines.h"
+#include "suggest/core/dictionary/property/historical_info.h"
 #include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_constants.h"
-#include "suggest/policyimpl/dictionary/utils/historical_info.h"
 
 namespace latinime {
 namespace backward {
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_writer.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_writer.cpp
index 6827c3e..8d16974 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_writer.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_writer.cpp
@@ -396,8 +396,7 @@
         const UnigramProperty *const unigramProperty) const {
     // TODO: Consolidate historical info and probability.
     if (mHeaderPolicy->hasHistoricalInfoOfWords()) {
-        const HistoricalInfo historicalInfoForUpdate(unigramProperty->getTimestamp(),
-                unigramProperty->getLevel(), unigramProperty->getCount());
+        const HistoricalInfo &historicalInfoForUpdate = unigramProperty->getHistoricalInfo();
         const HistoricalInfo updatedHistoricalInfo =
                 ForgettingCurveUtils::createUpdatedHistoricalInfo(
                         originalProbabilityEntry->getHistoricalInfo(),
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.cpp
index 9631cf1..36eafa1 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.cpp
@@ -52,6 +52,7 @@
 const int Ver4PatriciaTriePolicy::MARGIN_TO_REFUSE_DYNAMIC_OPERATIONS = 1024;
 const int Ver4PatriciaTriePolicy::MIN_DICT_SIZE_TO_REFUSE_DYNAMIC_OPERATIONS =
         Ver4DictConstants::MAX_DICTIONARY_SIZE - MARGIN_TO_REFUSE_DYNAMIC_OPERATIONS;
+const int Ver4PatriciaTriePolicy::DUMMY_PROBABILITY_FOR_VALID_WORDS = 1;
 
 void Ver4PatriciaTriePolicy::createAndGetAllChildDicNodes(const DicNode *const dicNode,
         DicNodeVector *const childDicNodes) const {
@@ -145,18 +146,15 @@
 
 int Ver4PatriciaTriePolicy::getProbability(const int unigramProbability,
         const int bigramProbability) const {
-    if (mHeaderPolicy->isDecayingDict()) {
-        // Both probabilities are encoded. Decode them and get probability.
-        return ForgettingCurveUtils::getProbability(unigramProbability, bigramProbability);
-    } else {
-        if (unigramProbability == NOT_A_PROBABILITY) {
-            return NOT_A_PROBABILITY;
-        } else if (bigramProbability == NOT_A_PROBABILITY) {
-            return ProbabilityUtils::backoff(unigramProbability);
-        } else {
-            return bigramProbability;
-        }
+    // In the v4 format, bigramProbability is a conditional probability.
+    const int bigramConditionalProbability = bigramProbability;
+    if (unigramProbability == NOT_A_PROBABILITY) {
+        return NOT_A_PROBABILITY;
     }
+    if (bigramConditionalProbability == NOT_A_PROBABILITY) {
+        return ProbabilityUtils::backoff(unigramProbability);
+    }
+    return bigramConditionalProbability;
 }
 
 int Ver4PatriciaTriePolicy::getProbabilityOfWord(const WordIdArrayView prevWordIds,
@@ -169,37 +167,66 @@
     if (ptNodeParams.isDeleted() || ptNodeParams.isBlacklisted() || ptNodeParams.isNotAWord()) {
         return NOT_A_PROBABILITY;
     }
-    if (!prevWordIds.empty()) {
-        const int bigramsPosition = getBigramsPositionOfPtNode(
-                getTerminalPtNodePosFromWordId(prevWordIds[0]));
-        BinaryDictionaryBigramsIterator bigramsIt(&mBigramPolicy, bigramsPosition);
-        while (bigramsIt.hasNext()) {
-            bigramsIt.next();
-            if (bigramsIt.getBigramPos() == ptNodePos
-                    && bigramsIt.getProbability() != NOT_A_PROBABILITY) {
-                return getProbability(ptNodeParams.getProbability(), bigramsIt.getProbability());
-            }
-        }
+    if (prevWordIds.empty()) {
+        return getProbability(ptNodeParams.getProbability(), NOT_A_PROBABILITY);
+    }
+    if (prevWordIds[0] == NOT_A_WORD_ID) {
         return NOT_A_PROBABILITY;
     }
-    return getProbability(ptNodeParams.getProbability(), NOT_A_PROBABILITY);
+    const PtNodeParams prevWordPtNodeParams =
+            mNodeReader.fetchPtNodeParamsInBufferFromPtNodePos(prevWordIds[0]);
+    if (prevWordPtNodeParams.isDeleted()) {
+        return getProbability(ptNodeParams.getProbability(), NOT_A_PROBABILITY);
+    }
+    const int bigramsPosition = mBuffers->getBigramDictContent()->getBigramListHeadPos(
+            prevWordPtNodeParams.getTerminalId());
+    BinaryDictionaryBigramsIterator bigramsIt(&mBigramPolicy, bigramsPosition);
+    while (bigramsIt.hasNext()) {
+        bigramsIt.next();
+        if (bigramsIt.getBigramPos() == ptNodePos
+                && bigramsIt.getProbability() != NOT_A_PROBABILITY) {
+            const int bigramConditionalProbability = getBigramConditionalProbability(
+                    prevWordPtNodeParams.getProbability(), bigramsIt.getProbability());
+            return getProbability(ptNodeParams.getProbability(), bigramConditionalProbability);
+        }
+    }
+    return NOT_A_PROBABILITY;
 }
 
 void Ver4PatriciaTriePolicy::iterateNgramEntries(const WordIdArrayView prevWordIds,
         NgramListener *const listener) const {
-    if (prevWordIds.empty()) {
+    if (prevWordIds.firstOrDefault(NOT_A_DICT_POS) == NOT_A_DICT_POS) {
         return;
     }
-    const int bigramsPosition = getBigramsPositionOfPtNode(
-            getTerminalPtNodePosFromWordId(prevWordIds[0]));
+    const PtNodeParams prevWordPtNodeParams =
+            mNodeReader.fetchPtNodeParamsInBufferFromPtNodePos(prevWordIds[0]);
+    if (prevWordPtNodeParams.isDeleted()) {
+        return;
+    }
+    const int bigramsPosition = mBuffers->getBigramDictContent()->getBigramListHeadPos(
+            prevWordPtNodeParams.getTerminalId());
     BinaryDictionaryBigramsIterator bigramsIt(&mBigramPolicy, bigramsPosition);
     while (bigramsIt.hasNext()) {
         bigramsIt.next();
-        listener->onVisitEntry(bigramsIt.getProbability(),
+        const int bigramConditionalProbability = getBigramConditionalProbability(
+                prevWordPtNodeParams.getProbability(), bigramsIt.getProbability());
+        listener->onVisitEntry(bigramConditionalProbability,
                 getWordIdFromTerminalPtNodePos(bigramsIt.getBigramPos()));
     }
 }
 
+int Ver4PatriciaTriePolicy::getBigramConditionalProbability(const int prevWordUnigramProbability,
+        const int bigramProbability) const {
+    if (mHeaderPolicy->hasHistoricalInfoOfWords()) {
+        // Calculate conditional probability.
+        return std::min(MAX_PROBABILITY - prevWordUnigramProbability + bigramProbability,
+                MAX_PROBABILITY);
+    } else {
+        // bigramProbability is a conditional probability.
+        return bigramProbability;
+    }
+}
+
 BinaryDictionaryShortcutIterator Ver4PatriciaTriePolicy::getShortcutIterator(
         const int wordId) const {
     const int shortcutPos = getShortcutPositionOfPtNode(getTerminalPtNodePosFromWordId(wordId));
@@ -339,11 +366,9 @@
     }
     if (prevWordIds[0] == NOT_A_WORD_ID) {
         if (prevWordsInfo->isNthPrevWordBeginningOfSentence(1 /* n */)) {
-            const std::vector<UnigramProperty::ShortcutProperty> shortcuts;
             const UnigramProperty beginningOfSentenceUnigramProperty(
                     true /* representsBeginningOfSentence */, true /* isNotAWord */,
-                    false /* isBlacklisted */, MAX_PROBABILITY /* probability */,
-                    NOT_A_TIMESTAMP /* timestamp */, 0 /* level */, 0 /* count */, &shortcuts);
+                    false /* isBlacklisted */, MAX_PROBABILITY /* probability */, HistoricalInfo());
             if (!addUnigramEntry(prevWordsInfo->getNthPrevWordCodePoints(1 /* n */),
                     &beginningOfSentenceUnigramProperty)) {
                 AKLOGE("Cannot add unigram entry for the beginning-of-sentence.");
@@ -414,6 +439,32 @@
     }
 }
 
+
+bool Ver4PatriciaTriePolicy::updateCounter(const PrevWordsInfo *const prevWordsInfo,
+        const CodePointArrayView wordCodePoints, const bool isValidWord,
+        const HistoricalInfo historicalInfo) {
+    if (!mBuffers->isUpdatable()) {
+        AKLOGI("Warning: updateCounter() is called for non-updatable dictionary.");
+        return false;
+    }
+    const int probability = isValidWord ? DUMMY_PROBABILITY_FOR_VALID_WORDS : NOT_A_PROBABILITY;
+    const UnigramProperty unigramProperty(false /* representsBeginningOfSentence */,
+            false /* isNotAWord */, false /*isBlacklisted*/, probability, historicalInfo);
+    if (!addUnigramEntry(wordCodePoints, &unigramProperty)) {
+        AKLOGE("Cannot update unigarm entry in updateCounter().");
+        return false;
+    }
+    const int probabilityForNgram = prevWordsInfo->isNthPrevWordBeginningOfSentence(1 /* n */)
+            ? NOT_A_PROBABILITY : probability;
+    const NgramProperty ngramProperty(wordCodePoints.toVector(), probabilityForNgram,
+            historicalInfo);
+    if (!addNgramEntry(prevWordsInfo, &ngramProperty)) {
+        AKLOGE("Cannot update unigarm entry in updateCounter().");
+        return false;
+    }
+    return true;
+}
+
 bool Ver4PatriciaTriePolicy::flush(const char *const filePath) {
     if (!mBuffers->isUpdatable()) {
         AKLOGI("Warning: flush() is called for non-updatable dictionary. filePath: %s", filePath);
@@ -528,8 +579,7 @@
                     bigramEntry.getProbability();
             ngrams.emplace_back(
                     CodePointArrayView(bigramWord1CodePoints, codePointCount).toVector(),
-                    probability, historicalInfo->getTimeStamp(), historicalInfo->getLevel(),
-                    historicalInfo->getCount());
+                    probability, *historicalInfo);
         }
     }
     // Fetch shortcut information.
@@ -552,8 +602,7 @@
     }
     const UnigramProperty unigramProperty(ptNodeParams.representsBeginningOfSentence(),
             ptNodeParams.isNotAWord(), ptNodeParams.isBlacklisted(), ptNodeParams.getProbability(),
-            historicalInfo->getTimeStamp(), historicalInfo->getLevel(),
-            historicalInfo->getCount(), &shortcuts);
+            *historicalInfo, std::move(shortcuts));
     return WordProperty(wordCodePoints.toVector(), &unigramProperty, &ngrams);
 }
 
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.h
index 324a53e..b82563e 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.h
@@ -118,6 +118,10 @@
     bool removeNgramEntry(const PrevWordsInfo *const prevWordsInfo,
             const CodePointArrayView wordCodePoints);
 
+    bool updateCounter(const PrevWordsInfo *const prevWordsInfo,
+            const CodePointArrayView wordCodePoints, const bool isValidWord,
+            const HistoricalInfo historicalInfo);
+
     bool flush(const char *const filePath);
 
     bool flushWithGC(const char *const filePath);
@@ -147,6 +151,7 @@
     // prevent the dictionary from overflowing.
     static const int MARGIN_TO_REFUSE_DYNAMIC_OPERATIONS;
     static const int MIN_DICT_SIZE_TO_REFUSE_DYNAMIC_OPERATIONS;
+    static const int DUMMY_PROBABILITY_FOR_VALID_WORDS;
 
     const Ver4DictBuffers::Ver4DictBuffersPtr mBuffers;
     const HeaderPolicy *const mHeaderPolicy;
@@ -169,6 +174,8 @@
     int getTerminalPtNodePosFromWordId(const int wordId) const;
     const WordAttributes getWordAttributes(const int probability,
             const PtNodeParams &ptNodeParams) const;
+    int getBigramConditionalProbability(const int prevWordUnigramProbability,
+            const int bigramProbability) const;
 };
 } // namespace v402
 } // namespace backward
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_writing_helper.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_writing_helper.cpp
index 3fb4caa..2887dc6 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_writing_helper.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_writing_helper.cpp
@@ -216,7 +216,7 @@
                         probabilityEntry.getHistoricalInfo(), mBuffers->getHeaderPolicy()) :
                 probabilityEntry.getProbability();
         priorityQueue.push(DictProbability(terminalPos, probability,
-                probabilityEntry.getHistoricalInfo()->getTimeStamp()));
+                probabilityEntry.getHistoricalInfo()->getTimestamp()));
     }
 
     // Delete unigrams.
@@ -263,7 +263,7 @@
                             bigramEntry.getHistoricalInfo(), mBuffers->getHeaderPolicy()) :
                     bigramEntry.getProbability();
             priorityQueue.push(DictProbability(entryPos, probability,
-                    bigramEntry.getHistoricalInfo()->getTimeStamp()));
+                    bigramEntry.getHistoricalInfo()->getTimestamp()));
         }
     }
 
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp
index 49095d0..d3d684b 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp
@@ -452,7 +452,7 @@
             const int probability = getProbability(word1Probability, bigramsIt.getProbability());
             ngrams.emplace_back(
                     CodePointArrayView(bigramWord1CodePoints, word1CodePointCount).toVector(),
-                    probability, NOT_A_TIMESTAMP /* timestamp */, 0 /* level */, 0 /* count */);
+                    probability, HistoricalInfo());
         }
     }
     // Fetch shortcut information.
@@ -477,7 +477,7 @@
     }
     const UnigramProperty unigramProperty(ptNodeParams.representsBeginningOfSentence(),
             ptNodeParams.isNotAWord(), ptNodeParams.isBlacklisted(), ptNodeParams.getProbability(),
-            NOT_A_TIMESTAMP /* timestamp */, 0 /* level */, 0 /* count */, &shortcuts);
+            HistoricalInfo(), std::move(shortcuts));
     return WordProperty(wordCodePoints.toVector(), &unigramProperty, &ngrams);
 }
 
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h
index a912d03..32a95bb 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h
@@ -107,6 +107,14 @@
         return false;
     }
 
+    bool updateCounter(const PrevWordsInfo *const prevWordsInfo,
+            const CodePointArrayView wordCodePoints, const bool isValidWord,
+            const HistoricalInfo historicalInfo) {
+        // This method should not be called for non-updatable dictionary.
+        AKLOGI("Warning: updateCounter() is called for non-updatable dictionary.");
+        return false;
+    }
+
     bool flush(const char *const filePath) {
         // This method should not be called for non-updatable dictionary.
         AKLOGI("Warning: flush() is called for non-updatable dictionary.");
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.cpp
index 1392302..956dabb 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.cpp
@@ -296,7 +296,7 @@
                 ForgettingCurveUtils::decodeProbability(probabilityEntry.getHistoricalInfo(),
                         headerPolicy) : probabilityEntry.getProbability();
         outEntryInfo->emplace_back(probability,
-                probabilityEntry.getHistoricalInfo()->getTimeStamp(),
+                probabilityEntry.getHistoricalInfo()->getTimestamp(),
                 entry.key(), targetLevel, prevWordIds->data());
     }
     return true;
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h
index fd52e57..fa14156 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h
@@ -21,10 +21,10 @@
 #include <cstdint>
 
 #include "defines.h"
+#include "suggest/core/dictionary/property/historical_info.h"
 #include "suggest/core/dictionary/property/ngram_property.h"
 #include "suggest/core/dictionary/property/unigram_property.h"
 #include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h"
-#include "suggest/policyimpl/dictionary/utils/historical_info.h"
 
 namespace latinime {
 
@@ -53,15 +53,13 @@
                     unigramProperty->isNotAWord(), unigramProperty->isBlacklisted(),
                     unigramProperty->isPossiblyOffensive())),
               mProbability(unigramProperty->getProbability()),
-              mHistoricalInfo(unigramProperty->getTimestamp(), unigramProperty->getLevel(),
-                      unigramProperty->getCount()) {}
+              mHistoricalInfo(unigramProperty->getHistoricalInfo()) {}
 
     // Create from ngram property.
     // TODO: Set flags.
     ProbabilityEntry(const NgramProperty *const ngramProperty)
             : mFlags(0), mProbability(ngramProperty->getProbability()),
-              mHistoricalInfo(ngramProperty->getTimestamp(), ngramProperty->getLevel(),
-                      ngramProperty->getCount()) {}
+              mHistoricalInfo(ngramProperty->getHistoricalInfo()) {}
 
     bool isValid() const {
         return (mFlags & Ver4DictConstants::FLAG_NOT_A_VALID_ENTRY) == 0;
@@ -103,7 +101,7 @@
         uint64_t encodedEntry = static_cast<uint64_t>(mFlags);
         if (hasHistoricalInfo) {
             encodedEntry = (encodedEntry << (Ver4DictConstants::TIME_STAMP_FIELD_SIZE * CHAR_BIT))
-                    ^ static_cast<uint64_t>(mHistoricalInfo.getTimeStamp());
+                    ^ static_cast<uint64_t>(mHistoricalInfo.getTimestamp());
             encodedEntry = (encodedEntry << (Ver4DictConstants::WORD_LEVEL_FIELD_SIZE * CHAR_BIT))
                     ^ static_cast<uint64_t>(mHistoricalInfo.getLevel());
             encodedEntry = (encodedEntry << (Ver4DictConstants::WORD_COUNT_FIELD_SIZE * CHAR_BIT))
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp
index d46acf5..036197c 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp
@@ -43,6 +43,7 @@
 const int Ver4PatriciaTriePolicy::MARGIN_TO_REFUSE_DYNAMIC_OPERATIONS = 1024;
 const int Ver4PatriciaTriePolicy::MIN_DICT_SIZE_TO_REFUSE_DYNAMIC_OPERATIONS =
         Ver4DictConstants::MAX_DICTIONARY_SIZE - MARGIN_TO_REFUSE_DYNAMIC_OPERATIONS;
+const int Ver4PatriciaTriePolicy::DUMMY_PROBABILITY_FOR_VALID_WORDS = 1;
 
 void Ver4PatriciaTriePolicy::createAndGetAllChildDicNodes(const DicNode *const dicNode,
         DicNodeVector *const childDicNodes) const {
@@ -298,11 +299,9 @@
         if (!prevWordsInfo->isNthPrevWordBeginningOfSentence(i + 1 /* n */)) {
             return false;
         }
-        const std::vector<UnigramProperty::ShortcutProperty> shortcuts;
         const UnigramProperty beginningOfSentenceUnigramProperty(
                 true /* representsBeginningOfSentence */, true /* isNotAWord */,
-                false /* isBlacklisted */, MAX_PROBABILITY /* probability */,
-                NOT_A_TIMESTAMP /* timestamp */, 0 /* level */, 0 /* count */, &shortcuts);
+                false /* isBlacklisted */, MAX_PROBABILITY /* probability */, HistoricalInfo());
         if (!addUnigramEntry(prevWordsInfo->getNthPrevWordCodePoints(1 /* n */),
                 &beginningOfSentenceUnigramProperty)) {
             AKLOGE("Cannot add unigram entry for the beginning-of-sentence.");
@@ -364,6 +363,35 @@
     }
 }
 
+bool Ver4PatriciaTriePolicy::updateCounter(const PrevWordsInfo *const prevWordsInfo,
+        const CodePointArrayView wordCodePoints, const bool isValidWord,
+        const HistoricalInfo historicalInfo) {
+    if (!mBuffers->isUpdatable()) {
+        AKLOGI("Warning: updateCounter() is called for non-updatable dictionary.");
+        return false;
+    }
+    // TODO: Have count up method in language model dict content.
+    const int probability = isValidWord ? DUMMY_PROBABILITY_FOR_VALID_WORDS : NOT_A_PROBABILITY;
+    const UnigramProperty unigramProperty(false /* representsBeginningOfSentence */,
+            false /* isNotAWord */, false /*isBlacklisted*/, probability, historicalInfo);
+    if (!addUnigramEntry(wordCodePoints, &unigramProperty)) {
+        AKLOGE("Cannot update unigarm entry in updateCounter().");
+        return false;
+    }
+    const int probabilityForNgram = prevWordsInfo->isNthPrevWordBeginningOfSentence(1 /* n */)
+            ? NOT_A_PROBABILITY : probability;
+    const NgramProperty ngramProperty(wordCodePoints.toVector(), probabilityForNgram,
+            historicalInfo);
+    for (size_t i = 1; i <= prevWordsInfo->getPrevWordCount(); ++i) {
+        const PrevWordsInfo trimmedPrevWordsInfo(prevWordsInfo->getTrimmedPrevWordsInfo(i));
+        if (!addNgramEntry(&trimmedPrevWordsInfo, &ngramProperty)) {
+            AKLOGE("Cannot update ngram entry in updateCounter().");
+            return false;
+        }
+    }
+    return true;
+}
+
 bool Ver4PatriciaTriePolicy::flush(const char *const filePath) {
     if (!mBuffers->isUpdatable()) {
         AKLOGI("Warning: flush() is called for non-updatable dictionary. filePath: %s", filePath);
@@ -464,8 +492,7 @@
                 ForgettingCurveUtils::decodeProbability(historicalInfo, mHeaderPolicy) :
                 probabilityEntry.getProbability();
         ngrams.emplace_back(CodePointArrayView(bigramWord1CodePoints, codePointCount).toVector(),
-                probability, historicalInfo->getTimeStamp(), historicalInfo->getLevel(),
-                historicalInfo->getCount());
+                probability, *historicalInfo);
     }
     // Fetch shortcut information.
     std::vector<UnigramProperty::ShortcutProperty> shortcuts;
@@ -487,8 +514,7 @@
     }
     const UnigramProperty unigramProperty(probabilityEntry.representsBeginningOfSentence(),
             probabilityEntry.isNotAWord(), probabilityEntry.isBlacklisted(),
-            probabilityEntry.getProbability(), historicalInfo->getTimeStamp(),
-            historicalInfo->getLevel(), historicalInfo->getCount(), &shortcuts);
+            probabilityEntry.getProbability(), *historicalInfo, std::move(shortcuts));
     return WordProperty(wordCodePoints.toVector(), &unigramProperty, &ngrams);
 }
 
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h
index 598122b..662bb8d 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h
@@ -98,6 +98,10 @@
     bool removeNgramEntry(const PrevWordsInfo *const prevWordsInfo,
             const CodePointArrayView wordCodePoints);
 
+    bool updateCounter(const PrevWordsInfo *const prevWordsInfo,
+            const CodePointArrayView wordCodePoints, const bool isValidWord,
+            const HistoricalInfo historicalInfo);
+
     bool flush(const char *const filePath);
 
     bool flushWithGC(const char *const filePath);
@@ -127,6 +131,8 @@
     // prevent the dictionary from overflowing.
     static const int MARGIN_TO_REFUSE_DYNAMIC_OPERATIONS;
     static const int MIN_DICT_SIZE_TO_REFUSE_DYNAMIC_OPERATIONS;
+    // TODO: Remove
+    static const int DUMMY_PROBABILITY_FOR_VALID_WORDS;
 
     const Ver4DictBuffers::Ver4DictBuffersPtr mBuffers;
     const HeaderPolicy *const mHeaderPolicy;
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.cpp
index fed0ae7..e5ef2ab 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.cpp
@@ -29,10 +29,14 @@
 const int ForgettingCurveUtils::MULTIPLIER_TWO_IN_PROBABILITY_SCALE = 8;
 const int ForgettingCurveUtils::DECAY_INTERVAL_SECONDS = 2 * 60 * 60;
 
-const int ForgettingCurveUtils::MAX_LEVEL = 3;
-const int ForgettingCurveUtils::MIN_VISIBLE_LEVEL = 1;
-const int ForgettingCurveUtils::MAX_ELAPSED_TIME_STEP_COUNT = 15;
-const int ForgettingCurveUtils::DISCARD_LEVEL_ZERO_ENTRY_TIME_STEP_COUNT_THRESHOLD = 14;
+const int ForgettingCurveUtils::MAX_LEVEL = 15;
+const int ForgettingCurveUtils::MIN_VISIBLE_LEVEL = 2;
+const int ForgettingCurveUtils::MAX_ELAPSED_TIME_STEP_COUNT = 31;
+const int ForgettingCurveUtils::DISCARD_LEVEL_ZERO_ENTRY_TIME_STEP_COUNT_THRESHOLD = 30;
+const int ForgettingCurveUtils::OCCURRENCES_TO_RAISE_THE_LEVEL = 1;
+// TODO: Evaluate whether this should be 7.5 days.
+// 15 days
+const int ForgettingCurveUtils::DURATION_TO_LOWER_THE_LEVEL_IN_SECONDS = 15 * 24 * 60 * 60;
 
 const float ForgettingCurveUtils::UNIGRAM_COUNT_HARD_LIMIT_WEIGHT = 1.2;
 const float ForgettingCurveUtils::BIGRAM_COUNT_HARD_LIMIT_WEIGHT = 1.2;
@@ -43,7 +47,7 @@
 /* static */ const HistoricalInfo ForgettingCurveUtils::createUpdatedHistoricalInfo(
         const HistoricalInfo *const originalHistoricalInfo, const int newProbability,
         const HistoricalInfo *const newHistoricalInfo, const HeaderPolicy *const headerPolicy) {
-    const int timestamp = newHistoricalInfo->getTimeStamp();
+    const int timestamp = newHistoricalInfo->getTimestamp();
     if (newProbability != NOT_A_PROBABILITY && originalHistoricalInfo->getLevel() == 0) {
         // Add entry as a valid word.
         const int level = clampToVisibleEntryLevelRange(newHistoricalInfo->getLevel());
@@ -54,19 +58,23 @@
             || (originalHistoricalInfo->getLevel() == newHistoricalInfo->getLevel()
                     && originalHistoricalInfo->getCount() < newHistoricalInfo->getCount())) {
         // Initial information.
+        int count = newHistoricalInfo->getCount();
+        if (count >= OCCURRENCES_TO_RAISE_THE_LEVEL) {
+            const int level = clampToValidLevelRange(newHistoricalInfo->getLevel() + 1);
+            return HistoricalInfo(timestamp, level, 0 /* count */);
+        }
         const int level = clampToValidLevelRange(newHistoricalInfo->getLevel());
-        const int count = clampToValidCountRange(newHistoricalInfo->getCount(), headerPolicy);
-        return HistoricalInfo(timestamp, level, count);
+        return HistoricalInfo(timestamp, level, clampToValidCountRange(count, headerPolicy));
     } else {
         const int updatedCount = originalHistoricalInfo->getCount() + 1;
-        if (updatedCount >= headerPolicy->getForgettingCurveOccurrencesToLevelUp()) {
+        if (updatedCount >= OCCURRENCES_TO_RAISE_THE_LEVEL) {
             // The count exceeds the max value the level can be incremented.
             if (originalHistoricalInfo->getLevel() >= MAX_LEVEL) {
                 // The level is already max.
                 return HistoricalInfo(timestamp,
                         originalHistoricalInfo->getLevel(), originalHistoricalInfo->getCount());
             } else {
-                // Level up.
+                // Raise the level.
                 return HistoricalInfo(timestamp,
                         originalHistoricalInfo->getLevel() + 1, 0 /* count */);
             }
@@ -78,53 +86,40 @@
 
 /* static */ int ForgettingCurveUtils::decodeProbability(
         const HistoricalInfo *const historicalInfo, const HeaderPolicy *const headerPolicy) {
-    const int elapsedTimeStepCount = getElapsedTimeStepCount(historicalInfo->getTimeStamp(),
-            headerPolicy->getForgettingCurveDurationToLevelDown());
+    const int elapsedTimeStepCount = getElapsedTimeStepCount(historicalInfo->getTimestamp(),
+            DURATION_TO_LOWER_THE_LEVEL_IN_SECONDS);
     return sProbabilityTable.getProbability(
             headerPolicy->getForgettingCurveProbabilityValuesTableId(),
             clampToValidLevelRange(historicalInfo->getLevel()),
             clampToValidTimeStepCountRange(elapsedTimeStepCount));
 }
 
-/* static */ int ForgettingCurveUtils::getProbability(const int unigramProbability,
-        const int bigramProbability) {
-    if (unigramProbability == NOT_A_PROBABILITY) {
-        return NOT_A_PROBABILITY;
-    } else if (bigramProbability == NOT_A_PROBABILITY) {
-        return std::min(backoff(unigramProbability), MAX_PROBABILITY);
-    } else {
-        // TODO: Investigate better way to handle bigram probability.
-        return std::min(std::max(unigramProbability,
-                bigramProbability + MULTIPLIER_TWO_IN_PROBABILITY_SCALE), MAX_PROBABILITY);
-    }
-}
-
 /* static */ bool ForgettingCurveUtils::needsToKeep(const HistoricalInfo *const historicalInfo,
         const HeaderPolicy *const headerPolicy) {
     return historicalInfo->getLevel() > 0
-            || getElapsedTimeStepCount(historicalInfo->getTimeStamp(),
-                    headerPolicy->getForgettingCurveDurationToLevelDown())
+            || getElapsedTimeStepCount(historicalInfo->getTimestamp(),
+                    DURATION_TO_LOWER_THE_LEVEL_IN_SECONDS)
                             < DISCARD_LEVEL_ZERO_ENTRY_TIME_STEP_COUNT_THRESHOLD;
 }
 
 /* static */ const HistoricalInfo ForgettingCurveUtils::createHistoricalInfoToSave(
         const HistoricalInfo *const originalHistoricalInfo,
         const HeaderPolicy *const headerPolicy) {
-    if (originalHistoricalInfo->getTimeStamp() == NOT_A_TIMESTAMP) {
+    if (originalHistoricalInfo->getTimestamp() == NOT_A_TIMESTAMP) {
         return HistoricalInfo();
     }
-    const int durationToLevelDownInSeconds = headerPolicy->getForgettingCurveDurationToLevelDown();
+    const int durationToLevelDownInSeconds = DURATION_TO_LOWER_THE_LEVEL_IN_SECONDS;
     const int elapsedTimeStep = getElapsedTimeStepCount(
-            originalHistoricalInfo->getTimeStamp(), durationToLevelDownInSeconds);
+            originalHistoricalInfo->getTimestamp(), durationToLevelDownInSeconds);
     if (elapsedTimeStep <= MAX_ELAPSED_TIME_STEP_COUNT) {
         // No need to update historical info.
         return *originalHistoricalInfo;
     }
-    // Level down.
+    // Lower the level.
     const int maxLevelDownAmonut = elapsedTimeStep / (MAX_ELAPSED_TIME_STEP_COUNT + 1);
     const int levelDownAmount = (maxLevelDownAmonut >= originalHistoricalInfo->getLevel()) ?
             originalHistoricalInfo->getLevel() : maxLevelDownAmonut;
-    const int adjustedTimestampInSeconds = originalHistoricalInfo->getTimeStamp() +
+    const int adjustedTimestampInSeconds = originalHistoricalInfo->getTimestamp() +
             levelDownAmount * durationToLevelDownInSeconds;
     return HistoricalInfo(adjustedTimestampInSeconds,
             originalHistoricalInfo->getLevel() - levelDownAmount, 0 /* count */);
@@ -170,7 +165,7 @@
 
 /* static */ int ForgettingCurveUtils::clampToValidCountRange(const int count,
         const HeaderPolicy *const headerPolicy) {
-    return std::min(std::max(count, 0), headerPolicy->getForgettingCurveOccurrencesToLevelUp() - 1);
+    return std::min(std::max(count, 0), OCCURRENCES_TO_RAISE_THE_LEVEL - 1);
 }
 
 /* static */ int ForgettingCurveUtils::clampToValidLevelRange(const int level) {
@@ -187,9 +182,9 @@
 const int ForgettingCurveUtils::ProbabilityTable::STRONG_PROBABILITY_TABLE_ID = 2;
 const int ForgettingCurveUtils::ProbabilityTable::AGGRESSIVE_PROBABILITY_TABLE_ID = 3;
 const int ForgettingCurveUtils::ProbabilityTable::WEAK_MAX_PROBABILITY = 127;
-const int ForgettingCurveUtils::ProbabilityTable::MODEST_BASE_PROBABILITY = 32;
-const int ForgettingCurveUtils::ProbabilityTable::STRONG_BASE_PROBABILITY = 35;
-const int ForgettingCurveUtils::ProbabilityTable::AGGRESSIVE_BASE_PROBABILITY = 40;
+const int ForgettingCurveUtils::ProbabilityTable::MODEST_BASE_PROBABILITY = 8;
+const int ForgettingCurveUtils::ProbabilityTable::STRONG_BASE_PROBABILITY = 9;
+const int ForgettingCurveUtils::ProbabilityTable::AGGRESSIVE_BASE_PROBABILITY = 10;
 
 
 ForgettingCurveUtils::ProbabilityTable::ProbabilityTable() : mTables() {
@@ -202,7 +197,7 @@
             const float endProbability = getBaseProbabilityForLevel(tableId, level - 1);
             for (int timeStepCount = 0; timeStepCount <= MAX_ELAPSED_TIME_STEP_COUNT;
                     ++timeStepCount) {
-                if (level == 0) {
+                if (level < MIN_VISIBLE_LEVEL) {
                     mTables[tableId][level][timeStepCount] = NOT_A_PROBABILITY;
                     continue;
                 }
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h b/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h
index 313eb6b..ccbc4a9 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h
@@ -20,7 +20,7 @@
 #include <vector>
 
 #include "defines.h"
-#include "suggest/policyimpl/dictionary/utils/historical_info.h"
+#include "suggest/core/dictionary/property/historical_info.h"
 
 namespace latinime {
 
@@ -39,9 +39,6 @@
     static int decodeProbability(const HistoricalInfo *const historicalInfo,
             const HeaderPolicy *const headerPolicy);
 
-    static int getProbability(const int encodedUnigramProbability,
-            const int encodedBigramProbability);
-
     static bool needsToKeep(const HistoricalInfo *const historicalInfo,
             const HeaderPolicy *const headerPolicy);
 
@@ -101,6 +98,8 @@
     static const int MIN_VISIBLE_LEVEL;
     static const int MAX_ELAPSED_TIME_STEP_COUNT;
     static const int DISCARD_LEVEL_ZERO_ENTRY_TIME_STEP_COUNT_THRESHOLD;
+    static const int OCCURRENCES_TO_RAISE_THE_LEVEL;
+    static const int DURATION_TO_LOWER_THE_LEVEL_IN_SECONDS;
 
     static const float UNIGRAM_COUNT_HARD_LIMIT_WEIGHT;
     static const float BIGRAM_COUNT_HARD_LIMIT_WEIGHT;
diff --git a/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_test.cpp b/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_test.cpp
index daa32c3..4469dc7 100644
--- a/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_test.cpp
+++ b/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_test.cpp
@@ -60,7 +60,7 @@
     languageModelDictContent.setProbabilityEntry(wordId, &probabilityEntry);
     const ProbabilityEntry entry = languageModelDictContent.getProbabilityEntry(wordId);
     EXPECT_EQ(flag, entry.getFlags());
-    EXPECT_EQ(timestamp, entry.getHistoricalInfo()->getTimeStamp());
+    EXPECT_EQ(timestamp, entry.getHistoricalInfo()->getTimestamp());
     EXPECT_EQ(level, entry.getHistoricalInfo()->getLevel());
     EXPECT_EQ(count, entry.getHistoricalInfo()->getCount());
 
diff --git a/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/probability_entry_test.cpp b/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/probability_entry_test.cpp
index f0494f3..260b347 100644
--- a/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/probability_entry_test.cpp
+++ b/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/probability_entry_test.cpp
@@ -51,7 +51,7 @@
             ProbabilityEntry::decode(encodedEntry, true /* hasHistoricalInfo */);
 
     EXPECT_EQ(flag, decodedEntry.getFlags());
-    EXPECT_EQ(timestamp, decodedEntry.getHistoricalInfo()->getTimeStamp());
+    EXPECT_EQ(timestamp, decodedEntry.getHistoricalInfo()->getTimestamp());
     EXPECT_EQ(level, decodedEntry.getHistoricalInfo()->getLevel());
     EXPECT_EQ(count, decodedEntry.getHistoricalInfo()->getCount());
 }
diff --git a/tests/src/com/android/inputmethod/compat/SuggestionSpanUtilsTest.java b/tests/src/com/android/inputmethod/compat/SuggestionSpanUtilsTest.java
new file mode 100644
index 0000000..1c320db
--- /dev/null
+++ b/tests/src/com/android/inputmethod/compat/SuggestionSpanUtilsTest.java
@@ -0,0 +1,207 @@
+/*
+ * 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.compat;
+
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.text.Spanned;
+import android.text.TextUtils;
+import android.text.style.SuggestionSpan;
+
+import com.android.inputmethod.latin.SuggestedWords;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Locale;
+
+@SmallTest
+public class SuggestionSpanUtilsTest extends AndroidTestCase {
+
+    /**
+     * Helper method to create a dummy {@link SuggestedWordInfo}.
+     *
+     * @param kindAndFlags the kind and flags to be used to create {@link SuggestedWordInfo}.
+     * @param word the word to be used to create {@link SuggestedWordInfo}.
+     * @return a new instance of {@link SuggestedWordInfo}.
+     */
+    private static SuggestedWordInfo createWordInfo(final String word, final int kindAndFlags) {
+        return new SuggestedWordInfo(word, 1 /* score */, kindAndFlags, null /* sourceDict */,
+                SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
+                SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */);
+    }
+
+    private static void assertNotSuggestionSpan(final String expectedText,
+            final CharSequence actualText) {
+        assertTrue(TextUtils.equals(expectedText, actualText));
+        if (!(actualText instanceof Spanned)) {
+            return;
+        }
+        final Spanned spanned = (Spanned)actualText;
+        final SuggestionSpan[] suggestionSpans = spanned.getSpans(0, spanned.length(),
+                SuggestionSpan.class);
+        assertEquals(0, suggestionSpans.length);
+    }
+
+    private static void assertSuggestionSpan(final String expectedText,
+            final int reuiredSuggestionSpanFlags, final int requiredSpanFlags,
+            final String[] expectedSuggestions,
+            final CharSequence actualText) {
+        assertTrue(TextUtils.equals(expectedText, actualText));
+        assertTrue(actualText instanceof Spanned);
+        final Spanned spanned = (Spanned)actualText;
+        final SuggestionSpan[] suggestionSpans = spanned.getSpans(0, spanned.length(),
+                SuggestionSpan.class);
+        assertEquals(1, suggestionSpans.length);
+        final SuggestionSpan suggestionSpan = suggestionSpans[0];
+        if (reuiredSuggestionSpanFlags != 0) {
+            assertTrue((suggestionSpan.getFlags() & reuiredSuggestionSpanFlags) != 0);
+        }
+        if (requiredSpanFlags != 0) {
+            assertTrue((spanned.getSpanFlags(suggestionSpan) & requiredSpanFlags) != 0);
+        }
+        if (expectedSuggestions != null) {
+            final String[] actualSuggestions = suggestionSpan.getSuggestions();
+            assertEquals(expectedSuggestions.length, actualSuggestions.length);
+            for (int i = 0; i < expectedSuggestions.length; ++i) {
+                assertEquals(expectedSuggestions[i], actualSuggestions[i]);
+            }
+        }
+    }
+
+    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)
+    public void testGetTextWithAutoCorrectionIndicatorUnderline() {
+        final String ORIGINAL_TEXT = "Hey!";
+        final CharSequence text = SuggestionSpanUtils.getTextWithAutoCorrectionIndicatorUnderline(
+                getContext(), ORIGINAL_TEXT);
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
+            assertNotSuggestionSpan(ORIGINAL_TEXT, text);
+            return;
+        }
+
+        assertSuggestionSpan(ORIGINAL_TEXT,
+                SuggestionSpan.FLAG_AUTO_CORRECTION /* reuiredSuggestionSpanFlags */,
+                Spanned.SPAN_COMPOSING | Spanned.SPAN_EXCLUSIVE_EXCLUSIVE /* requiredSpanFlags */,
+                new String[]{}, text);
+    }
+
+    public void testGetTextWithSuggestionSpan() {
+        final SuggestedWordInfo predicition1 =
+                createWordInfo("Quality", SuggestedWordInfo.KIND_PREDICTION);
+        final SuggestedWordInfo predicition2 =
+                createWordInfo("Speed", SuggestedWordInfo.KIND_PREDICTION);
+        final SuggestedWordInfo predicition3 =
+                createWordInfo("Price", SuggestedWordInfo.KIND_PREDICTION);
+
+        final SuggestedWordInfo typed =
+                createWordInfo("Hey", SuggestedWordInfo.KIND_TYPED);
+
+        final SuggestedWordInfo[] corrections =
+                new SuggestedWordInfo[SuggestionSpan.SUGGESTIONS_MAX_SIZE * 2];
+        for (int i = 0; i < corrections.length; ++i) {
+            corrections[i] = createWordInfo("correction" + i, SuggestedWordInfo.KIND_CORRECTION);
+        }
+
+        // SuggestionSpan will not be attached when {@link SuggestedWords#INPUT_STYLE_PREDICTION}
+        // is specified.
+        {
+            final SuggestedWords predictedWords = new SuggestedWords(
+                    new ArrayList<>(Arrays.asList(predicition1, predicition2, predicition3)),
+                    null /* rawSuggestions */,
+                    false /* typedWordValid */,
+                    false /* willAutoCorrect */,
+                    false /* isObsoleteSuggestions */,
+                    SuggestedWords.INPUT_STYLE_PREDICTION);
+            final String PICKED_WORD = predicition2.mWord;
+            assertNotSuggestionSpan(
+                    PICKED_WORD,
+                    SuggestionSpanUtils.getTextWithSuggestionSpan(getContext(), PICKED_WORD,
+                            predictedWords));
+        }
+
+        final ArrayList<SuggestedWordInfo> suggestedWordList = new ArrayList<>();
+        suggestedWordList.add(typed);
+        suggestedWordList.add(predicition1);
+        suggestedWordList.add(predicition2);
+        suggestedWordList.add(predicition3);
+        suggestedWordList.addAll(Arrays.asList(corrections));
+        final SuggestedWords typedAndCollectedWords = new SuggestedWords(
+                suggestedWordList,
+                null /* rawSuggestions */,
+                false /* typedWordValid */,
+                false /* willAutoCorrect */,
+                false /* isObsoleteSuggestions */,
+                SuggestedWords.INPUT_STYLE_TYPING);
+
+        for (final SuggestedWordInfo pickedWord : suggestedWordList) {
+            final String PICKED_WORD = pickedWord.mWord;
+
+            final ArrayList<String> expectedSuggestions = new ArrayList<>();
+            for (SuggestedWordInfo suggestedWordInfo : suggestedWordList) {
+                if (expectedSuggestions.size() >= SuggestionSpan.SUGGESTIONS_MAX_SIZE) {
+                    break;
+                }
+                if (suggestedWordInfo.isKindOf(SuggestedWordInfo.KIND_PREDICTION)) {
+                    // Currently predictions are not filled into SuggestionSpan.
+                    continue;
+                }
+                final String suggestedWord = suggestedWordInfo.mWord;
+                if (TextUtils.equals(PICKED_WORD, suggestedWord)) {
+                    // Typed word itself is not added to SuggestionSpan.
+                    continue;
+                }
+                expectedSuggestions.add(suggestedWord);
+            }
+
+            assertSuggestionSpan(
+                    PICKED_WORD,
+                    0 /* reuiredSuggestionSpanFlags */,
+                    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE /* requiredSpanFlags */,
+                    expectedSuggestions.toArray(new String[expectedSuggestions.size()]),
+                    SuggestionSpanUtils.getTextWithSuggestionSpan(getContext(), PICKED_WORD,
+                            typedAndCollectedWords));
+        }
+    }
+
+    public void testFindFirstLocaleFromSuggestionSpans() {
+        final String[] suggestions = new String[] {"Quality", "Speed", "Price"};
+        final SuggestionSpan nullLocaleSpan = new SuggestionSpan((Locale)null, suggestions, 0);
+        final SuggestionSpan emptyLocaleSpan = new SuggestionSpan(new Locale(""), suggestions, 0);
+        final SuggestionSpan enUsLocaleSpan = new SuggestionSpan(Locale.US, suggestions, 0);
+        final SuggestionSpan jaJpLocaleSpan = new SuggestionSpan(Locale.JAPAN, suggestions, 0);
+
+        assertEquals(null, SuggestionSpanUtils.findFirstLocaleFromSuggestionSpans(
+                new SuggestionSpan[] {}));
+
+        assertEquals(null, SuggestionSpanUtils.findFirstLocaleFromSuggestionSpans(
+                new SuggestionSpan[] {emptyLocaleSpan}));
+
+        assertEquals(Locale.US, SuggestionSpanUtils.findFirstLocaleFromSuggestionSpans(
+                new SuggestionSpan[] {enUsLocaleSpan}));
+
+        assertEquals(Locale.US, SuggestionSpanUtils.findFirstLocaleFromSuggestionSpans(
+                new SuggestionSpan[] {nullLocaleSpan, enUsLocaleSpan}));
+
+        assertEquals(Locale.US, SuggestionSpanUtils.findFirstLocaleFromSuggestionSpans(
+                new SuggestionSpan[] {nullLocaleSpan, emptyLocaleSpan, enUsLocaleSpan}));
+
+        assertEquals(Locale.JAPAN, SuggestionSpanUtils.findFirstLocaleFromSuggestionSpans(
+                new SuggestionSpan[] {nullLocaleSpan, jaJpLocaleSpan, enUsLocaleSpan}));
+    }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/action/KlpActionLabelTests.java b/tests/src/com/android/inputmethod/keyboard/action/KlpActionLabelTests.java
index 74343cb..07c604e 100644
--- a/tests/src/com/android/inputmethod/keyboard/action/KlpActionLabelTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/action/KlpActionLabelTests.java
@@ -131,7 +131,7 @@
                 textsSet.getText("label_done_key"));
         final ExpectedActionKey previousKey = ExpectedActionKey.newLabelKey(
                 textsSet.getText("label_previous_key"));
-        final String tag = "label=hi_ZZ system=" + systemLocale
+        final String tag = "label=" + subtype.getLocale() + " system=" + systemLocale
                 + " " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype);
         final RunInLocale<Void> job = new RunInLocale<Void>() {
             @Override
@@ -147,26 +147,34 @@
     public void testHinglishActionLabel() {
         final RichInputMethodManager richImm = RichInputMethodManager.getInstance();
         final Locale hi_ZZ = new Locale("hi", "ZZ");
-        final InputMethodSubtype hinglish = richImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+        final InputMethodSubtype hiLatn = richImm.findSubtypeByLocaleAndKeyboardLayoutSet(
                 hi_ZZ.toString(), SubtypeLocaleUtils.QWERTY);
+        // This is a preliminary subtype and may not exist.
+        if (hiLatn == null) {
+            return;
+        }
         // An action label should be displayed in subtype's locale regardless of the system locale.
-        doTestActionKeysInLocaleWithKeyboardTextsSet(hinglish, hi_ZZ, hi_ZZ);
-        doTestActionKeysInLocaleWithKeyboardTextsSet(hinglish, hi_ZZ, Locale.US);
-        doTestActionKeysInLocaleWithKeyboardTextsSet(hinglish, hi_ZZ, Locale.FRENCH);
-        doTestActionKeysInLocaleWithKeyboardTextsSet(hinglish, hi_ZZ, Locale.ITALIAN);
-        doTestActionKeysInLocaleWithKeyboardTextsSet(hinglish, hi_ZZ, Locale.JAPANESE);
+        doTestActionKeysInLocaleWithKeyboardTextsSet(hiLatn, hi_ZZ, new Locale("hi"));
+        doTestActionKeysInLocaleWithKeyboardTextsSet(hiLatn, hi_ZZ, Locale.US);
+        doTestActionKeysInLocaleWithKeyboardTextsSet(hiLatn, hi_ZZ, Locale.FRENCH);
+        doTestActionKeysInLocaleWithKeyboardTextsSet(hiLatn, hi_ZZ, Locale.ITALIAN);
+        doTestActionKeysInLocaleWithKeyboardTextsSet(hiLatn, hi_ZZ, Locale.JAPANESE);
     }
 
     public void testSerbianLatinActionLabel() {
         final RichInputMethodManager richImm = RichInputMethodManager.getInstance();
         final Locale sr_ZZ = new Locale("sr", "ZZ");
-        final InputMethodSubtype hinglish = richImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+        final InputMethodSubtype srLatn = richImm.findSubtypeByLocaleAndKeyboardLayoutSet(
                 sr_ZZ.toString(), "serbian_qwertz");
+        // This is a preliminary subtype and may not exist.
+        if (srLatn == null) {
+            return;
+        }
         // An action label should be displayed in subtype's locale regardless of the system locale.
-        doTestActionKeysInLocaleWithKeyboardTextsSet(hinglish, sr_ZZ, sr_ZZ);
-        doTestActionKeysInLocaleWithKeyboardTextsSet(hinglish, sr_ZZ, Locale.US);
-        doTestActionKeysInLocaleWithKeyboardTextsSet(hinglish, sr_ZZ, Locale.FRENCH);
-        doTestActionKeysInLocaleWithKeyboardTextsSet(hinglish, sr_ZZ, Locale.ITALIAN);
-        doTestActionKeysInLocaleWithKeyboardTextsSet(hinglish, sr_ZZ, Locale.JAPANESE);
+        doTestActionKeysInLocaleWithKeyboardTextsSet(srLatn, sr_ZZ, new Locale("sr"));
+        doTestActionKeysInLocaleWithKeyboardTextsSet(srLatn, sr_ZZ, Locale.US);
+        doTestActionKeysInLocaleWithKeyboardTextsSet(srLatn, sr_ZZ, Locale.FRENCH);
+        doTestActionKeysInLocaleWithKeyboardTextsSet(srLatn, sr_ZZ, Locale.ITALIAN);
+        doTestActionKeysInLocaleWithKeyboardTextsSet(srLatn, sr_ZZ, Locale.JAPANESE);
     }
 }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Arabic.java b/tests/src/com/android/inputmethod/keyboard/layout/Arabic.java
index 3f85e4b..9c2efe2 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/Arabic.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Arabic.java
@@ -18,6 +18,7 @@
 
 import com.android.inputmethod.keyboard.layout.Symbols.RtlSymbols;
 import com.android.inputmethod.keyboard.layout.SymbolsShifted.RtlSymbolsShifted;
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 import com.android.inputmethod.latin.Constants;
@@ -27,17 +28,15 @@
 public final class Arabic extends LayoutBase {
     private static final String LAYOUT_NAME = "arabic";
 
-    public Arabic(final LayoutCustomizer customizer) {
-        super(customizer, ArabicSymbols.class, ArabicSymbolsShifted.class);
+    public Arabic(final Locale locale) {
+        super(new ArabicCustomizer(locale), ArabicSymbols.class, ArabicSymbolsShifted.class);
     }
 
     @Override
     public String getName() { return LAYOUT_NAME; }
 
-    public static class ArabicCustomizer extends LayoutCustomizer {
-        public ArabicCustomizer(final Locale locale) {
-            super(locale);
-        }
+    private static class ArabicCustomizer extends LayoutCustomizer {
+        ArabicCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKey getAlphabetKey() { return ARABIC_ALPHABET_KEY; }
@@ -141,12 +140,11 @@
     ExpectedKey[][] getCommonAlphabetLayout(final boolean isPhone) {
         if (isPhone) {
             return ALPHABET_COMMON;
-        } else {
-            final ExpectedKeyboardBuilder builder = new ExpectedKeyboardBuilder(ALPHABET_COMMON);
-            // U+0626: "ئ" ARABIC LETTER YEH WITH HAMZA ABOVE
-            builder.insertKeysAtRow(3, 2, "\u0626");
-            return builder.build();
         }
+        final ExpectedKeyboardBuilder builder = new ExpectedKeyboardBuilder(ALPHABET_COMMON);
+        // U+0626: "ئ" ARABIC LETTER YEH WITH HAMZA ABOVE
+        builder.insertKeysAtRow(3, 2, "\u0626");
+        return builder.build();
     }
 
     @Override
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/ArmenianPhonetic.java b/tests/src/com/android/inputmethod/keyboard/layout/ArmenianPhonetic.java
index 2cecedc..261618f 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/ArmenianPhonetic.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/ArmenianPhonetic.java
@@ -16,6 +16,7 @@
 
 package com.android.inputmethod.keyboard.layout;
 
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 import com.android.inputmethod.latin.Constants;
@@ -28,15 +29,16 @@
 public final class ArmenianPhonetic extends LayoutBase {
     private static final String LAYOUT_NAME = "armenian_phonetic";
 
-    public ArmenianPhonetic(final LayoutCustomizer customizer) {
-        super(customizer, ArmenianSymbols.class, ArmenianSymbolsShifted.class);
+    public ArmenianPhonetic(final Locale locale) {
+        super(new ArmenianPhoneticCustomizer(locale), ArmenianSymbols.class,
+                ArmenianSymbolsShifted.class);
     }
 
     @Override
     public String getName() { return LAYOUT_NAME; }
 
-    public static class ArmenianPhoneticCustomizer extends LayoutCustomizer {
-        public ArmenianPhoneticCustomizer(final Locale locale) { super(locale); }
+    private static class ArmenianPhoneticCustomizer extends LayoutCustomizer {
+        ArmenianPhoneticCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public int getNumberOfRows() { return 5; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Azerty.java b/tests/src/com/android/inputmethod/keyboard/layout/Azerty.java
index a094963..f3176d0 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/Azerty.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Azerty.java
@@ -17,6 +17,7 @@
 package com.android.inputmethod.keyboard.layout;
 
 import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Bengali.java b/tests/src/com/android/inputmethod/keyboard/layout/Bengali.java
index 2101ddf..339cab4 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/Bengali.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Bengali.java
@@ -16,11 +16,9 @@
 
 package com.android.inputmethod.keyboard.layout;
 
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
-import com.android.inputmethod.latin.Constants;
-
-import java.util.Locale;
 
 /**
  * The Bengali keyboard.
@@ -35,32 +33,6 @@
     @Override
     public String getName() { return LAYOUT_NAME; }
 
-    public static class BengaliCustomizer extends LayoutCustomizer {
-        public BengaliCustomizer(final Locale locale) { super(locale); }
-
-        @Override
-        public ExpectedKey getAlphabetKey() { return BENGALI_ALPHABET_KEY; }
-
-        @Override
-        public ExpectedKey[] getOtherCurrencyKeys() {
-            return SymbolsShifted.CURRENCIES_OTHER_GENERIC;
-        }
-
-        @Override
-        public ExpectedKey[] getLeftShiftKeys(final boolean isPhone) { return EMPTY_KEYS; }
-
-        @Override
-        public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
-            return isPhone ? EMPTY_KEYS : EXCLAMATION_AND_QUESTION_MARKS;
-        }
-
-        // U+0995: "क" BENGALI LETTER KA
-        // U+0996: "ख" BENGALI LETTER KHA
-        // U+0997: "ग" BENGALI LETTER GA
-        private static final ExpectedKey BENGALI_ALPHABET_KEY = key(
-                "\u0995\u0996\u0997", Constants.CODE_SWITCH_ALPHA_SYMBOL);
-    }
-
     @Override
     ExpectedKey[][] getCommonAlphabetLayout(boolean isPhone) { return ALPHABET_COMMON; }
 
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/BengaliAkkhor.java b/tests/src/com/android/inputmethod/keyboard/layout/BengaliAkkhor.java
new file mode 100644
index 0000000..bb1dc10
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/BengaliAkkhor.java
@@ -0,0 +1,497 @@
+/*
+ * 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.layout;
+
+import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+/**
+ * The Bengali Akkhor keyboard.
+ */
+public final class BengaliAkkhor extends LayoutBase {
+    private static final String LAYOUT_NAME = "bengali_akkhor";
+
+    public BengaliAkkhor(final LayoutCustomizer customizer) {
+        super(customizer, Symbols.class, SymbolsShifted.class);
+    }
+
+    @Override
+    public String getName() { return LAYOUT_NAME; }
+
+    @Override
+    ExpectedKey[][] getCommonAlphabetLayout(boolean isPhone) { return ALPHABET_COMMON; }
+
+    @Override
+    ExpectedKey[][] getCommonAlphabetShiftLayout(boolean isPhone, final int elementId) {
+        if (elementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) {
+            return getCommonAlphabetLayout(isPhone);
+        }
+        return ALPHABET_SHIFTED_COMMON;
+    }
+
+    private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+            .setKeysOfRow(1,
+                    // U+09A7: "ধ" BENGALI LETTER DHA
+                    // U+09E7: "১" BENGALI DIGIT ONE
+                    // U+09A7/U+09CD/U+09AC:
+                    //     "ধ্ব্র" BENGALI LETTER DHA/BENGALI SIGN VIRAMA/BENGALI LETTER BA
+                    // U+09A7/U+09CD/U+09AF:
+                    //     "ধ্য্র" BENGALI LETTER DHA/BENGALI SIGN VIRAMA/BENGALI LETTER YA
+                    // U+09A7/U+09CD/U+09B0:
+                    //     "ধ্র" BENGALI LETTER DHA/BENGALI SIGN VIRAMA/BENGALI LETTER RA
+                    key("\u09A7", joinMoreKeys("\u09E7", "\u09A7\u09CD\u09AC", "\u09A7\u09CD\u09AF",
+                            "\u09A7\u09CD\u09B0")),
+                    // U+09A5: "থ" BENGALI LETTER THA
+                    // U+09E8: "২" BENGALI DIGIT TWO
+                    // U+09A5/U+09CD/U+09AF:
+                    //     "থ্য" BENGALI LETTER THA/BENGALI SIGN VIRAMA/BENGALI LETTER YA
+                    // U+09A5/U+09CD/U+09B0:
+                    //     "থ্র" BENGALI LETTER THA/BENGALI SIGN VIRAMA/BENGALI LETTER RA
+                    key("\u09A5", joinMoreKeys("\u09E8", "\u09A5\u09CD\u09AF",
+                            "\u09A5\u09CD\u09B0")),
+                    // U+09C7: "ে" BENGALI VOWEL SIGN E
+                    // U+09E9: "৩" BENGALI DIGIT THREE
+                    // U+098F: "এ" BENGALI LETTER E
+                    key("\u09C7", joinMoreKeys("\u09E9", "\u098F")),
+                    // U+09B0: "র" BENGALI LETTER RA
+                    // U+09EA: "৪" BENGALI DIGIT FOUR
+                    key("\u09B0", joinMoreKeys("\u09EA")),
+                    // U+09A4: "ত" BENGALI LETTER TA
+                    // U+09EB: "৫" BENGALI DIGIT FIVE
+                    // U+09CE: "ৎ" BENGALI LETTER KHANDA TA
+                    // U+09A4/U+09CD/U+09A4:
+                    //     "ত্ত" BENGALI LETTER TA/BENGALI SIGN VIRAMA/BENGALI LETTER TA
+                    // U+09A4/U+09CD/U+09A8:
+                    //     "ত্ন" BENGALI LETTER TA/BENGALI SIGN VIRAMA/BENGALI LETTER NA
+                    // U+09A4/U+09CD/U+09AC:
+                    //     "ত্ব" BENGALI LETTER TA/BENGALI SIGN VIRAMA/BENGALI LETTER BA
+                    // U+09A4/U+09CD/U+09AE:
+                    //     "ত্ম" BENGALI LETTER TA/BENGALI SIGN VIRAMA/BENGALI LETTER MA
+                    key("\u09A4", joinMoreKeys("\u09EB", "\u09CE", "\u09A4\u09CD\u09A4",
+                            "\u09A4\u09CD\u09A8", "\u09A4\u09CD\u09AC", "\u09A4\u09CD\u09AE")),
+                    // U+09DF: "য়" BENGALI LETTER YYA
+                    // U+09EC: "৬" BENGALI DIGIT SIX
+                    key("\u09DF", joinMoreKeys("\u09EC")),
+                    // U+09C1: "ু" BENGALI VOWEL SIGN U
+                    // U+09ED: "৭" BENGALI DIGIT SEVEN
+                    // U+0989: "উ" BENGALI LETTER U
+                    key("\u09C1", joinMoreKeys("\u09ED", "\u0989")),
+                    // U+09BF: "ি" BENGALI VOWEL SIGN I
+                    // U+09EE: "৮" BENGALI DIGIT EIGHT
+                    // U+0987: "ই BENGALI LETTER I
+                    key("\u09Bf", joinMoreKeys("\u09EE", "\u0987")),
+                    // U+09CB: "ো" BENGALI VOWEL SIGN O
+                    // U+09EF: "৯" BENGALI DIGIT NINE
+                    // U+0993: "ও" BENGALI LETTER O
+                    key("\u09CB", joinMoreKeys("\u09EF", "\u0993")),
+                    // U+09AA: "প" BENGALI LETTER PA
+                    // U+09E6: "০" BENGALI DIGIT ZERO
+                    // U+09AA/U+09CD/U+09A4:
+                    //     "প্ত" BENGALI LETTER PA/BENGALI SIGN VIRAMA/BENGALI LETTER TA
+                    // U+09AA/U+09CD/U+09A8:
+                    //     "প্ন" BENGALI LETTER PA/BENGALI SIGN VIRAMA/BENGALI LETTER NA
+                    // U+09AA/U+09CD/U+09AA:
+                    //     "প্প" BENGALI LETTER PA/BENGALI SIGN VIRAMA/BENGALI LETTER PA
+                    // U+09AA/U+09CD/U+09AF:
+                    //     "প্য" BENGALI LETTER PA/BENGALI SIGN VIRAMA/BENGALI LETTER YA
+                    // U+09AA/U+09CD/U+09B0:
+                    //     "প্র" BENGALI LETTER PA/BENGALI SIGN VIRAMA/BENGALI LETTER RA
+                    // U+09AA/U+09CD/U+09B2:
+                    //     "প্ল" BENGALI LETTER PA/BENGALI SIGN VIRAMA/BENGALI LETTER LA
+                    // U+09AA/U+09CD/U+09B8:
+                    //     "প্স" BENGALI LETTER PA/BENGALI SIGN VIRAMA/BENGALI LETTER SA
+                    key("\u09AA", joinMoreKeys("\u09E6", "\u09AA\u09CD\u09A4", "\u09AA\u09CD\u09A8",
+                            "\u09AA\u09CD\u09AA", "\u09AA\u09CD\u09AF", "\u09AA\u09CD\u09B0",
+                            "\u09AA\u09CD\u09B2", "\u09AA\u09CD\u09B8")),
+                    // U+0986: "আ" BENGALI LETTER AA
+                    key("\u0986"))
+            .setKeysOfRow(2,
+                    // U+09BE: "া BENGALI VOWEL SIGN AA
+                    // U+0986: "আ" BENGALI LETTER AA
+                    key("\u09BE", moreKey("\u0986")),
+                    // U+09B8: "স" BENGALI LETTER SA
+                    // U+09B8/U+09CD/U+09AC:
+                    //     "স্ব" BENGALI LETTER SA/BENGALI SIGN VIRAMA/BENGALI LETTER BA
+                    // U+09B8/U+09CD/U+09A4:
+                    //     "স্ত" BENGALI LETTER SA/BENGALI SIGN VIRAMA/BENGALI LETTER TA
+                    // U+09B8/U+09CD/U+099F:
+                    //     "স্ট" BENGALI LETTER SA/BENGALI SIGN VIRAMA/BENGALI LETTER TTA
+                    // U+09B8/U+09CD/U+0995:
+                    //     "স্ক" BENGALI LETTER SA/BENGALI SIGN VIRAMA/BENGALI LETTER KA
+                    // U+09B8/U+09CD/U+09AA:
+                    //     "স্প" BENGALI LETTER SA/BENGALI SIGN VIRAMA/BENGALI LETTER PA
+                    key("\u09B8", joinMoreKeys("\u09B8\u09CD\u09AC", "\u09B8\u09CD\u09A4",
+                            "\u09B8\u09CD\u099F", "\u09B8\u09CD\u0995", "\u09B8\u09CD\u09AA")),
+                    // U+09A6: "দ" BENGALI LETTER DA
+                    // U+09A6/U+09CD/U+09A6:
+                    //     "দ্দ" BENGALI LETTER DA/BENGALI SIGN VIRAMA/BENGALI LETTER DA
+                    // U+09A6/U+09CD/U+09A7:
+                    //     "দ্ধ" BENGALI LETTER DA/BENGALI SIGN VIRAMA/BENGALI LETTER DHA
+                    // U+09A6/U+09CD/U+09AC:
+                    //     "দ্ব" BENGALI LETTER DA/BENGALI SIGN VIRAMA/BENGALI LETTER BA
+                    // U+09A6/U+09CD/U+09AD:
+                    //     "দ্ভ" BENGALI LETTER DA/BENGALI SIGN VIRAMA/BENGALI LETTER BHA
+                    // U+09A6/U+09CD/U+09AE:
+                    //     "দ্ম" BENGALI LETTER DA/BENGALI SIGN VIRAMA/BENGALI LETTER MA
+                    // U+09A6/U+09CD/U+09AF:
+                    //     "দ্য" BENGALI LETTER DA/BENGALI SIGN VIRAMA/BENGALI LETTER YA
+                    // U+09A6/U+09CD/U+09B0:
+                    //     "দ্র" BENGALI LETTER DA/BENGALI SIGN VIRAMA/BENGALI LETTER RA
+                    key("\u09A6", joinMoreKeys("\u09A6\u09CD\u09A6", "\u09A6\u09CD\u09A7",
+                            "\u09A6\u09CD\u09AC", "\u09A6\u09CD\u09AD", "\u09A6\u09CD\u09AE",
+                            "\u09A6\u09CD\u09AF", "\u09A6\u09CD\u09B0")),
+                    // U+09C3: "ৃ" BENGALI VOWEL SIGN VOCALIC R
+                    // U+098B: "ঋ" BENGALI LETTER VOCALIC R
+                    key("\u09C3", moreKey("\u098B")),
+                    // U+0997: "গ" BENGALI LETTER GA
+                    // U+0997/U+09CD/U+09A7:
+                    //     "গ্ধ" BENGALI LETTER GA/BENGALI SIGN VIRAMA/BENGALI LETTER DH A
+                    // U+0997/U+09CD/U+09B0:
+                    //     "গ্র" BENGALI LETTER GA/BENGALI SIGN VIRAMA/BENGALI LETTER RA
+                    // U+0997/U+09CD/U+09B2:
+                    //     "গ্ল" BENGALI LETTER GA/BENGALI SIGN VIRAMA/BENGALI LETTER LA
+                    // U+0997/U+09CD/U+09A8:
+                    //     "গ্ন" BENGALI LETTER GA/BENGALI SIGN VIRAMA/BENGALI LETTER NA
+                    key("\u0997", joinMoreKeys("\u0997\u09CD\u09A7", "\u0997\u09CD\u09B0",
+                            "\u0997\u09CD\u09B2", "\u0997\u09CD\u09A8")),
+                    // U+09CD: "্" BENGALI SIGN VIRAMA
+                    key("\u09CD"),
+                    // U+099C: "জ" BENGALI LETTER JA
+                    // U+099C/U+09CD/U+099E:
+                    //     "জ্ঞ" BENGALI LETTER JA/BENGALI SIGN VIRAMA/BENGALI LETTER NYA
+                    // U+099C/U+09CD/U+099C:
+                    //     "জ্জ" BENGALI LETTER JA/BENGALI SIGN VIRAMA/BENGALI LETTER JA
+                    // U+099C/U+09CD/U+09AF:
+                    //     "জ্ব" BENGALI LETTER JA/BENGALI SIGN VIRAMA/BENGALI LETTER YA
+                    // U+099C/U+09CD/U+09AC:
+                    //     "জ্য" BENGALI LETTER JA/BENGALI SIGN VIRAMA/BENGALI LETTER BA
+                    // U+099C/U+09CD/U+09B0:
+                    //     "জ্র" BENGALI LETTER JA/BENGALI SIGN VIRAMA/BENGALI LETTER RA
+                    key("\u099C", joinMoreKeys("\u099C\u09CD\u099E", "\u099C\u09CD\u099C",
+                            "\u099C\u09CD\u09AF", "\u099C\u09CD\u09AC", "\u099C\u09CD\u09B0")),
+                    // U+0995: "ক" BENGALI LETTER KA
+                    // U+0995/U+09CD/U+09B7:
+                    //     "ক্ষ" BENGALI LETTER KA/BENGALI SIGN VIRAMA/BENGALI LETTER SSA
+                    // U+0995/U+09CD/U+0995:
+                    //     "ক্ক" BENGALI LETTER KA/BENGALI SIGN VIRAMA/BENGALI LETTER KA
+                    // U+0995/U+09CD/U+099F:
+                    //     "ক্ট" BENGALI LETTER KA/BENGALI SIGN VIRAMA/BENGALI LETTER TTA
+                    // U+0995/U+09CD/U+09A4:
+                    //     "ক্ত" BENGALI LETTER KA/BENGALI SIGN VIRAMA/BENGALI LETTER TA
+                    // U+0995/U+09CD/U+09B0:
+                    //     "ক্র" BENGALI LETTER KA/BENGALI SIGN VIRAMA/BENGALI LETTER RA
+                    // U+0995/U+09CD/U+09B8:
+                    //     "ক্স" BENGALI LETTER KA/BENGALI SIGN VIRAMA/BENGALI LETTER SA
+                    // U+0995/U+09CD/U+09B2:
+                    //     "ক্ল" BENGALI LETTER KA/BENGALI SIGN VIRAMA/BENGALI LETTER LA
+                    key("\u0995", joinMoreKeys("\u0995\u09CD\u09B7", "\u0995\u09CD\u0995",
+                            "\u0995\u09CD\u099F", "\u0995\u09CD\u09A4", "\u0995\u09CD\u09B0",
+                            "\u0995\u09CD\u09B8", "\u0995\u09CD\u09B2")),
+                    // U+09B2: "ল" BENGALI LETTER LA
+                    // U+09B2/U+09CD/U+0995:
+                    //     "ল্ক" BENGALI LETTER LA/BENGALI SIGN VIRAMA/BENGALI LETTER KA
+                    // U+09B2/U+09CD/U+0997:
+                    //     "ল্গ" BENGALI LETTER LA/BENGALI SIGN VIRAMA/BENGALI LETTER GA
+                    // U+09B2/U+09CD/U+099F:
+                    //     "ল্ট" BENGALI LETTER LA/BENGALI SIGN VIRAMA/BENGALI LETTER TTA
+                    // U+09B2/U+09CD/U+09A1:
+                    //     "ল্ড" BENGALI LETTER LA/BENGALI SIGN VIRAMA/BENGALI LETTER DDA
+                    // U+09B2/U+09CD/U+09A4:
+                    //     "ল্ত" BENGALI LETTER LA/BENGALI SIGN VIRAMA/BENGALI LETTER TA
+                    // U+09B2/U+09CD/U+09A6:
+                    //     "ল্দ" BENGALI LETTER LA/BENGALI SIGN VIRAMA/BENGALI LETTER DA
+                    // U+09B2/U+09CD/U+09A7:
+                    //     "ল্ধ" BENGALI LETTER LA/BENGALI SIGN VIRAMA/BENGALI LETTER DHA
+                    // U+09B2/U+09CD/U+09AA:
+                    //     "ল্প" BENGALI LETTER LA/BENGALI SIGN VIRAMA/BENGALI LETTER PA
+                    // U+09B2/U+09CD/U+09AB:
+                    //     "ল্ফ" BENGALI LETTER LA/BENGALI SIGN VIRAMA/BENGALI LETTER PHA
+                    // U+09B2/U+09CD/U+09AC:
+                    //     "ল্ব" BENGALI LETTER LA/BENGALI SIGN VIRAMA/BENGALI LETTER BA
+                    // U+09B2/U+09CD/U+09AE:
+                    //     "ল্ম" BENGALI LETTER LA/BENGALI SIGN VIRAMA/BENGALI LETTER MA
+                    // U+09B2/U+09CD/U+09B2:
+                    //     "ল্ল" BENGALI LETTER LA/BENGALI SIGN VIRAMA/BENGALI LETTER LA
+                    key("\u09B2", joinMoreKeys("\u09B2\u09CD\u0995", "\u09B2\u09CD\u0997",
+                            "\u09B2\u09CD\u099F", "\u09B2\u09CD\u09A1", "\u09B2\u09CD\u09A4",
+                            "\u09B2\u09CD\u09A6", "\u09B2\u09CD\u09A7", "\u09B2\u09CD\u09AA",
+                            "\u09B2\u09CD\u09AB", "\u09B2\u09CD\u09AC", "\u09B2\u09CD\u09AE",
+                            "\u09B2\u09CD\u09B2")),
+                    // U+0987: "ই" BENGALI LETTER I
+                    key("\u0987"),
+                    // U+0989: "উ" BENGALI LETTER U
+                    key("\u0989"))
+            .setKeysOfRow(3,
+                    // U+09AF: "য" BENGALI LETTER YA
+                    // U+09CD/U+09AF: "্য" BENGALI SIGN VIRAMA/BENGALI LETTER YA
+                    key("\u09AF", moreKey("\u09CD\u09AF")),
+                    // U+09B7: "ষ" BENGALI LETTER SSA
+                    // U+09B7/U+09CD/U+0995:
+                    //     "ষ্ক" BENGALI LETTER SSA/BENGALI SIGN VIRAMA/BENGALI LETTER KA
+                    // U+09B7/U+09CD/U+099F:
+                    //     "ষ্ট" BENGALI LETTER SSA/BENGALI SIGN VIRAMA/BENGALI LETTER TTA
+                    // U+09B7/U+09CD/U+09A0:
+                    //     "ষ্ঠ" BENGALI LETTER SSA/BENGALI SIGN VIRAMA/BENGALI LETTER TTHA
+                    // U+09B7/U+09CD/U+09A3:
+                    //     "ষ্ণ" BENGALI LETTER SSA/BENGALI SIGN VIRAMA/BENGALI LETTER NNA
+                    // U+09B7/U+09CD/U+09AA:
+                    //     "ষ্প" BENGALI LETTER SSA/BENGALI SIGN VIRAMA/BENGALI LETTER PA
+                    // U+09B7/U+09CD/U+09AB:
+                    //     "ষ্ফ" BENGALI LETTER SSA/BENGALI SIGN VIRAMA/BENGALI LETTER PHA
+                    // U+09B7/U+09CD/U+09AE:
+                    //     "ষ্ম" BENGALI LETTER SSA/BENGALI SIGN VIRAMA/BENGALI LETTER MA
+                    key("\u09B7", joinMoreKeys("\u09B7\u09CD\u0995", "\u09B7\u09CD\u099F",
+                            "\u09B7\u09CD\u09A0", "\u09B7\u09CD\u09A3", "\u09B7\u09CD\u09AA",
+                            "\u09B7\u09CD\u09AB", "\u09B7\u09CD\u09AE")),
+                    // U+099A: "চ" BENGALI LETTER CA
+                    // U+099A/U+09CD/U+099A:
+                    //     "চ্চ" BENGALI LETTER CA/BENGALI SIGN VIRAMA/BENGALI LETTER CA
+                    // U+099A/U+09CD/U+099B:
+                    //     "চ্ছ" BENGALI LETTER CA/BENGALI SIGN VIRAMA/BENGALI LETTER CHA
+                    key("\u099A", joinMoreKeys("\u099A\u09CD\u099A", "\u099A\u09CD\u099B")),
+                    // U+09AD: "ভ" BENGALI LETTER BHA
+                    // U+09AD/U+09CD/U+09AF:
+                    //     "ভ্" BENGALI LETTER BHA/BENGALI SIGN VIRAMA/BENGALI LETTER YA
+                    // U+09AD/U+09CD/U+09B0:
+                    //     "ভ্র" BENGALI LETTER BHA/BENGALI SIGN VIRAMA/BENGALI LETTER RA
+                    // U+09AD/U+09CD/U+09B2:
+                    //     "ভ্ল" BENGALI LETTER BHA/BENGALI SIGN VIRAMA/BENGALI LETTER LA
+                    key("\u09AD", joinMoreKeys("\u09AD\u09CD\u09AF", "\u09AD\u09CD\u09B0",
+                            "\u09AD\u09CD\u09B2")),
+                    // U+09AC: "ব" BENGALI LETTER BA
+                    // U+09CD/U+09AC: "্ব" BENGALI SIGN VIRAMA/BENGALI LETTER BA
+                    // U+09AC/U+09CD/U+09B0:
+                    //     "ব্র" BENGALI LETTER BA/BENGALI SIGN VIRAMA/BENGALI LETTER RA
+                    // U+09AC/U+09CD/U+099C:
+                    //     "ব্জ" BENGALI LETTER BA/BENGALI SIGN VIRAMA/BENGALI LETTER JA
+                    // U+09AC/U+09CD/U+09A6:
+                    //     "ব্দ" BENGALI LETTER BA/BENGALI SIGN VIRAMA/BENGALI LETTER DA
+                    // U+09AC/U+09CD/U+09A7:
+                    //     "ব্ধ" BENGALI LETTER BA/BENGALI SIGN VIRAMA/BENGALI LETTER DHA
+                    // U+09AC/U+09CD/U+09AC:
+                    //     "ব্ব" BENGALI LETTER BA/BENGALI SIGN VIRAMA/BENGALI LETTER BA
+                    // U+09AC/U+09CD/U+09B2:
+                    //     "ব্ল" BENGALI LETTER BA/BENGALI SIGN VIRAMA/BENGALI LETTER LA
+                    // U+09F1: "ৱ" BENGALI LETTER RA WITH MIDDLE DIAGONAL
+                    // U+09F0: "ৰ" BENGALI LETTER RA WITH LOWER DIAGONAL
+                    key("\u09AC", joinMoreKeys("\u09CD\u09AC", "\u09AC\u09CD\u09B0",
+                            "\u09AC\u09CD\u099C", "\u09AC\u09CD\u09A6", "\u09AC\u09CD\u09A7",
+                            "\u09AC\u09CD\u09AC", "\u09AC\u09CD\u09B2", "\u09F1", "\u09F0")),
+                    // U+09A8: "ন" BENGALI LETTER NA
+                    // U+09A8/U+09CD/U+09A4:
+                    //     "ন্ত" BENGALI LETTER NA/BENGALI SIGN VIRAMA/BENGALI LETTER TA
+                    // U+09A8/U+09CD/U+09A5:
+                    //     "ন্থ" BENGALI LETTER NA/BENGALI SIGN VIRAMA/BENGALI LETTER THA
+                    // U+09A8/U+09CD/U+099F:
+                    //     "ন্ট" BENGALI LETTER NA/BENGALI SIGN VIRAMA/BENGALI LETTER TTA
+                    // U+09A8/U+09CD/U+09A6:
+                    //     "ন্দ" BENGALI LETTER NA/BENGALI SIGN VIRAMA/BENGALI LETTER DA
+                    // U+09A8/U+09CD/U+09A7:
+                    //     "ন্ধ" BENGALI LETTER NA/BENGALI SIGN VIRAMA/BENGALI LETTER DHA
+                    // U+09A8/U+09CD/U+09A1:
+                    //     "ন্ড" BENGALI LETTER NA/BENGALI SIGN VIRAMA/BENGALI LETTER DDA
+                    // U+09A8/U+09CD/U+09A8:
+                    //     "ন্ন" BENGALI LETTER NA/BENGALI SIGN VIRAMA/BENGALI LETTER NA
+                    // U+09A8/U+09CD/U+09AC:
+                    //     "ন্ব" BENGALI LETTER NA/BENGALI SIGN VIRAMA/BENGALI LETTER BA
+                    // U+09A8/U+09CD/U+09AE:
+                    //     "ন্ম" BENGALI LETTER NA/BENGALI SIGN VIRAMA/BENGALI LETTER MA
+                    // U+09A8/U+09CD/U+09B8:
+                    //     "ন্স" BENGALI LETTER NA/BENGALI SIGN VIRAMA/BENGALI LETTER SA
+                    key("\u09A8", joinMoreKeys("\u09A8\u09CD\u09A4", "\u09A8\u09CD\u09A5",
+                            "\u09A8\u09CD\u099F", "\u09A8\u09CD\u09A6", "\u09A8\u09CD\u09A7",
+                            "\u09A8\u09CD\u09A1", "\u09A8\u09CD\u09A8", "\u09A8\u09CD\u09AC",
+                            "\u09A8\u09CD\u09AE", "\u09A8\u09CD\u09B8")),
+                    // U+09AE: "ম" BENGALI LETTER MA
+                    // U+09AE/U+09CD/U+09A8:
+                    //     "ম্ন" BENGALI LETTER MA/BENGALI SIGN VIRAMA/BENGALI LETTER NA
+                    // U+09AE/U+09CD/U+09AA:
+                    //     "ম্প" BENGALI LETTER MA/BENGALI SIGN VIRAMA/BENGALI LETTER PA
+                    // U+09AE/U+09CD/U+09AC:
+                    //     "ম্ব" BENGALI LETTER MA/BENGALI SIGN VIRAMA/BENGALI LETTER BA
+                    // U+09AE/U+09CD/U+09AD:
+                    //     "ম্ভ" BENGALI LETTER MA/BENGALI SIGN VIRAMA/BENGALI LETTER BHA
+                    // U+09AE/U+09CD/U+09AE:
+                    //     "ম্ম" BENGALI LETTER MA/BENGALI SIGN VIRAMA/BENGALI LETTER MA
+                    // U+09AE/U+09CD/U+09B0:
+                    //     "ম্র" BENGALI LETTER MA/BENGALI SIGN VIRAMA/BENGALI LETTER RA
+                    // U+09AE/U+09CD/U+09B2:
+                    //     "ম্ল" BENGALI LETTER MA/BENGALI SIGN VIRAMA/BENGALI LETTER LA
+                    key("\u09AE", joinMoreKeys("\u09AE\u09CD\u09A8", "\u09AE\u09CD\u09AA",
+                            "\u09AE\u09CD\u09AC", "\u09AE\u09CD\u09AD", "\u09AE\u09CD\u09AE",
+                            "\u09AE\u09CD\u09B0", "\u09AE\u09CD\u09B2")),
+                    // U+098F: "এ" BENGALI LETTER E
+                    key("\u098F"),
+                    // U+0993: "ও" BENGALI LETTER O
+                    key("\u0993"))
+            .build();
+
+    private static final ExpectedKey[][] ALPHABET_SHIFTED_COMMON = new ExpectedKeyboardBuilder()
+            .setKeysOfRow(1,
+                    // U+09A2: "ঢ" BENGALI LETTER DDHA
+                    key("\u09A2"),
+                    // U+09A0: "ঠ" BENGALI LETTER TTHA
+                    key("\u09A0"),
+                    // U+09C8: "ৈ" BENGALI VOWEL SIGN AI
+                    // U+0990: "ঐ" BENGALI LETTER AI
+                    key("\u09C8", moreKey("\u0990")),
+                    // U+09DC: "ড়" BENGALI LETTER RRA
+                    // U+09BC: "়" BENGALI SIGN NUKTA
+                    key("\u09DC", moreKey("\u09BC")),
+                    // U+099F: "ট" BENGALI LETTER TTA
+                    // U+09F3: "৳" BENGALI RUPEE SIGN
+                    // U+099F/U+09CD/U+099F:
+                    //     "ট্ট" BENGALI LETTER TTA/BENGALI SIGN VIRAMA/BENGALI LETTER TTA
+                    // U+099F/U+09CD/U+09AC:
+                    //     "ট্ব" BENGALI LETTER TTA/BENGALI SIGN VIRAMA/BENGALI LETTER BA
+                    // U+099F/U+09CD/U+09AE:
+                    //     "ট্ম" BENGALI LETTER TTA/BENGALI SIGN VIRAMA/BENGALI LETTER MA
+                    key("\u099F", joinMoreKeys("\u09F3", "\u099F\u09CD\u099F", "\u099F\u09CD\u09AC",
+                            "\u099F\u09CD\u09AE")),
+                    // U+099E: "ঞ" BENGALI LETTER NYA
+                    // U+099E/U+09CD/U+099A:
+                    //     "ঞ্চ" BENGALI LETTER NYA/BENGALI SIGN VIRAMA/BENGALI LETTER CA
+                    // U+099E/U+09CD/U+099B:
+                    //     "ঞ্ছ" BENGALI LETTER NYA/BENGALI SIGN VIRAMA/BENGALI LETTER CHA
+                    // U+099E/U+09CD/U+099C:
+                    //     "ঞ্জ" BENGALI LETTER NYA/BENGALI SIGN VIRAMA/BENGALI LETTER JA
+                    key("\u099E", joinMoreKeys("\u099E\u09CD\u099A", "\u099E\u09CD\u099B",
+                            "\u099E\u09CD\u099C")),
+                    // U+09C2: "ূ" BENGALI VOWEL SIGN UU
+                    // U+098A: "ঊ" BENGALI LETTER UU
+                    key("\u09C2", moreKey("\u098A")),
+                    // U+09C0: "ী" BENGALI VOWEL SIGN II
+                    // U+0988: "ঈ" BENGALI LETTER II
+                    key("\u09C0", moreKey("\u0988")),
+                    // U+09CC: "ৌ" BENGALI VOWEL SIGN AU
+                    // U+099A: "ঔ" BENGALI LETTER CA
+                    // U+09D7: "ৗ" BENGALI AU LENGTH MARK
+                    key("\u09CC", joinMoreKeys("\u099A", "\u09D7")),
+                    // U+09AB: "ফ" BENGALI LETTER PHA
+                    // U+09AB/U+09CD/U+099F:
+                    //     "ফ্ট" BENGALI LETTER PHA/BENGALI SIGN VIRAMA/BENGALI LETTER TTA
+                    // U+09AB/U+09CD/U+09AF:
+                    //     "ফ্য" BENGALI LETTER PHA/BENGALI SIGN VIRAMA/BENGALI LETTER YA
+                    // U+09AB/U+09CD/U+09B0:
+                    //     "ফ্র" BENGALI LETTER PHA/BENGALI SIGN VIRAMA/BENGALI LETTER RA
+                    // U+09AB/U+09CD/U+09B2:
+                    //     "ফ্ল" BENGALI LETTER PHA/BENGALI SIGN VIRAMA/BENGALI LETTER LA
+                    key("\u09AB", joinMoreKeys("\u09AB\u09CD\u099F", "\u09AB\u09CD\u09AF",
+                            "\u09AB\u09CD\u09B0", "\u09AB\u09CD\u09B2")),
+                    // U+098B: "ঋ" BENGALI LETTER VOCALIC R
+                    // U+098C: "ঌ" BENGALI LETTER VOCALIC L
+                    // U+09E1: "ৡ" BENGALI LETTER VOCALIC LL
+                    // U+09F4: "৴" BENGALI CURRENCY NUMERATOR ONE
+                    // U+09F5: "৵" BENGALI CURRENCY NUMERATOR TWO
+                    // U+09F6: "৶" BENGALI CURRENCY NUMERATOR THREE
+                    // U+09E2: " ৢ" BENGALI VOWEL SIGN VOCALIC L
+                    // U+09E3: " ৣ" BENGALI VOWEL SIGN VOCALIC LL
+                    key("\u098B", joinMoreKeys("\u098C", "\u09E1", "\u09F4", "\u09F5", "\u09F6",
+                            "\u09E2", "\u09E3")))
+            .setKeysOfRow(2,
+                    // U+0985: "অ" BENGALI LETTER A
+                    key("\u0985"),
+                    // U+09B6: "শ" BENGALI LETTER SHA
+                    // U+09B6/U+09CD/U+099A:
+                    //     "শ্চ" BENGALI LETTER SHA/BENGALI SIGN VIRAMA/BENGALI LETTER CA
+                    // U+09B6/U+09CD/U+099B:
+                    //     "শ্ছ" BENGALI LETTER SHA/BENGALI SIGN VIRAMA/BENGALI LETTER CHA
+                    // U+09B6/U+09CD/U+09A4:
+                    //     "শ্ত" BENGALI LETTER SHA/BENGALI SIGN VIRAMA/BENGALI LETTER TA
+                    // U+09B6/U+09CD/U+09A8:
+                    //     "শ্ন" BENGALI LETTER SHA/BENGALI SIGN VIRAMA/BENGALI LETTER NA
+                    // U+09B6/U+09CD/U+09AC:
+                    //     "শ্ব" BENGALI LETTER SHA/BENGALI SIGN VIRAMA/BENGALI LETTER BA
+                    // U+09B6/U+09CD/U+09AE:
+                    //     "শ্ম" BENGALI LETTER SHA/BENGALI SIGN VIRAMA/BENGALI LETTER MA
+                    // U+09B6/U+09CD/U+09B0:
+                    //     "শ্র" BENGALI LETTER SHA/BENGALI SIGN VIRAMA/BENGALI LETTER RA
+                    // U+09B6/U+09CD/U+09B2:
+                    //     "শ্ল" BENGALI LETTER SHA/BENGALI SIGN VIRAMA/BENGALI LETTER LA
+                    key("\u09B6", joinMoreKeys("\u09B6\u09CD\u099A", "\u09B6\u09CD\u099B",
+                            "\u09B6\u09CD\u09A4", "\u09B6\u09CD\u09A8", "\u09B6\u09CD\u09AC",
+                            "\u09B6\u09CD\u09AE", "\u09B6\u09CD\u09B0", "\u09B6\u09CD\u09B2")),
+                    // U+09A1: "ড" BENGALI LETTER DDA
+                    // U+09A1/U+09CD/U+09A1:
+                    //     "ড্ড" BENGALI LETTER DDA/BENGALI SIGN VIRAMA/BENGALI LETTER DDA
+                    key("\u09A1", moreKey("\u09A1\u09CD\u09A1")),
+                    // U+09DD: "ঢ়" BENGALI LETTER RHA
+                    key("\u09DD"),
+                    // U+0998: "ঘ" BENGALI LETTER GHA
+                    key("\u0998"),
+                    // U+09B9: "হ" BENGALI LETTER HA
+                    // U+09BD: "ঽ" BENGALI SIGN AVAGRAHA
+                    // U+09B9/U+09CD/U+09A3:
+                    //     "হ্ণ" BENGALI LETTER HA/BENGALI SIGN VIRAMA/BENGALI LETTER NNA
+                    // U+09B9/U+09CD/U+09A8:
+                    //     "হ্ন" BENGALI LETTER HA/BENGALI SIGN VIRAMA/BENGALI LETTER NA
+                    // U+09B9/U+09CD/U+09AC:
+                    //     "হ্ব" BENGALI LETTER HA/BENGALI SIGN VIRAMA/BENGALI LETTER BA
+                    // U+09B9/U+09CD/U+09AE:
+                    //     "হ্ম" BENGALI LETTER HA/BENGALI SIGN VIRAMA/BENGALI LETTER MA
+                    // U+09B9/U+09CD/U+09B0:
+                    //     "হ্র" BENGALI LETTER HA/BENGALI SIGN VIRAMA/BENGALI LETTER RA
+                    // U+09B9/U+09CD/U+09B2:
+                    //     "হ্ল" BENGALI LETTER HA/BENGALI SIGN VIRAMA/BENGALI LETTER LA
+                    key("\u09B9", joinMoreKeys("\u09BD", "\u09B9\u09CD\u09A3", "\u09B9\u09CD\u09A8",
+                            "\u09B9\u09CD\u09AC", "\u09B9\u09CD\u09AE", "\u09B9\u09CD\u09B0",
+                            "\u09B9\u09CD\u09B2")),
+                    // U+099D: "ঝ" BENGALI LETTER JHA
+                    key("\u099D"),
+                    // U+0996: "খ" BENGALI LETTER KHA
+                    key("\u0996"),
+                    // U+09CE: "ৎ" BENGALI LETTER KHANDA TA
+                    key("\u09CE"),
+                    // U+0988: "ঈ" BENGALI LETTER II
+                    key("\u0988"),
+                    // U+098A: "ঊ" BENGALI LETTER UU
+                    key("\u098A"))
+            .setKeysOfRow(3,
+                    // U+0964: "।" DEVANAGARI DANDA
+                    // U+0965: "॥" DEVANAGARI DOUBLE DANDA
+                    key("\u0964", moreKey("\u0965")),
+                    // U+0999: "ঙ BENGALI LETTER NGA
+                    // U+0999/U+09CD/U+0995: "ঙ্ক"
+                    // U+0999/U+09CD/U+0996: "ঙ্খ"
+                    // U+0999/U+09CD/U+0997: "ঙ্গ"
+                    key("\u0999", joinMoreKeys("\u0999\u09CD\u0995", "\u0999\u09CD\u0996",
+                            "\u0999\u09CD\u0997")),
+                    // U+099B: "ছ" BENGALI LETTER CHA
+                    key("\u099B"),
+                    // U+0983: "ঃ" BENGALI SIGN VISARGA
+                    key("\u0983"),
+                    // U+0981: "ঁ" BENGALI SIGN CANDRABINDU
+                    key("\u0981"),
+                    // U+09A3: "ণ" BENGALI LETTER NNA
+                    // U+09A3/U+09CD/U+099F:
+                    //     "ণ্ট" BENGALI LETTER NNA/BENGALI SIGN VIRAMA/BENGALI LETTER TT/A
+                    // U+09A3/U+09CD/U+09A1:
+                    //     "ণ্ড" BENGALI LETTER NNA/BENGALI SIGN VIRAMA/BENGALI LETTER DDA
+                    // U+09A3/U+09CD/U+09A3:
+                    //     "ণ্ণ" BENGALI LETTER NNA/BENGALI SIGN VIRAMA/BENGALI LETTER NN
+                    key("\u09A3", joinMoreKeys("\u09A3\u09CD\u099F", "\u09A3\u09CD\u09A1",
+                            "\u09A3\u09CD\u09A3")),
+                    // U+0982: "ং" BENGALI SIGN ANUSVARA
+                    key("\u0982"),
+                    // U+0990: "ঐ" BENGALI LETTER AI
+                    key("\u0990"),
+                    // U+0994: "ঔ" BENGALI LETTER AU
+                    key("\u0994"))
+            .build();
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Bulgarian.java b/tests/src/com/android/inputmethod/keyboard/layout/Bulgarian.java
index 3282e44..bbe0384 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/Bulgarian.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Bulgarian.java
@@ -16,7 +16,8 @@
 
 package com.android.inputmethod.keyboard.layout;
 
-import com.android.inputmethod.keyboard.layout.EastSlavic.EastSlavicCustomizer;
+import com.android.inputmethod.keyboard.layout.customizer.EastSlavicCustomizer;
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
@@ -25,17 +26,17 @@
 public final class Bulgarian extends LayoutBase {
     private static final String LAYOUT_NAME = "bulgarian";
 
-    public Bulgarian(final LayoutCustomizer customizer) {
-        super(customizer, Symbols.class, SymbolsShifted.class);
+    public Bulgarian(final Locale locale) {
+        super(new BulgarianCustomizer(locale), Symbols.class, SymbolsShifted.class);
     }
 
     @Override
     public String getName() { return LAYOUT_NAME; }
 
-    public static class BulgarianCustomizer extends LayoutCustomizer {
+    private static class BulgarianCustomizer extends LayoutCustomizer {
         private final EastSlavicCustomizer mEastSlavicCustomizer;
 
-        public BulgarianCustomizer(final Locale locale) {
+        BulgarianCustomizer(final Locale locale) {
             super(locale);
             mEastSlavicCustomizer = new EastSlavicCustomizer(locale);
         }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/BulgarianBds.java b/tests/src/com/android/inputmethod/keyboard/layout/BulgarianBds.java
index 20a5f7d..74372b9 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/BulgarianBds.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/BulgarianBds.java
@@ -16,7 +16,7 @@
 
 package com.android.inputmethod.keyboard.layout;
 
-import com.android.inputmethod.keyboard.layout.EastSlavic.EastSlavicCustomizer;
+import com.android.inputmethod.keyboard.layout.customizer.EastSlavicCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
@@ -25,15 +25,15 @@
 public final class BulgarianBds extends LayoutBase {
     private static final String LAYOUT_NAME = "bulgarian_bds";
 
-    public BulgarianBds(final LayoutCustomizer customizer) {
-        super(customizer, Symbols.class, SymbolsShifted.class);
+    public BulgarianBds(final Locale locale) {
+        super(new BulgarianBdsCustomizer(locale), Symbols.class, SymbolsShifted.class);
     }
 
     @Override
     public String getName() { return LAYOUT_NAME; }
 
-    public static class BulgarianBdsCustomizer extends EastSlavicCustomizer {
-        public BulgarianBdsCustomizer(final Locale locale) { super(locale); }
+    private static class BulgarianBdsCustomizer extends EastSlavicCustomizer {
+        BulgarianBdsCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_R9L; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Colemak.java b/tests/src/com/android/inputmethod/keyboard/layout/Colemak.java
index a4a9701..3f8ce28 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/Colemak.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Colemak.java
@@ -17,6 +17,7 @@
 package com.android.inputmethod.keyboard.layout;
 
 import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Dvorak.java b/tests/src/com/android/inputmethod/keyboard/layout/Dvorak.java
index 66fce01..7cb3b92 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/Dvorak.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Dvorak.java
@@ -17,12 +17,10 @@
 package com.android.inputmethod.keyboard.layout;
 
 import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
-import com.android.inputmethod.keyboard.layout.expected.ExpectedKey.ExpectedAdditionalMoreKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
-import java.util.Locale;
-
 /**
  * The Dvorak alphabet keyboard.
  */
@@ -36,52 +34,17 @@
     @Override
     public String getName() { return LAYOUT_NAME; }
 
-    public static class DvorakCustomizer extends LayoutCustomizer {
-        public DvorakCustomizer(final Locale locale) { super(locale); }
-
-        @Override
-        public ExpectedKey[] getLeftShiftKeys(final boolean isPhone) {
-            return isPhone ? joinKeys(SHIFT_KEY): joinKeys(SHIFT_KEY, key("q"));
-        }
-
-        @Override
-        public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
-            return isPhone ? EMPTY_KEYS : joinKeys(key("z"), SHIFT_KEY);
-        }
-
-        @Override
-        public ExpectedKey[] getKeysLeftToSpacebar(final boolean isPhone) {
-            // U+00A1: "¡" INVERTED EXCLAMATION MARK
-            return isPhone ? joinKeys(key("q", SETTINGS_KEY))
-                    : joinKeys(key("!", joinMoreKeys("\u00A1", SETTINGS_KEY)));
-        }
-
-        @Override
-        public ExpectedKey[] getKeysRightToSpacebar(final boolean isPhone) {
-            final ExpectedAdditionalMoreKey[] punctuationMoreKeys =
-                    convertToAdditionalMoreKeys(getPunctuationMoreKeys(isPhone));
-            // U+00BF: "¿" INVERTED QUESTION MARK
-            return isPhone
-                    ? joinKeys(key("z", punctuationMoreKeys))
-                    : joinKeys(key("?", joinMoreKeys(punctuationMoreKeys, "\u00BF")));
-        }
-
-        private static ExpectedAdditionalMoreKey[] convertToAdditionalMoreKeys(
-                final ExpectedKey ... moreKeys) {
-            final ExpectedAdditionalMoreKey[] additionalMoreKeys =
-                    new ExpectedAdditionalMoreKey[moreKeys.length];
-            for (int index = 0; index < moreKeys.length; index++) {
-                additionalMoreKeys[index] = ExpectedAdditionalMoreKey.newInstance(moreKeys[index]);
-            }
-            return additionalMoreKeys;
-        }
-    }
-
     @Override
     public ExpectedKey[][] getCommonAlphabetLayout(final boolean isPhone) {
         return ALPHABET_COMMON;
     }
 
+    /**
+     * Get the left most key of the first row.
+     * @param isPhone true if requesting phone's keys.
+     * @param elementId the element id of the requesting shifted mode.
+     * @return the left most key of the first row.
+     */
     protected ExpectedKey getRow1_1Key(final boolean isPhone, final int elementId) {
         if (elementId == KeyboardId.ELEMENT_ALPHABET
                 || elementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) {
@@ -90,6 +53,12 @@
         return key("\"", additionalMoreKey("1"));
     }
 
+    /**
+     * Get the 2nd left key of the first row.
+     * @param isPhone true if requesting phone's keys.
+     * @param elementId the element id of the requesting shifted mode.
+     * @return the 2nd left key of the first row.
+     */
     protected ExpectedKey getRow1_2Key(final boolean isPhone, final int elementId) {
         if (elementId == KeyboardId.ELEMENT_ALPHABET
                 || elementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) {
@@ -98,6 +67,12 @@
         return key("<", additionalMoreKey("2"));
     }
 
+    /**
+     * Get the 3rd left key of the first row.
+     * @param isPhone true if requesting phone's keys.
+     * @param elementId the element id of the requesting shifted mode.
+     * @return the 3rd left key of the first row.
+     */
     protected ExpectedKey getRow1_3Key(final boolean isPhone, final int elementId) {
         if (elementId == KeyboardId.ELEMENT_ALPHABET
                 || elementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) {
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/EastSlavic.java b/tests/src/com/android/inputmethod/keyboard/layout/EastSlavic.java
index 7fcc974..f95d2d2 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/EastSlavic.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/EastSlavic.java
@@ -16,11 +16,9 @@
 
 package com.android.inputmethod.keyboard.layout;
 
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
-import com.android.inputmethod.latin.Constants;
-
-import java.util.Locale;
 
 public final class EastSlavic extends LayoutBase {
     private static final String LAYOUT_NAME = "east_slavic";
@@ -32,26 +30,6 @@
     @Override
     public String getName() { return LAYOUT_NAME; }
 
-    public static class EastSlavicCustomizer extends LayoutCustomizer {
-        public EastSlavicCustomizer(final Locale locale) {
-            super(locale);
-        }
-
-        @Override
-        public final ExpectedKey getAlphabetKey() { return EAST_SLAVIC_ALPHABET_KEY; }
-
-        @Override
-        public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
-            return isPhone ? EMPTY_KEYS : EXCLAMATION_AND_QUESTION_MARKS;
-        }
-
-        // U+0410: "А" CYRILLIC CAPITAL LETTER A
-        // U+0411: "Б" CYRILLIC CAPITAL LETTER BE
-        // U+0412: "В" CYRILLIC CAPITAL LETTER VE
-        private static final ExpectedKey EAST_SLAVIC_ALPHABET_KEY = key(
-                "\u0410\u0411\u0412", Constants.CODE_SWITCH_ALPHA_SYMBOL);
-    }
-
     @Override
     ExpectedKey[][] getCommonAlphabetLayout(final boolean isPhone) { return ALPHABET_COMMON; }
 
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Farsi.java b/tests/src/com/android/inputmethod/keyboard/layout/Farsi.java
index 7390457..7c75a3e 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/Farsi.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Farsi.java
@@ -18,6 +18,7 @@
 
 import com.android.inputmethod.keyboard.layout.Symbols.RtlSymbols;
 import com.android.inputmethod.keyboard.layout.SymbolsShifted.RtlSymbolsShifted;
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 import com.android.inputmethod.latin.Constants;
@@ -27,17 +28,15 @@
 public final class Farsi extends LayoutBase {
     private static final String LAYOUT_NAME = "farsi";
 
-    public Farsi(final LayoutCustomizer customizer) {
-        super(customizer, FarsiSymbols.class, FarsiSymbolsShifted.class);
+    public Farsi(final Locale locale) {
+        super(new FarsiCustomizer(locale), FarsiSymbols.class, FarsiSymbolsShifted.class);
     }
 
     @Override
     public String getName() { return LAYOUT_NAME; }
 
-    public static class FarsiCustomizer extends LayoutCustomizer {
-        public FarsiCustomizer(final Locale locale) {
-            super(locale);
-        }
+    private static class FarsiCustomizer extends LayoutCustomizer {
+        FarsiCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKey getAlphabetKey() { return FARSI_ALPHABET_KEY; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Georgian.java b/tests/src/com/android/inputmethod/keyboard/layout/Georgian.java
index 6f20dfc..c26cb96 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/Georgian.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Georgian.java
@@ -17,6 +17,7 @@
 package com.android.inputmethod.keyboard.layout;
 
 import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 import com.android.inputmethod.latin.Constants;
@@ -29,15 +30,15 @@
 public final class Georgian extends LayoutBase {
     private static final String LAYOUT_NAME = "georgian";
 
-    public Georgian(final LayoutCustomizer customizer) {
-        super(customizer, Symbols.class, SymbolsShifted.class);
+    public Georgian(final Locale locale) {
+        super(new GeorgianCustomizer(locale), Symbols.class, SymbolsShifted.class);
     }
 
     @Override
     public String getName() { return LAYOUT_NAME; }
 
-    public static class GeorgianCustomizer extends LayoutCustomizer {
-        public GeorgianCustomizer(final Locale locale) { super(locale); }
+    private static class GeorgianCustomizer extends LayoutCustomizer {
+        GeorgianCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKey getAlphabetKey() { return GEORGIAN_ALPHABET_KEY; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Greek.java b/tests/src/com/android/inputmethod/keyboard/layout/Greek.java
index 475052c..a8eb3d9 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/Greek.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Greek.java
@@ -17,6 +17,7 @@
 package com.android.inputmethod.keyboard.layout;
 
 import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.keyboard.layout.customizer.EuroCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 import com.android.inputmethod.latin.Constants;
@@ -29,15 +30,15 @@
 public final class Greek extends LayoutBase {
     private static final String LAYOUT_NAME = "greek";
 
-    public Greek(final LayoutCustomizer customizer) {
-        super(customizer, Symbols.class, SymbolsShifted.class);
+    public Greek(final Locale locale) {
+        super(new GreekCustomizer(locale), Symbols.class, SymbolsShifted.class);
     }
 
     @Override
     public String getName() { return LAYOUT_NAME; }
 
-    public static class GreekCustomizer extends EuroCustomizer {
-        public GreekCustomizer(final Locale locale) { super(locale); }
+    private static class GreekCustomizer extends EuroCustomizer {
+        GreekCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKey getAlphabetKey() { return GREEK_ALPHABET_KEY; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Hebrew.java b/tests/src/com/android/inputmethod/keyboard/layout/Hebrew.java
index 552f0d3..69b4358 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/Hebrew.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Hebrew.java
@@ -18,6 +18,7 @@
 
 import com.android.inputmethod.keyboard.layout.Symbols.RtlSymbols;
 import com.android.inputmethod.keyboard.layout.SymbolsShifted.RtlSymbolsShifted;
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 import com.android.inputmethod.latin.Constants;
@@ -27,17 +28,15 @@
 public final class Hebrew extends LayoutBase {
     private static final String LAYOUT_NAME = "hebrew";
 
-    public Hebrew(final LayoutCustomizer customizer) {
-        super(customizer, HebrewSymbols.class, RtlSymbolsShifted.class);
+    public Hebrew(final Locale locale) {
+        super(new HebrewCustomizer(locale), HebrewSymbols.class, RtlSymbolsShifted.class);
     }
 
     @Override
     public String getName() { return LAYOUT_NAME; }
 
-    public static class HebrewCustomizer extends LayoutCustomizer {
-        public HebrewCustomizer(final Locale locale) {
-            super(locale);
-        }
+    private static class HebrewCustomizer extends LayoutCustomizer {
+        HebrewCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKey getAlphabetKey() { return HEBREW_ALPHABET_KEY; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Hindi.java b/tests/src/com/android/inputmethod/keyboard/layout/Hindi.java
index b12b8be..82f67ac 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/Hindi.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Hindi.java
@@ -19,11 +19,9 @@
 import static com.android.inputmethod.keyboard.layout.DevanagariLetterConstants.*;
 
 import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
-import com.android.inputmethod.latin.Constants;
-
-import java.util.Locale;
 
 /**
  * The Hindi keyboard.
@@ -38,50 +36,6 @@
     @Override
     public String getName() { return LAYOUT_NAME; }
 
-    public static class HindiCustomizer extends LayoutCustomizer {
-        public HindiCustomizer(final Locale locale) { super(locale); }
-
-        @Override
-        public ExpectedKey getAlphabetKey() { return HINDI_ALPHABET_KEY; }
-
-        @Override
-        public ExpectedKey getSymbolsKey() { return HINDI_SYMBOLS_KEY; }
-
-        @Override
-        public ExpectedKey getBackToSymbolsKey() { return HINDI_BACK_TO_SYMBOLS_KEY; }
-
-        @Override
-        public ExpectedKey getCurrencyKey() { return CURRENCY_RUPEE; }
-
-        @Override
-        public ExpectedKey[] getOtherCurrencyKeys() {
-            return SymbolsShifted.CURRENCIES_OTHER_GENERIC;
-        }
-
-        @Override
-        public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
-            return isPhone ? EMPTY_KEYS : EXCLAMATION_AND_QUESTION_MARKS;
-        }
-
-        // U+0915: "क" DEVANAGARI LETTER KA
-        // U+0916: "ख" DEVANAGARI LETTER KHA
-        // U+0917: "ग" DEVANAGARI LETTER GA
-        private static final ExpectedKey HINDI_ALPHABET_KEY = key(
-                "\u0915\u0916\u0917", Constants.CODE_SWITCH_ALPHA_SYMBOL);
-        // U+0967: "१" DEVANAGARI DIGIT ONE
-        // U+0968: "२" DEVANAGARI DIGIT TWO
-        // U+0969: "३" DEVANAGARI DIGIT THREE
-        private static final String HINDI_SYMBOLS_LABEL = "?\u0967\u0968\u0969";
-        private static final ExpectedKey HINDI_SYMBOLS_KEY = key(HINDI_SYMBOLS_LABEL,
-                Constants.CODE_SWITCH_ALPHA_SYMBOL);
-        private static final ExpectedKey HINDI_BACK_TO_SYMBOLS_KEY = key(HINDI_SYMBOLS_LABEL,
-                Constants.CODE_SHIFT);
-
-        // U+20B9: "₹" INDIAN RUPEE SIGN
-        private static final ExpectedKey CURRENCY_RUPEE = key("\u20B9",
-                Symbols.CURRENCY_GENERIC_MORE_KEYS);
-    }
-
     @Override
     ExpectedKey[][] getCommonAlphabetLayout(boolean isPhone) { return ALPHABET_COMMON; }
 
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/HindiCompact.java b/tests/src/com/android/inputmethod/keyboard/layout/HindiCompact.java
index c2a15f3..a2e3399 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/HindiCompact.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/HindiCompact.java
@@ -18,8 +18,8 @@
 
 import static com.android.inputmethod.keyboard.layout.DevanagariLetterConstants.*;
 
-import com.android.inputmethod.keyboard.layout.Hindi.HindiCustomizer;
 import com.android.inputmethod.keyboard.layout.Hindi.HindiSymbols;
+import com.android.inputmethod.keyboard.layout.customizer.HindiCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
@@ -31,41 +31,20 @@
 public final class HindiCompact extends LayoutBase {
     private static final String LAYOUT_NAME = "hindi_compact";
 
-    public HindiCompact(final LayoutCustomizer customizer) {
-        super(customizer, HindiSymbols.class, SymbolsShifted.class);
+    public HindiCompact(final Locale locale) {
+        super(new HindiCompactCustomizer(locale), HindiSymbols.class, SymbolsShifted.class);
     }
 
     @Override
     public String getName() { return LAYOUT_NAME; }
 
-    public static class HindiCompactCustomizer extends HindiCustomizer {
-        public HindiCompactCustomizer(final Locale locale) { super(locale); }
+    private static class HindiCompactCustomizer extends HindiCustomizer {
+        HindiCompactCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKey[] getLeftShiftKeys(final boolean isPhone) {
             return EMPTY_KEYS;
         }
-
-        @Override
-        public ExpectedKey[] getKeysRightToSpacebar(final boolean isPhone) {
-            // U+0964: "।" DEVANAGARI DANDA
-            final ExpectedKey periodKey = key("\u0964", getPunctuationMoreKeys(isPhone));
-            return joinKeys(periodKey);
-        }
-
-        @Override
-        public ExpectedKey[] getPunctuationMoreKeys(final boolean isPhone) {
-            return isPhone ? HINDI_PHONE_PUNCTUATION_MORE_KEYS : HINDI_TABLET_PUNCTUATION_MORE_KEYS;
-        }
-
-        // Punctuation more keys for phone form factor.
-        private static final ExpectedKey[] HINDI_PHONE_PUNCTUATION_MORE_KEYS = joinKeys(
-                ",", ".", "?", "!", "#", ")", "(", "/", ";",
-                "'", "@", ":", "-", "\"", "+", "%", "&");
-        // Punctuation more keys for tablet form factor.
-        private static final ExpectedKey[] HINDI_TABLET_PUNCTUATION_MORE_KEYS = joinKeys(
-                ",", ".", "'", "#", ")", "(", "/", ";",
-                "@", ":", "-", "\"", "+", "%", "&");
     }
 
     @Override
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Kannada.java b/tests/src/com/android/inputmethod/keyboard/layout/Kannada.java
index 8bee1f8..4fff577 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/Kannada.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Kannada.java
@@ -16,6 +16,7 @@
 
 package com.android.inputmethod.keyboard.layout;
 
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 import com.android.inputmethod.latin.Constants;
@@ -28,15 +29,15 @@
 public final class Kannada extends LayoutBase {
     private static final String LAYOUT_NAME = "kannada";
 
-    public Kannada(final LayoutCustomizer customizer) {
-        super(customizer, Symbols.class, SymbolsShifted.class);
+    public Kannada(final Locale locale) {
+        super(new KannadaCustomizer(locale), Symbols.class, SymbolsShifted.class);
     }
 
     @Override
     public String getName() { return LAYOUT_NAME; }
 
-    public static class KannadaCustomizer extends LayoutCustomizer {
-        public KannadaCustomizer(final Locale locale) { super(locale); }
+    private static class KannadaCustomizer extends LayoutCustomizer {
+        KannadaCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKey getAlphabetKey() { return KANNADA_ALPHABET_KEY; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Khmer.java b/tests/src/com/android/inputmethod/keyboard/layout/Khmer.java
index 7e4f159..20c4d03 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/Khmer.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Khmer.java
@@ -17,6 +17,7 @@
 package com.android.inputmethod.keyboard.layout;
 
 import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 import com.android.inputmethod.latin.Constants;
@@ -29,15 +30,15 @@
 public final class Khmer extends LayoutBase {
     private static final String LAYOUT_NAME = "khmer";
 
-    public Khmer(final LayoutCustomizer customizer) {
-        super(customizer, Symbols.class, SymbolsShifted.class);
+    public Khmer(final Locale locale) {
+        super(new KhmerCustomizer(locale), Symbols.class, SymbolsShifted.class);
     }
 
     @Override
     public String getName() { return LAYOUT_NAME; }
 
-    public static class KhmerCustomizer extends LayoutCustomizer {
-        public KhmerCustomizer(final Locale locale) { super(locale); }
+    private static class KhmerCustomizer extends LayoutCustomizer {
+        KhmerCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public int getNumberOfRows() { return 5; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Lao.java b/tests/src/com/android/inputmethod/keyboard/layout/Lao.java
index aaa1c8a..091c3a6 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/Lao.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Lao.java
@@ -17,6 +17,7 @@
 package com.android.inputmethod.keyboard.layout;
 
 import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 import com.android.inputmethod.latin.Constants;
@@ -29,15 +30,15 @@
 public final class Lao extends LayoutBase {
     private static final String LAYOUT_NAME = "lao";
 
-    public Lao(final LayoutCustomizer customizer) {
-        super(customizer, Symbols.class, SymbolsShifted.class);
+    public Lao(final Locale locale) {
+        super(new LaoCustomizer(locale), Symbols.class, SymbolsShifted.class);
     }
 
     @Override
     public String getName() { return LAYOUT_NAME; }
 
-    public static class LaoCustomizer extends LayoutCustomizer {
-        public LaoCustomizer(final Locale locale) { super(locale); }
+    private static class LaoCustomizer extends LayoutCustomizer {
+        LaoCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public int getNumberOfRows() { return 5; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/LayoutBase.java b/tests/src/com/android/inputmethod/keyboard/layout/LayoutBase.java
index b714ec7..285a50c 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/LayoutBase.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/LayoutBase.java
@@ -17,11 +17,10 @@
 package com.android.inputmethod.keyboard.layout;
 
 import com.android.inputmethod.keyboard.KeyboardId;
-import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.AbstractLayoutBase;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
-import com.android.inputmethod.latin.Constants;
 
 import java.util.Locale;
 
@@ -29,218 +28,6 @@
  * The base class of keyboard layout.
  */
 public abstract class LayoutBase extends AbstractLayoutBase {
-    /**
-     * This class is used to customize common keyboard layout to language specific layout.
-     */
-    public static class LayoutCustomizer {
-        private final Locale mLocale;
-
-        // Empty keys definition to remove keys by adding this.
-        protected static final ExpectedKey[] EMPTY_KEYS = joinKeys();
-
-        public LayoutCustomizer(final Locale locale) {
-            mLocale = locale;
-        }
-
-        public final Locale getLocale() {
-            return mLocale;
-        }
-
-        public int getNumberOfRows() {
-            return 4;
-        }
-
-        /**
-         * Set accented letters to a specific keyboard element.
-         * @param builder the {@link ExpectedKeyboardBuilder} object that contains common keyboard
-         *        layout.
-         * @param elementId the element id of keyboard
-         * @return the {@link ExpectedKeyboardBuilder} object that contains accented letters as
-         *        "more keys".
-         */
-        public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder,
-                final int elementId) {
-            // This method can be overridden by an extended class to provide customized expected
-            // accented letters depending on the shift state of keyboard.
-            // This is a default behavior to call a shift-state-independent
-            // {@link #setAccentedLetters(ExpectedKeyboardBuilder)} implementation, so that
-            // <code>elementId</code> is ignored here.
-            return setAccentedLetters(builder);
-        }
-
-        /**
-         * Set accented letters to common layout.
-         * @param builder the {@link ExpectedKeyboardBuilder} object that contains common keyboard
-         *        layout.
-         * @return the {@link ExpectedKeyboardBuilder} object that contains accented letters as
-         *        "more keys".
-         */
-        public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
-            return builder;
-        }
-
-        /**
-         * Get the function key to switch to alphabet layout.
-         * @return the {@link ExpectedKey} of the alphabet key.
-         */
-        public ExpectedKey getAlphabetKey() { return ALPHABET_KEY; }
-
-        /**
-         * Get the function key to switch to symbols layout.
-         * @return the {@link ExpectedKey} of the symbols key.
-         */
-        public ExpectedKey getSymbolsKey() { return SYMBOLS_KEY; }
-
-        /**
-         * Get the function key to switch to symbols shift layout.
-         * @param isPhone true if requesting phone's key.
-         * @return the {@link ExpectedKey} of the symbols shift key.
-         */
-        public ExpectedKey getSymbolsShiftKey(boolean isPhone) {
-            return isPhone ? SYMBOLS_SHIFT_KEY : TABLET_SYMBOLS_SHIFT_KEY;
-        }
-
-        /**
-         * Get the function key to switch from symbols shift to symbols layout.
-         * @return the {@link ExpectedKey} of the back to symbols key.
-         */
-        public ExpectedKey getBackToSymbolsKey() { return BACK_TO_SYMBOLS_KEY; }
-
-        /**
-         * Get the currency key.
-         * @return the {@link ExpectedKey} of the currency key.
-         */
-        public ExpectedKey getCurrencyKey() { return Symbols.CURRENCY_DOLLAR; }
-
-        /**
-         * Get other currencies keys.
-         * @return the array of {@link ExpectedKey} that represents other currency keys.
-         */
-        public ExpectedKey[] getOtherCurrencyKeys() {
-            return SymbolsShifted.CURRENCIES_OTHER_THAN_DOLLAR;
-        }
-
-        /**
-         * Get "more keys" of double quotation mark.
-         * @return the array of {@link ExpectedKey} of more double quotation marks in natural order.
-         */
-        public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_9LR; }
-
-        /**
-         * Get "more keys" of single quotation mark.
-         * @return the array of {@link ExpectedKey} of more single quotation marks in natural order.
-         */
-        public ExpectedKey[] getSingleQuoteMoreKeys() { return Symbols.SINGLE_QUOTES_9LR; }
-
-        /**
-         * Get double angle quotation marks in natural order.
-         * @return the array of {@link ExpectedKey} of double angle quotation marks in natural
-         *         order.
-         */
-        public ExpectedKey[] getDoubleAngleQuoteKeys() { return Symbols.DOUBLE_ANGLE_QUOTES_LR; }
-
-        /**
-         * Get single angle quotation marks in natural order.
-         * @return the array of {@link ExpectedKey} of single angle quotation marks in natural
-         *         order.
-         */
-        public ExpectedKey[] getSingleAngleQuoteKeys() { return Symbols.SINGLE_ANGLE_QUOTES_LR; }
-
-        /**
-         * Get the left shift keys.
-         * @param isPhone true if requesting phone's keys.
-         * @return the array of {@link ExpectedKey} that should be placed at left edge of the
-         *         keyboard.
-         */
-        public ExpectedKey[] getLeftShiftKeys(final boolean isPhone) {
-            return joinKeys(SHIFT_KEY);
-        }
-
-        /**
-         * Get the right shift keys.
-         * @param isPhone true if requesting phone's keys.
-         * @return the array of {@link ExpectedKey} that should be placed at right edge of the
-         *         keyboard.
-         */
-        public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
-            return isPhone ? EMPTY_KEYS : joinKeys(EXCLAMATION_AND_QUESTION_MARKS, SHIFT_KEY);
-        }
-
-        /**
-         * Get the enter key.
-         * @param isPhone true if requesting phone's key.
-         * @return the array of {@link ExpectedKey} that should be placed as an enter key.
-         */
-        public ExpectedKey getEnterKey(final boolean isPhone) {
-            return isPhone ? key(ENTER_KEY, EMOJI_ACTION_KEY) : ENTER_KEY;
-        }
-
-        /**
-         * Get the emoji key.
-         * @param isPhone true if requesting phone's key.
-         * @return the array of {@link ExpectedKey} that should be placed as an emoji key.
-         */
-        public ExpectedKey getEmojiKey(final boolean isPhone) {
-            return EMOJI_NORMAL_KEY;
-        }
-
-        /**
-         * Get the space keys.
-         * @param isPhone true if requesting phone's keys.
-         * @return the array of {@link ExpectedKey} that should be placed at the center of the
-         *         keyboard.
-         */
-        public ExpectedKey[] getSpaceKeys(final boolean isPhone) {
-            return joinKeys(LANGUAGE_SWITCH_KEY, SPACE_KEY);
-        }
-
-        /**
-         * Get the keys left to the spacebar.
-         * @param isPhone true if requesting phone's keys.
-         * @return the array of {@link ExpectedKey} that should be placed at left of the spacebar.
-         */
-        public ExpectedKey[] getKeysLeftToSpacebar(final boolean isPhone) {
-            // U+002C: "," COMMA
-            return joinKeys(key("\u002C", SETTINGS_KEY));
-        }
-
-        /**
-         * Get the keys right to the spacebar.
-         * @param isPhone true if requesting phone's keys.
-         * @return the array of {@link ExpectedKey} that should be placed at right of the spacebar.
-         */
-        public ExpectedKey[] getKeysRightToSpacebar(final boolean isPhone) {
-            final ExpectedKey periodKey = key(".", getPunctuationMoreKeys(isPhone));
-            return joinKeys(periodKey);
-        }
-
-        /**
-         * Get "more keys" for the punctuation key (usually the period key).
-         * @param isPhone true if requesting phone's keys.
-         * @return the array of {@link ExpectedKey} that are "more keys" of the punctuation key.
-         */
-        public ExpectedKey[] getPunctuationMoreKeys(final boolean isPhone) {
-            return isPhone ? PHONE_PUNCTUATION_MORE_KEYS : TABLET_PUNCTUATION_MORE_KEYS;
-        }
-    }
-
-    /**
-     * The layout customize class for countries that use Euro.
-     */
-    public static class EuroCustomizer extends LayoutCustomizer {
-        public EuroCustomizer(final Locale locale) {
-            super(locale);
-        }
-
-        @Override
-        public final ExpectedKey getCurrencyKey() { return Symbols.CURRENCY_EURO; }
-
-        @Override
-        public final ExpectedKey[] getOtherCurrencyKeys() {
-            return SymbolsShifted.CURRENCIES_OTHER_THAN_EURO;
-        }
-    }
-
     private final LayoutCustomizer mCustomizer;
     private final Symbols mSymbols;
     private final SymbolsShifted mSymbolsShifted;
@@ -276,77 +63,6 @@
      */
     public final LayoutCustomizer getCustomizer() { return mCustomizer; }
 
-    // Icon ids.
-    private static final int ICON_DELETE = KeyboardIconsSet.getIconId(
-            KeyboardIconsSet.NAME_DELETE_KEY);
-    private static final int ICON_SPACE = KeyboardIconsSet.getIconId(
-            KeyboardIconsSet.NAME_SPACE_KEY);
-    private static final int ICON_TAB = KeyboardIconsSet.getIconId(
-            KeyboardIconsSet.NAME_TAB_KEY);
-    private static final int ICON_SHORTCUT = KeyboardIconsSet.getIconId(
-            KeyboardIconsSet.NAME_SHORTCUT_KEY);
-    private static final int ICON_SETTINGS = KeyboardIconsSet.getIconId(
-            KeyboardIconsSet.NAME_SETTINGS_KEY);
-    private static final int ICON_LANGUAGE_SWITCH = KeyboardIconsSet.getIconId(
-            KeyboardIconsSet.NAME_LANGUAGE_SWITCH_KEY);
-    private static final int ICON_ENTER = KeyboardIconsSet.getIconId(
-            KeyboardIconsSet.NAME_ENTER_KEY);
-    private static final int ICON_EMOJI_ACTION = KeyboardIconsSet.getIconId(
-            KeyboardIconsSet.NAME_EMOJI_ACTION_KEY);
-    private static final int ICON_EMOJI_NORMAL = KeyboardIconsSet.getIconId(
-            KeyboardIconsSet.NAME_EMOJI_NORMAL_KEY);
-    private static final int ICON_SHIFT = KeyboardIconsSet.getIconId(
-            KeyboardIconsSet.NAME_SHIFT_KEY);
-    private static final int ICON_SHIFTED_SHIFT = KeyboardIconsSet.getIconId(
-            KeyboardIconsSet.NAME_SHIFT_KEY_SHIFTED);
-    private static final int ICON_ZWNJ = KeyboardIconsSet.getIconId(
-            KeyboardIconsSet.NAME_ZWNJ_KEY);
-    private static final int ICON_ZWJ = KeyboardIconsSet.getIconId(
-            KeyboardIconsSet.NAME_ZWJ_KEY);
-
-    // Functional keys.
-    public static final ExpectedKey DELETE_KEY = key(ICON_DELETE, Constants.CODE_DELETE);
-    public static final ExpectedKey TAB_KEY = key(ICON_TAB, Constants.CODE_TAB);
-    public static final ExpectedKey SHORTCUT_KEY = key(ICON_SHORTCUT, Constants.CODE_SHORTCUT);
-    public static final ExpectedKey SETTINGS_KEY = key(ICON_SETTINGS, Constants.CODE_SETTINGS);
-    public static final ExpectedKey LANGUAGE_SWITCH_KEY = key(
-            ICON_LANGUAGE_SWITCH, Constants.CODE_LANGUAGE_SWITCH);
-    public static final ExpectedKey ENTER_KEY = key(ICON_ENTER, Constants.CODE_ENTER);
-    public static final ExpectedKey EMOJI_ACTION_KEY = key(ICON_EMOJI_ACTION, Constants.CODE_EMOJI);
-    public static final ExpectedKey EMOJI_NORMAL_KEY = key(ICON_EMOJI_NORMAL, Constants.CODE_EMOJI);
-    public static final ExpectedKey SPACE_KEY = key(ICON_SPACE, Constants.CODE_SPACE);
-    static final ExpectedKey CAPSLOCK_MORE_KEY = key(" ", Constants.CODE_CAPSLOCK);
-    public static final ExpectedKey SHIFT_KEY = key(ICON_SHIFT,
-            Constants.CODE_SHIFT, CAPSLOCK_MORE_KEY);
-    public static final ExpectedKey SHIFTED_SHIFT_KEY = key(ICON_SHIFTED_SHIFT,
-            Constants.CODE_SHIFT, CAPSLOCK_MORE_KEY);
-    static final ExpectedKey ALPHABET_KEY = key("ABC", Constants.CODE_SWITCH_ALPHA_SYMBOL);
-    static final ExpectedKey SYMBOLS_KEY = key("?123", Constants.CODE_SWITCH_ALPHA_SYMBOL);
-    static final ExpectedKey BACK_TO_SYMBOLS_KEY = key("?123", Constants.CODE_SHIFT);
-    static final ExpectedKey SYMBOLS_SHIFT_KEY = key("= \\ <", Constants.CODE_SHIFT);
-    static final ExpectedKey TABLET_SYMBOLS_SHIFT_KEY = key("~ [ <", Constants.CODE_SHIFT);
-
-    // U+00A1: "¡" INVERTED EXCLAMATION MARK
-    // U+00BF: "¿" INVERTED QUESTION MARK
-    public static final ExpectedKey[] EXCLAMATION_AND_QUESTION_MARKS = joinKeys(
-            key("!", moreKey("\u00A1")), key("?", moreKey("\u00BF")));
-    // U+200C: ZERO WIDTH NON-JOINER
-    // U+200D: ZERO WIDTH JOINER
-    static final ExpectedKey ZWNJ_KEY = key(ICON_ZWNJ, "\u200C");
-    static final ExpectedKey ZWJ_KEY = key(ICON_ZWJ, "\u200D");
-    // Domain key
-    public static final ExpectedKey DOMAIN_KEY =
-            key(".com", joinMoreKeys(".net", ".org", ".gov", ".edu")).preserveCase();
-
-    // Punctuation more keys for phone form factor.
-    public static final ExpectedKey[] PHONE_PUNCTUATION_MORE_KEYS = joinKeys(
-            ",", "?", "!", "#", ")", "(", "/", ";",
-            "'", "@", ":", "-", "\"", "+", "%", "&");
-    // Punctuation more keys for tablet form factor.
-    public static final ExpectedKey[] TABLET_PUNCTUATION_MORE_KEYS = joinKeys(
-            ",", "'", "#", ")", "(", "/", ";",
-            "@", ":", "-", "\"", "+", "%", "&");
-
     /**
      * Helper method to create alphabet layout adding special function keys.
      * @param builder the {@link ExpectedKeyboardBuilder} object that contains common keyboard
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Malayalam.java b/tests/src/com/android/inputmethod/keyboard/layout/Malayalam.java
index b44b888..55c2e8b 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/Malayalam.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Malayalam.java
@@ -16,6 +16,7 @@
 
 package com.android.inputmethod.keyboard.layout;
 
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 import com.android.inputmethod.latin.Constants;
@@ -28,15 +29,15 @@
 public final class Malayalam extends LayoutBase {
     private static final String LAYOUT_NAME = "malayalam";
 
-    public Malayalam(final LayoutCustomizer customizer) {
-        super(customizer, Symbols.class, SymbolsShifted.class);
+    public Malayalam(final Locale locale) {
+        super(new MalayalamCustomizer(locale), Symbols.class, SymbolsShifted.class);
     }
 
     @Override
     public String getName() { return LAYOUT_NAME; }
 
-    public static class MalayalamCustomizer extends LayoutCustomizer {
-        public MalayalamCustomizer(final Locale locale) { super(locale); }
+    private static class MalayalamCustomizer extends LayoutCustomizer {
+        MalayalamCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKey getAlphabetKey() { return MALAYALAM_ALPHABET_KEY; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Marathi.java b/tests/src/com/android/inputmethod/keyboard/layout/Marathi.java
index 00cf838..af26ec5 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/Marathi.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Marathi.java
@@ -18,8 +18,8 @@
 
 import static com.android.inputmethod.keyboard.layout.DevanagariLetterConstants.*;
 
-import com.android.inputmethod.keyboard.layout.Hindi.HindiCustomizer;
 import com.android.inputmethod.keyboard.layout.Hindi.HindiSymbols;
+import com.android.inputmethod.keyboard.layout.customizer.DevanagariCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
@@ -31,20 +31,37 @@
 public final class Marathi extends LayoutBase {
     private static final String LAYOUT_NAME = "marathi";
 
-    public Marathi(final LayoutCustomizer customizer) {
-        super(customizer, HindiSymbols.class, SymbolsShifted.class);
+    public Marathi(final Locale locale) {
+        super(new MarathiCustomizer(locale), HindiSymbols.class, SymbolsShifted.class);
     }
 
     @Override
     public String getName() { return LAYOUT_NAME; }
 
-    public static class MarathiCustomizer extends HindiCustomizer {
-        public MarathiCustomizer(final Locale locale) { super(locale); }
+    private static class MarathiCustomizer extends DevanagariCustomizer {
+        MarathiCustomizer(final Locale locale) { super(locale); }
+
+        @Override
+        public ExpectedKey getCurrencyKey() { return CURRENCY_RUPEE; }
+
+        @Override
+        public ExpectedKey[] getOtherCurrencyKeys() {
+            return SymbolsShifted.CURRENCIES_OTHER_GENERIC;
+        }
 
         @Override
         public ExpectedKey[] getLeftShiftKeys(final boolean isPhone) {
             return EMPTY_KEYS;
         }
+
+        @Override
+        public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
+            return isPhone ? EMPTY_KEYS : EXCLAMATION_AND_QUESTION_MARKS;
+        }
+
+        // U+20B9: "₹" INDIAN RUPEE SIGN
+        private static final ExpectedKey CURRENCY_RUPEE = key("\u20B9",
+                Symbols.CURRENCY_GENERIC_MORE_KEYS);
     }
 
     @Override
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Mongolian.java b/tests/src/com/android/inputmethod/keyboard/layout/Mongolian.java
index 3c6c058..288a17e 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/Mongolian.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Mongolian.java
@@ -16,7 +16,7 @@
 
 package com.android.inputmethod.keyboard.layout;
 
-import com.android.inputmethod.keyboard.layout.EastSlavic.EastSlavicCustomizer;
+import com.android.inputmethod.keyboard.layout.customizer.EastSlavicCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
@@ -25,17 +25,15 @@
 public final class Mongolian extends LayoutBase {
     private static final String LAYOUT_NAME = "mongolian";
 
-    public Mongolian(final LayoutCustomizer customizer) {
-        super(customizer, Symbols.class, SymbolsShifted.class);
+    public Mongolian(final Locale locale) {
+        super(new MongolianCustomizer(locale), Symbols.class, SymbolsShifted.class);
     }
 
     @Override
     public String getName() { return LAYOUT_NAME; }
 
-    public static class MongolianMNCustomizer extends EastSlavicCustomizer {
-        public MongolianMNCustomizer(final Locale locale) {
-            super(locale);
-        }
+    private static class MongolianCustomizer extends EastSlavicCustomizer {
+        MongolianCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKey getCurrencyKey() { return CURRENCY_TUGRIK; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Myanmar.java b/tests/src/com/android/inputmethod/keyboard/layout/Myanmar.java
index 3c70d32..f7b3590 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/Myanmar.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Myanmar.java
@@ -17,6 +17,7 @@
 package com.android.inputmethod.keyboard.layout;
 
 import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 import com.android.inputmethod.latin.Constants;
@@ -29,15 +30,15 @@
 public final class Myanmar extends LayoutBase {
     private static final String LAYOUT_NAME = "myanmar";
 
-    public Myanmar(final LayoutCustomizer customizer) {
-        super(customizer, Symbols.class, SymbolsShifted.class);
+    public Myanmar(final Locale locale) {
+        super(new MyanmarCustomizer(locale), Symbols.class, SymbolsShifted.class);
     }
 
     @Override
     public String getName() { return LAYOUT_NAME; }
 
-    public static class MyanmarCustomizer extends LayoutCustomizer {
-        public MyanmarCustomizer(final Locale locale) { super(locale); }
+    private static class MyanmarCustomizer extends LayoutCustomizer {
+        MyanmarCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public int getNumberOfRows() { return 5; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/NepaliRomanized.java b/tests/src/com/android/inputmethod/keyboard/layout/NepaliRomanized.java
index 7933d07..299cb61 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/NepaliRomanized.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/NepaliRomanized.java
@@ -19,8 +19,8 @@
 import static com.android.inputmethod.keyboard.layout.DevanagariLetterConstants.*;
 
 import com.android.inputmethod.keyboard.KeyboardId;
-import com.android.inputmethod.keyboard.layout.Hindi.HindiCustomizer;
 import com.android.inputmethod.keyboard.layout.Hindi.HindiSymbols;
+import com.android.inputmethod.keyboard.layout.customizer.NepaliCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
@@ -32,35 +32,18 @@
 public final class NepaliRomanized extends LayoutBase {
     private static final String LAYOUT_NAME = "nepali_romanized";
 
-    public NepaliRomanized(final LayoutCustomizer customizer) {
-        super(customizer, HindiSymbols.class, SymbolsShifted.class);
+    public NepaliRomanized(final Locale locale) {
+        super(new NepaliCustomizer(locale), HindiSymbols.class, SymbolsShifted.class);
     }
 
     @Override
     public String getName() { return LAYOUT_NAME; }
 
-    public static class NepaliRomanizedCustomizer extends HindiCustomizer {
-        public NepaliRomanizedCustomizer(final Locale locale) { super(locale); }
-
-        @Override
-        public ExpectedKey getCurrencyKey() { return CURRENCY_NEPALI; }
-
-        @Override
-        public ExpectedKey[] getSpaceKeys(final boolean isPhone) {
-            return joinKeys(LANGUAGE_SWITCH_KEY, SPACE_KEY, key(ZWNJ_KEY, ZWJ_KEY));
-        }
-
-        // U+0930/U+0941/U+002E "रु." NEPALESE RUPEE SIGN
-        private static final ExpectedKey CURRENCY_NEPALI = key("\u0930\u0941\u002E",
-                Symbols.DOLLAR_SIGN, Symbols.CENT_SIGN, Symbols.EURO_SIGN, Symbols.POUND_SIGN,
-                Symbols.YEN_SIGN, Symbols.PESO_SIGN);
-    }
+    @Override
+    ExpectedKey[][] getCommonAlphabetLayout(final boolean isPhone) { return ALPHABET_COMMON; }
 
     @Override
-    ExpectedKey[][] getCommonAlphabetLayout(boolean isPhone) { return ALPHABET_COMMON; }
-
-    @Override
-    ExpectedKey[][] getCommonAlphabetShiftLayout(boolean isPhone, final int elementId) {
+    ExpectedKey[][] getCommonAlphabetShiftLayout(final boolean isPhone, final int elementId) {
         if (elementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) {
             return getCommonAlphabetLayout(isPhone);
         }
@@ -126,11 +109,9 @@
                     // U+0928: "न" DEVANAGARI LETTER NA
                     // U+092E: "म" DEVANAGARI LETTER MA
                     "\u0937", "\u0921", "\u091A", "\u0935", "\u092C", "\u0928", "\u092E",
-                    // U+0964: "।" DEVANAGARI DANDA
-                    // U+093D: "ऽ" DEVANAGARI SIGN AVAGRAHA
-                    key("\u0964", moreKey("\u093D")),
                     // U+094D: "्" DEVANAGARI SIGN VIRAMA
-                    key(SIGN_VIRAMA, "\u094D"))
+                    // U+093D: "ऽ" DEVANAGARI SIGN AVAGRAHA
+                    key(SIGN_VIRAMA, "\u094D", moreKey("\u093D")))
             .build();
 
     private static final ExpectedKey[][] ALPHABET_SHIFTED_COMMON = new ExpectedKeyboardBuilder()
@@ -180,8 +161,6 @@
                     // U+0902: "ं" DEVANAGARI SIGN ANUSVARA
                     key(SIGN_ANUSVARA, "\u0902"),
                     // U+0919: "ङ" DEVANAGARI LETTER NGA
-                    "\u0919",
-                    // U+094D: "्" DEVANAGARI SIGN VIRAMA
-                    key(SIGN_VIRAMA, "\u094D"))
+                    "\u0919")
             .build();
 }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/NepaliTraditional.java b/tests/src/com/android/inputmethod/keyboard/layout/NepaliTraditional.java
index 4d6cded..0a2bea3 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/NepaliTraditional.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/NepaliTraditional.java
@@ -20,7 +20,7 @@
 
 import com.android.inputmethod.keyboard.KeyboardId;
 import com.android.inputmethod.keyboard.layout.Hindi.HindiSymbols;
-import com.android.inputmethod.keyboard.layout.NepaliRomanized.NepaliRomanizedCustomizer;
+import com.android.inputmethod.keyboard.layout.customizer.NepaliCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
@@ -32,93 +32,31 @@
 public final class NepaliTraditional extends LayoutBase {
     private static final String LAYOUT_NAME = "nepali_traditional";
 
-    public NepaliTraditional(final LayoutCustomizer customizer) {
-        super(customizer, HindiSymbols.class, SymbolsShifted.class);
+    public NepaliTraditional(final Locale locale) {
+        super(new NepaliTraditionalCustomizer(locale), HindiSymbols.class, SymbolsShifted.class);
     }
 
     @Override
     public String getName() { return LAYOUT_NAME; }
 
-    public static class NepaliTraditionalCustomizer extends NepaliRomanizedCustomizer {
-        public NepaliTraditionalCustomizer(final Locale locale) { super(locale); }
+    private static class NepaliTraditionalCustomizer extends NepaliCustomizer {
+        NepaliTraditionalCustomizer(final Locale locale) { super(locale); }
 
         @Override
-        public ExpectedKey[] getRightShiftKeys(final boolean isPhone) { return EMPTY_KEYS; }
-
-        @Override
-        public ExpectedKey[] getKeysRightToSpacebar(final boolean isPhone) {
-            if (isPhone) {
-                // U+094D: "्" DEVANAGARI SIGN VIRAMA
-                return joinKeys(key(SIGN_VIRAMA, "\u094D", PHONE_PUNCTUATION_MORE_KEYS));
-            }
-            return super.getKeysRightToSpacebar(isPhone);
+        public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
+            return isPhone ? EMPTY_KEYS : EXCLAMATION_AND_QUESTION_MARKS;
         }
     }
 
     @Override
-    ExpectedKey[][] getCommonAlphabetLayout(boolean isPhone) {
-        final ExpectedKeyboardBuilder builder = new ExpectedKeyboardBuilder(ALPHABET_COMMON);
-        if (isPhone) {
-            builder.addKeysOnTheRightOfRow(3,
-                    // U+0947: "े" DEVANAGARI VOWEL SIGN E
-                    // U+0903: "ः‍" DEVANAGARI SIGN VISARGA
-                    // U+093D: "ऽ" DEVANAGARI SIGN AVAGRAHA
-                    key(VOWEL_SIGN_E, "\u0947", joinMoreKeys(
-                            moreKey(SIGN_VISARGA, "\u0903"), "\u093D")),
-                    // U+0964: "।" DEVANAGARI DANDA
-                    "\u0964",
-                    // U+0930: "र" DEVANAGARI LETTER RA
-                    // U+0930/U+0941: "रु" DEVANAGARI LETTER RA/DEVANAGARI VOWEL SIGN U
-                    key("\u0930", moreKey("\u0930\u0941")));
-        } else {
-            builder.addKeysOnTheRightOfRow(3,
-                    // U+0903: "ः" DEVANAGARI SIGN VISARGA
-                    // U+093D: "ऽ" DEVANAGARI SIGN AVAGRAHA
-                    key(SIGN_VISARGA, "\u0903", moreKey("\u093D")),
-                    // U+0947: "े" DEVANAGARI VOWEL SIGN E
-                    key(VOWEL_SIGN_E, "\u0947"),
-                    // U+0964: "।" DEVANAGARI DANDA
-                    "\u0964",
-                    // U+0930: "र" DEVANAGARI LETTER RA
-                    key("\u0930", moreKey("!")),
-                    // U+094D: "्" DEVANAGARI SIGN VIRAMA
-                    key(SIGN_VIRAMA, "\u094D", moreKey("?")));
-        }
-        return builder.build();
-    }
+    ExpectedKey[][] getCommonAlphabetLayout(final boolean isPhone) { return ALPHABET_COMMON; }
 
     @Override
-    ExpectedKey[][] getCommonAlphabetShiftLayout(boolean isPhone, final int elementId) {
+    ExpectedKey[][] getCommonAlphabetShiftLayout(final boolean isPhone, final int elementId) {
         if (elementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) {
             return getCommonAlphabetLayout(isPhone);
         }
-        final ExpectedKeyboardBuilder builder = new ExpectedKeyboardBuilder(
-                ALPHABET_SHIFTED_COMMON);
-        if (isPhone) {
-            builder.addKeysOnTheRightOfRow(3,
-                    // U+0902: "ं" DEVANAGARI SIGN ANUSVARA
-                    key(SIGN_ANUSVARA, "\u0902"),
-                    // U+0919: "ङ" DEVANAGARI LETTER NGA
-                    "\u0919",
-                    // U+0948: "ै" DEVANAGARI VOWEL SIGN AI
-                    // U+0936/U+094D/U+0930:
-                    //     "श्र" DEVANAGARI LETTER SHA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER RA
-                    key(VOWEL_SIGN_AI, "\u0948", moreKey("\u0936\u094D\u0930")));
-        } else {
-            builder.addKeysOnTheRightOfRow(3,
-                    // U+0902: "ं" DEVANAGARI SIGN ANUSVARA
-                    key(SIGN_ANUSVARA, "\u0902"),
-                    // U+0919: "ङ" DEVANAGARI LETTER NGA
-                    "\u0919",
-                    // U+0948: "ै" DEVANAGARI VOWEL SIGN AI
-                    // U+0936/U+094D/U+0930:
-                    //     "श्र" DEVANAGARI LETTER SHA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER RA
-                    key(VOWEL_SIGN_AI, "\u0948", moreKey("\u0936\u094D\u0930")),
-                    // U+0930/U+0941: "रु" DEVANAGARI LETTER RA/DEVANAGARI VOWEL SIGN U
-                    key("\u0930\u0941", moreKey("!")),
-                    "?");
-        }
-        return builder.build();
+        return ALPHABET_SHIFTED_COMMON;
     }
 
     private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
@@ -181,7 +119,17 @@
                     // U+0916: "ख" DEVANAGARI LETTER KHA
                     // U+0926: "द" DEVANAGARI LETTER DA
                     // U+0932: "ल" DEVANAGARI LETTER LA
-                    "\u0936", "\u0939", "\u0905", "\u0916", "\u0926", "\u0932")
+                    "\u0936", "\u0939", "\u0905", "\u0916", "\u0926", "\u0932",
+                    // U+0947: "े" DEVANAGARI VOWEL SIGN E
+                    // U+0903: "ः‍" DEVANAGARI SIGN VISARGA
+                    // U+093D: "ऽ" DEVANAGARI SIGN AVAGRAHA
+                    key(VOWEL_SIGN_E, "\u0947", joinMoreKeys(
+                            moreKey(SIGN_VISARGA, "\u0903"), "\u093D")),
+                    // U+094D: "्" DEVANAGARI SIGN VIRAMA
+                    key(SIGN_VIRAMA, "\u094D"),
+                    // U+0930: "र" DEVANAGARI LETTER RA
+                    // U+0930/U+0941: "रु" DEVANAGARI LETTER RA/DEVANAGARI VOWEL SIGN U
+                    key("\u0930", moreKey("\u0930\u0941")))
             .build();
 
     private static final ExpectedKey[][] ALPHABET_SHIFTED_COMMON = new ExpectedKeyboardBuilder()
@@ -264,6 +212,14 @@
                     key(VOWEL_SIGN_AU, "\u094C"),
                     // U+0926/U+094D/U+092F:
                     //     "द्य" DEVANAGARI LETTER DA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER YA
-                    "\u0926\u094D\u092F")
+                    "\u0926\u094D\u092F",
+                    // U+0902: "ं" DEVANAGARI SIGN ANUSVARA
+                    key(SIGN_ANUSVARA, "\u0902"),
+                    // U+0919: "ङ" DEVANAGARI LETTER NGA
+                    "\u0919",
+                    // U+0948: "ै" DEVANAGARI VOWEL SIGN AI
+                    // U+0936/U+094D/U+0930:
+                    //     "श्र" DEVANAGARI LETTER SHA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER RA
+                    key(VOWEL_SIGN_AI, "\u0948", moreKey("\u0936\u094D\u0930")))
             .build();
 }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Nordic.java b/tests/src/com/android/inputmethod/keyboard/layout/Nordic.java
index c791c40..4f718e6 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/Nordic.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Nordic.java
@@ -16,6 +16,7 @@
 
 package com.android.inputmethod.keyboard.layout;
 
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/PcQwerty.java b/tests/src/com/android/inputmethod/keyboard/layout/PcQwerty.java
index 3f7340f..0085ac6 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/PcQwerty.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/PcQwerty.java
@@ -17,11 +17,10 @@
 package com.android.inputmethod.keyboard.layout;
 
 import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
-import java.util.Locale;
-
 /**
  * The PC QWERTY alphabet keyboard.
  */
@@ -35,35 +34,6 @@
     @Override
     public String getName() { return LAYOUT_NAME; }
 
-    public static class PcQwertyCustomizer extends LayoutCustomizer {
-        public PcQwertyCustomizer(final Locale locale) { super(locale); }
-
-        @Override
-        public int getNumberOfRows() { return 5; }
-
-        @Override
-        public ExpectedKey[] getLeftShiftKeys(final boolean isPhone) {
-            return joinKeys(SHIFT_KEY);
-        }
-
-        @Override
-        public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
-            return joinKeys(SHIFT_KEY);
-        }
-
-        @Override
-        public ExpectedKey[] getKeysLeftToSpacebar(final boolean isPhone) {
-            return joinKeys(SETTINGS_KEY);
-        }
-
-        @Override
-        public ExpectedKey[] getKeysRightToSpacebar(final boolean isPhone) {
-            return isPhone
-                    ? joinKeys(key(ENTER_KEY, EMOJI_ACTION_KEY))
-                    : joinKeys(EMOJI_NORMAL_KEY);
-        }
-    }
-
     @Override
     ExpectedKey[][] getCommonAlphabetLayout(final boolean isPhone) {
         final LayoutCustomizer customizer = getCustomizer();
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Qwerty.java b/tests/src/com/android/inputmethod/keyboard/layout/Qwerty.java
index d790a1e..508df0c 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/Qwerty.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Qwerty.java
@@ -16,6 +16,7 @@
 
 package com.android.inputmethod.keyboard.layout;
 
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Qwertz.java b/tests/src/com/android/inputmethod/keyboard/layout/Qwertz.java
index 26ba6cf..cc41fbf 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/Qwertz.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Qwertz.java
@@ -16,6 +16,7 @@
 
 package com.android.inputmethod.keyboard.layout;
 
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/SerbianQwertz.java b/tests/src/com/android/inputmethod/keyboard/layout/SerbianQwertz.java
index b23fe76..a493628 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/SerbianQwertz.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/SerbianQwertz.java
@@ -16,6 +16,7 @@
 
 package com.android.inputmethod.keyboard.layout;
 
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Sinhala.java b/tests/src/com/android/inputmethod/keyboard/layout/Sinhala.java
index 354141d..cdd9ea7 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/Sinhala.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Sinhala.java
@@ -17,6 +17,7 @@
 package com.android.inputmethod.keyboard.layout;
 
 import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 import com.android.inputmethod.latin.Constants;
@@ -29,15 +30,15 @@
 public final class Sinhala extends LayoutBase {
     private static final String LAYOUT_NAME = "sinhala";
 
-    public Sinhala(final LayoutCustomizer customizer) {
-        super(customizer, Symbols.class, SymbolsShifted.class);
+    public Sinhala(final Locale locale) {
+        super(new SinhalaCustomizer(locale), Symbols.class, SymbolsShifted.class);
     }
 
     @Override
     public String getName() { return LAYOUT_NAME; }
 
-    public static class SinhalaCustomizer extends LayoutCustomizer {
-        public SinhalaCustomizer(final Locale locale) { super(locale); }
+    private static class SinhalaCustomizer extends LayoutCustomizer {
+        SinhalaCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKey getAlphabetKey() { return SINHALA_ALPHABET_KEY; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/SouthSlavic.java b/tests/src/com/android/inputmethod/keyboard/layout/SouthSlavic.java
index be8b435..ad82787 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/SouthSlavic.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/SouthSlavic.java
@@ -16,11 +16,9 @@
 
 package com.android.inputmethod.keyboard.layout;
 
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
-import com.android.inputmethod.latin.Constants;
-
-import java.util.Locale;
 
 public final class SouthSlavic extends LayoutBase {
     private static final String LAYOUT_NAME = "south_slavic";
@@ -32,26 +30,6 @@
     @Override
     public String getName() { return LAYOUT_NAME; }
 
-    public static class SouthSlavicLayoutCustomizer extends LayoutCustomizer {
-        public SouthSlavicLayoutCustomizer(final Locale locale) {
-            super(locale);
-        }
-
-        @Override
-        public final ExpectedKey getAlphabetKey() { return SOUTH_SLAVIC_ALPHABET_KEY; }
-
-        @Override
-        public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
-            return isPhone ? EMPTY_KEYS : EXCLAMATION_AND_QUESTION_MARKS;
-        }
-
-        // U+0410: "А" CYRILLIC CAPITAL LETTER A
-        // U+0411: "Б" CYRILLIC CAPITAL LETTER BE
-        // U+0412: "В" CYRILLIC CAPITAL LETTER VE
-        private static final ExpectedKey SOUTH_SLAVIC_ALPHABET_KEY = key(
-                "\u0410\u0411\u0412", Constants.CODE_SWITCH_ALPHA_SYMBOL);
-    }
-
     @Override
     ExpectedKey[][] getCommonAlphabetLayout(final boolean isPhone) { return ALPHABET_COMMON; }
 
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Spanish.java b/tests/src/com/android/inputmethod/keyboard/layout/Spanish.java
index 225b9f6..fc6f1ea 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/Spanish.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Spanish.java
@@ -16,6 +16,7 @@
 
 package com.android.inputmethod.keyboard.layout;
 
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Swiss.java b/tests/src/com/android/inputmethod/keyboard/layout/Swiss.java
index 01a6020..57e3725 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/Swiss.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Swiss.java
@@ -16,6 +16,7 @@
 
 package com.android.inputmethod.keyboard.layout;
 
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Symbols.java b/tests/src/com/android/inputmethod/keyboard/layout/Symbols.java
index 8030897..7ad7b54 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/Symbols.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Symbols.java
@@ -16,7 +16,7 @@
 
 package com.android.inputmethod.keyboard.layout;
 
-import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.AbstractLayoutBase;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
@@ -41,13 +41,13 @@
                 customizer.getSingleQuoteMoreKeys(), customizer.getSingleAngleQuoteKeys())));
         if (isPhone) {
             builder.addKeysOnTheLeftOfRow(3, customizer.getSymbolsShiftKey(isPhone))
-                    .addKeysOnTheRightOfRow(3, LayoutBase.DELETE_KEY)
+                    .addKeysOnTheRightOfRow(3, DELETE_KEY)
                     .addKeysOnTheLeftOfRow(4, customizer.getAlphabetKey())
                     .addKeysOnTheRightOfRow(4, customizer.getEnterKey(isPhone));
         } else {
             // Tablet symbols keyboard has extra two keys at the left edge of the 3rd row.
             builder.addKeysOnTheLeftOfRow(3, (Object[])joinKeys("\\", "="));
-            builder.addKeysOnTheRightOfRow(1, LayoutBase.DELETE_KEY)
+            builder.addKeysOnTheRightOfRow(1, DELETE_KEY)
                     .addKeysOnTheRightOfRow(2, customizer.getEnterKey(isPhone))
                     .addKeysOnTheLeftOfRow(3, customizer.getSymbolsShiftKey(isPhone))
                     .addKeysOnTheRightOfRow(3, customizer.getSymbolsShiftKey(isPhone))
@@ -167,7 +167,7 @@
                     // U+00BF: "¿" INVERTED QUESTION MARK
                     key("?", moreKey("\u00BF")))
             .setKeysOfRow(4,
-                    key(","), key("_"), LayoutBase.SPACE_KEY, key("/"),
+                    key(","), key("_"), SPACE_KEY, key("/"),
                     // U+2026: "…" HORIZONTAL ELLIPSIS
                     key(".", moreKey("\u2026")))
             .build();
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/SymbolsShifted.java b/tests/src/com/android/inputmethod/keyboard/layout/SymbolsShifted.java
index 19cb607..6426216 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/SymbolsShifted.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/SymbolsShifted.java
@@ -16,7 +16,7 @@
 
 package com.android.inputmethod.keyboard.layout;
 
-import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 import com.android.inputmethod.keyboard.layout.expected.AbstractLayoutBase;
@@ -37,7 +37,7 @@
         builder.replaceKeyOfLabel(OTHER_CURRENCIES, (Object[])customizer.getOtherCurrencyKeys());
         if (isPhone) {
             builder.addKeysOnTheLeftOfRow(3, customizer.getBackToSymbolsKey())
-                    .addKeysOnTheRightOfRow(3, LayoutBase.DELETE_KEY)
+                    .addKeysOnTheRightOfRow(3, DELETE_KEY)
                     .addKeysOnTheLeftOfRow(4, customizer.getAlphabetKey())
                     .addKeysOnTheRightOfRow(4, customizer.getEnterKey(isPhone));
         } else {
@@ -45,7 +45,7 @@
             // U+00BF: "¿" INVERTED QUESTION MARK
             // U+00A1: "¡" INVERTED EXCLAMATION MARK
             builder.addKeysOnTheRightOfRow(3, (Object[])joinKeys("\u00A1", "\u00BF"));
-            builder.addKeysOnTheRightOfRow(1, LayoutBase.DELETE_KEY)
+            builder.addKeysOnTheRightOfRow(1, DELETE_KEY)
                     .addKeysOnTheRightOfRow(2, customizer.getEnterKey(isPhone))
                     .addKeysOnTheLeftOfRow(3, customizer.getBackToSymbolsKey())
                     .addKeysOnTheRightOfRow(3, customizer.getBackToSymbolsKey())
@@ -122,7 +122,7 @@
                     // U+2264: "≤" LESS-THAN OR EQUAL TO
                     // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
                     key("<", joinMoreKeys("\u2039", "\u2264", "\u00AB")),
-                    LayoutBase.SPACE_KEY,
+                    SPACE_KEY,
                     // U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
                     // U+2265: "≥" GREATER-THAN EQUAL TO
                     // U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Tamil.java b/tests/src/com/android/inputmethod/keyboard/layout/Tamil.java
index 597b6fa..1413e36 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/Tamil.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Tamil.java
@@ -16,11 +16,9 @@
 
 package com.android.inputmethod.keyboard.layout;
 
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
-import com.android.inputmethod.latin.Constants;
-
-import java.util.Locale;
 
 /**
  * The Tamil keyboard.
@@ -35,29 +33,6 @@
     @Override
     public String getName() { return LAYOUT_NAME; }
 
-    public static class TamilCustomizer extends LayoutCustomizer {
-        public TamilCustomizer(final Locale locale) { super(locale); }
-
-        @Override
-        public ExpectedKey getAlphabetKey() { return TAMIL_ALPHABET_KEY; }
-
-        @Override
-        public ExpectedKey[] getLeftShiftKeys(final boolean isPhone) {
-            return EMPTY_KEYS;
-        }
-
-        @Override
-        public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
-            return isPhone ? EMPTY_KEYS : EXCLAMATION_AND_QUESTION_MARKS;
-        }
-
-        // U+0BA4: "த" TAMIL LETTER TA
-        // U+0BAE/U+0BBF: "மி" TAMIL LETTER MA/TAMIL VOWEL SIGN I
-        // U+0BB4/U+0BCD: "ழ்" TAMIL LETTER LLLA/TAMIL SIGN VIRAMA
-        private static final ExpectedKey TAMIL_ALPHABET_KEY = key(
-                "\u0BA4\u0BAE\u0BBF\u0BB4\u0BCD", Constants.CODE_SWITCH_ALPHA_SYMBOL);
-    }
-
     @Override
     ExpectedKey[][] getCommonAlphabetLayout(boolean isPhone) { return ALPHABET_COMMON; }
 
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Telugu.java b/tests/src/com/android/inputmethod/keyboard/layout/Telugu.java
index cc8224c..84c5df6 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/Telugu.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Telugu.java
@@ -16,6 +16,7 @@
 
 package com.android.inputmethod.keyboard.layout;
 
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 import com.android.inputmethod.latin.Constants;
@@ -28,15 +29,15 @@
 public final class Telugu extends LayoutBase {
     private static final String LAYOUT_NAME = "telugu";
 
-    public Telugu(final LayoutCustomizer customizer) {
-        super(customizer, Symbols.class, SymbolsShifted.class);
+    public Telugu(final Locale locale) {
+        super(new TeluguCustomizer(locale), Symbols.class, SymbolsShifted.class);
     }
 
     @Override
     public String getName() { return LAYOUT_NAME; }
 
-    public static class TeluguCustomizer extends LayoutCustomizer {
-        public TeluguCustomizer(final Locale locale) { super(locale); }
+    private static class TeluguCustomizer extends LayoutCustomizer {
+        TeluguCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKey getAlphabetKey() { return TELUGU_ALPHABET_KEY; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Thai.java b/tests/src/com/android/inputmethod/keyboard/layout/Thai.java
index cfda294..1463336 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/Thai.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Thai.java
@@ -17,6 +17,7 @@
 package com.android.inputmethod.keyboard.layout;
 
 import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 import com.android.inputmethod.latin.Constants;
@@ -29,15 +30,15 @@
 public final class Thai extends LayoutBase {
     private static final String LAYOUT_NAME = "thai";
 
-    public Thai(final LayoutCustomizer customizer) {
-        super(customizer, Symbols.class, SymbolsShifted.class);
+    public Thai(final Locale locale) {
+        super(new ThaiCustomizer(locale), Symbols.class, SymbolsShifted.class);
     }
 
     @Override
     public String getName() { return LAYOUT_NAME; }
 
-    public static class ThaiCustomizer extends LayoutCustomizer {
-        public ThaiCustomizer(final Locale locale) { super(locale); }
+    private static class ThaiCustomizer extends LayoutCustomizer {
+        ThaiCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public int getNumberOfRows() { return 5; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Uzbek.java b/tests/src/com/android/inputmethod/keyboard/layout/Uzbek.java
new file mode 100644
index 0000000..f37fd22
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/Uzbek.java
@@ -0,0 +1,59 @@
+/*
+ * 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.layout;
+
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+/**
+ * The Uzbek alphabet keyboard.
+ */
+public final class Uzbek extends LayoutBase {
+    private static final String LAYOUT_NAME = "uzbek";
+
+    public Uzbek(final LayoutCustomizer customizer) {
+        super(customizer, Symbols.class, SymbolsShifted.class);
+    }
+
+    @Override
+    public String getName() { return LAYOUT_NAME; }
+
+    @Override
+    ExpectedKey[][] getCommonAlphabetLayout(final boolean isPhone) { return ALPHABET_COMMON; }
+
+    public static final String ROW1_11 = "ROW1_11";
+    public static final String ROW2_10 = "ROW2_10";
+    public static final String ROW2_11 = "ROW2_11";
+
+    private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder()
+            .setKeysOfRow(1,
+                    key("q", additionalMoreKey("1")),
+                    key("w", additionalMoreKey("2")),
+                    key("e", additionalMoreKey("3")),
+                    key("r", additionalMoreKey("4")),
+                    key("t", additionalMoreKey("5")),
+                    key("y", additionalMoreKey("6")),
+                    key("u", additionalMoreKey("7")),
+                    key("i", additionalMoreKey("8")),
+                    key("o", additionalMoreKey("9")),
+                    key("p", additionalMoreKey("0")),
+                    ROW1_11)
+            .setKeysOfRow(2, "a", "s", "d", "f", "g", "h", "j", "k", "l", ROW2_10, ROW2_11)
+            .setKeysOfRow(3, "z", "x", "c", "v", "b", "n", "m")
+            .build();
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/customizer/BengaliCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/customizer/BengaliCustomizer.java
new file mode 100644
index 0000000..d255516
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/customizer/BengaliCustomizer.java
@@ -0,0 +1,46 @@
+/*
+ * 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.layout.customizer;
+
+import com.android.inputmethod.keyboard.layout.SymbolsShifted;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.latin.Constants;
+
+import java.util.Locale;
+
+public class BengaliCustomizer extends LayoutCustomizer {
+    public BengaliCustomizer(final Locale locale) { super(locale); }
+
+    @Override
+    public ExpectedKey getAlphabetKey() { return BENGALI_ALPHABET_KEY; }
+
+    @Override
+    public ExpectedKey[] getOtherCurrencyKeys() {
+        return SymbolsShifted.CURRENCIES_OTHER_GENERIC;
+    }
+
+    @Override
+    public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
+        return isPhone ? EMPTY_KEYS : EXCLAMATION_AND_QUESTION_MARKS;
+    }
+
+    // U+0995: "क" BENGALI LETTER KA
+    // U+0996: "ख" BENGALI LETTER KHA
+    // U+0997: "ग" BENGALI LETTER GA
+    private static final ExpectedKey BENGALI_ALPHABET_KEY = key(
+            "\u0995\u0996\u0997", Constants.CODE_SWITCH_ALPHA_SYMBOL);
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/DanishCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/customizer/DanishCustomizer.java
similarity index 96%
rename from tests/src/com/android/inputmethod/keyboard/layout/tests/DanishCustomizer.java
rename to tests/src/com/android/inputmethod/keyboard/layout/customizer/DanishCustomizer.java
index 2c5df60..3d91194 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/DanishCustomizer.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/customizer/DanishCustomizer.java
@@ -14,17 +14,16 @@
  * limitations under the License.
  */
 
-package com.android.inputmethod.keyboard.layout.tests;
+package com.android.inputmethod.keyboard.layout.customizer;
 
 import com.android.inputmethod.keyboard.layout.Nordic;
 import com.android.inputmethod.keyboard.layout.Symbols;
-import com.android.inputmethod.keyboard.layout.LayoutBase.EuroCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
 import java.util.Locale;
 
-class DanishCustomizer extends EuroCustomizer {
+public class DanishCustomizer extends EuroCustomizer {
     public DanishCustomizer(final Locale locale) { super(locale); }
 
     @Override
@@ -110,4 +109,4 @@
                 // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
                 .setMoreKeysOf("n", "\u00F1", "\u0144");
     }
-}
\ No newline at end of file
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/customizer/DevanagariCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/customizer/DevanagariCustomizer.java
new file mode 100644
index 0000000..d4e5e58
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/customizer/DevanagariCustomizer.java
@@ -0,0 +1,49 @@
+/*
+ * 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.layout.customizer;
+
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.latin.Constants;
+
+import java.util.Locale;
+
+public class DevanagariCustomizer extends LayoutCustomizer {
+    public DevanagariCustomizer(final Locale locale) { super(locale); }
+
+    @Override
+    public ExpectedKey getAlphabetKey() { return HINDI_ALPHABET_KEY; }
+
+    @Override
+    public ExpectedKey getSymbolsKey() { return HINDI_SYMBOLS_KEY; }
+
+    @Override
+    public ExpectedKey getBackToSymbolsKey() { return HINDI_BACK_TO_SYMBOLS_KEY; }
+
+    // U+0915: "क" DEVANAGARI LETTER KA
+    // U+0916: "ख" DEVANAGARI LETTER KHA
+    // U+0917: "ग" DEVANAGARI LETTER GA
+    private static final ExpectedKey HINDI_ALPHABET_KEY = key(
+            "\u0915\u0916\u0917", Constants.CODE_SWITCH_ALPHA_SYMBOL);
+    // U+0967: "१" DEVANAGARI DIGIT ONE
+    // U+0968: "२" DEVANAGARI DIGIT TWO
+    // U+0969: "३" DEVANAGARI DIGIT THREE
+    private static final String HINDI_SYMBOLS_LABEL = "?\u0967\u0968\u0969";
+    private static final ExpectedKey HINDI_SYMBOLS_KEY = key(HINDI_SYMBOLS_LABEL,
+            Constants.CODE_SWITCH_ALPHA_SYMBOL);
+    private static final ExpectedKey HINDI_BACK_TO_SYMBOLS_KEY = key(HINDI_SYMBOLS_LABEL,
+            Constants.CODE_SHIFT);
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/ItalianCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/customizer/DutchCustomizer.java
similarity index 71%
copy from tests/src/com/android/inputmethod/keyboard/layout/tests/ItalianCustomizer.java
copy to tests/src/com/android/inputmethod/keyboard/layout/customizer/DutchCustomizer.java
index 7350709..825afb6 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/ItalianCustomizer.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/customizer/DutchCustomizer.java
@@ -14,64 +14,76 @@
  * limitations under the License.
  */
 
-package com.android.inputmethod.keyboard.layout.tests;
+package com.android.inputmethod.keyboard.layout.customizer;
 
-import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
+import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
 import java.util.Locale;
 
-class ItalianCustomizer extends LayoutCustomizer {
-    public ItalianCustomizer(final Locale locale) { super(locale); }
+public class DutchCustomizer extends EuroCustomizer {
+    public DutchCustomizer(final Locale locale) { super(locale); }
+
+    @Override
+    public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_L9R; }
+
+    @Override
+    public ExpectedKey[] getSingleQuoteMoreKeys() { return Symbols.SINGLE_QUOTES_L9R; }
 
     @Override
     public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
         return builder
-                // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
-                // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
-                // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
-                // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
-                // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
-                // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
-                // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
-                .setMoreKeysOf("e",
-                        "\u00E8", "\u00E9", "\u00EA", "\u00EB", "\u0119", "\u0117", "\u0113")
-                // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
-                // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
-                // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
-                // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
-                // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
-                .setMoreKeysOf("u", "\u00F9", "\u00FA", "\u00FB", "\u00FC", "\u016B")
-                // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
-                // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
-                // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
-                // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
-                // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
-                // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
-                .setMoreKeysOf("i", "\u00EC", "\u00ED", "\u00EE", "\u00EF", "\u012F", "\u012B")
-                // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
-                // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
-                // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
-                // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
-                // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
-                // U+0153: "œ" LATIN SMALL LIGATURE OE
-                // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
-                // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
-                // U+00BA: "º" MASCULINE ORDINAL INDICATOR
-                .setMoreKeysOf("o",
-                        "\u00F2", "\u00F3", "\u00F4", "\u00F6", "\u00F5", "\u0153", "\u00F8",
-                        "\u014D", "\u00BA")
-                // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
                 // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
-                // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
                 // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+                // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+                // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
                 // U+00E6: "æ" LATIN SMALL LETTER AE
                 // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
                 // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
                 // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
-                // U+00AA: "ª" FEMININE ORDINAL INDICATOR
                 .setMoreKeysOf("a",
-                        "\u00E0", "\u00E1", "\u00E2", "\u00E4", "\u00E6", "\u00E3", "\u00E5",
-                        "\u0101", "\u00AA");
+                        "\u00E1", "\u00E4", "\u00E2", "\u00E0", "\u00E6", "\u00E3", "\u00E5",
+                        "\u0101")
+                // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+                // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
+                // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
+                // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
+                // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
+                // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
+                // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
+                .setMoreKeysOf("e",
+                        "\u00E9", "\u00EB", "\u00EA", "\u00E8", "\u0119", "\u0117", "\u0113")
+                // U+0133: "ij" LATIN SMALL LIGATURE IJ
+                .setMoreKeysOf("y", "\u0133")
+                // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+                // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+                // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
+                // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+                // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
+                .setMoreKeysOf("u", "\u00FA", "\u00FC", "\u00FB", "\u00F9", "\u016B")
+                // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+                // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+                // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+                // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+                // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
+                // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
+                // U+0133: "ij" LATIN SMALL LIGATURE IJ
+                .setMoreKeysOf("i",
+                        "\u00ED", "\u00EF", "\u00EC", "\u00EE", "\u012F", "\u012B", "\u0133")
+                // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+                // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+                // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+                // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+                // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+                // U+0153: "œ" LATIN SMALL LIGATURE OE
+                // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+                // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
+                .setMoreKeysOf("o",
+                        "\u00F3", "\u00F6", "\u00F4", "\u00F2", "\u00F5", "\u0153", "\u00F8",
+                        "\u014D")
+                // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+                // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
+                .setMoreKeysOf("n", "\u00F1", "\u0144");
     }
 }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/customizer/DvorakCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/customizer/DvorakCustomizer.java
new file mode 100644
index 0000000..b7b0187
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/customizer/DvorakCustomizer.java
@@ -0,0 +1,78 @@
+/*
+ * 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.layout.customizer;
+
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey.ExpectedAdditionalMoreKey;
+
+import java.util.Locale;
+
+public class DvorakCustomizer extends LayoutCustomizer {
+    public DvorakCustomizer(final Locale locale) { super(locale); }
+
+    @Override
+    public ExpectedKey[] getLeftShiftKeys(final boolean isPhone) {
+        return isPhone ? joinKeys(SHIFT_KEY): joinKeys(SHIFT_KEY, key("q"));
+    }
+
+    @Override
+    public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
+        return isPhone ? EMPTY_KEYS : joinKeys(key("z"), SHIFT_KEY);
+    }
+
+    @Override
+    public ExpectedKey[] getKeysLeftToSpacebar(final boolean isPhone) {
+        // U+00A1: "¡" INVERTED EXCLAMATION MARK
+        return isPhone ? joinKeys(key("q", SETTINGS_KEY))
+                : joinKeys(key("!", joinMoreKeys("\u00A1", SETTINGS_KEY)));
+    }
+
+    @Override
+    public ExpectedKey[] getKeysRightToSpacebar(final boolean isPhone) {
+        final ExpectedAdditionalMoreKey[] punctuationMoreKeys =
+                convertToAdditionalMoreKeys(getPunctuationMoreKeys(isPhone));
+        // U+00BF: "¿" INVERTED QUESTION MARK
+        return isPhone
+                ? joinKeys(key("z", punctuationMoreKeys))
+                : joinKeys(key("?", joinMoreKeys(punctuationMoreKeys, "\u00BF")));
+    }
+
+    private static ExpectedAdditionalMoreKey[] convertToAdditionalMoreKeys(
+            final ExpectedKey ... moreKeys) {
+        final ExpectedAdditionalMoreKey[] additionalMoreKeys =
+                new ExpectedAdditionalMoreKey[moreKeys.length];
+        for (int index = 0; index < moreKeys.length; index++) {
+            additionalMoreKeys[index] = ExpectedAdditionalMoreKey.newInstance(moreKeys[index]);
+        }
+        return additionalMoreKeys;
+    }
+
+    public static class EnglishDvorakCustomizer extends DvorakCustomizer {
+        private final EnglishCustomizer mEnglishCustomizer;
+
+        public EnglishDvorakCustomizer(final Locale locale) {
+            super(locale);
+            mEnglishCustomizer = new EnglishCustomizer(locale);
+        }
+
+        @Override
+        public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+            return mEnglishCustomizer.setAccentedLetters(builder);
+        }
+    }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/customizer/EastSlavicCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/customizer/EastSlavicCustomizer.java
new file mode 100644
index 0000000..03fc973
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/customizer/EastSlavicCustomizer.java
@@ -0,0 +1,40 @@
+/*
+ * 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.layout.customizer;
+
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.latin.Constants;
+
+import java.util.Locale;
+
+public class EastSlavicCustomizer extends LayoutCustomizer {
+    public EastSlavicCustomizer(final Locale locale) { super(locale); }
+
+    @Override
+    public final ExpectedKey getAlphabetKey() { return EAST_SLAVIC_ALPHABET_KEY; }
+
+    @Override
+    public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
+        return isPhone ? EMPTY_KEYS : EXCLAMATION_AND_QUESTION_MARKS;
+    }
+
+    // U+0410: "А" CYRILLIC CAPITAL LETTER A
+    // U+0411: "Б" CYRILLIC CAPITAL LETTER BE
+    // U+0412: "В" CYRILLIC CAPITAL LETTER VE
+    private static final ExpectedKey EAST_SLAVIC_ALPHABET_KEY = key(
+            "\u0410\u0411\u0412", Constants.CODE_SWITCH_ALPHA_SYMBOL);
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/EnglishCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/customizer/EnglishCustomizer.java
similarity index 93%
rename from tests/src/com/android/inputmethod/keyboard/layout/tests/EnglishCustomizer.java
rename to tests/src/com/android/inputmethod/keyboard/layout/customizer/EnglishCustomizer.java
index 3e82f65..9a9c6bb 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/EnglishCustomizer.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/customizer/EnglishCustomizer.java
@@ -14,15 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.inputmethod.keyboard.layout.tests;
+package com.android.inputmethod.keyboard.layout.customizer;
 
-import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
 import java.util.Locale;
 
-class EnglishCustomizer extends LayoutCustomizer {
-    EnglishCustomizer(final Locale locale) { super(locale); }
+public class EnglishCustomizer extends LayoutCustomizer {
+    public EnglishCustomizer(final Locale locale) { super(locale); }
 
     @Override
     public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/EstonianEECustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/customizer/EstonianEECustomizer.java
similarity index 96%
rename from tests/src/com/android/inputmethod/keyboard/layout/tests/EstonianEECustomizer.java
rename to tests/src/com/android/inputmethod/keyboard/layout/customizer/EstonianEECustomizer.java
index d0b8772..a7d611a 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/EstonianEECustomizer.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/customizer/EstonianEECustomizer.java
@@ -14,10 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.inputmethod.keyboard.layout.tests;
+package com.android.inputmethod.keyboard.layout.customizer;
 
 import com.android.inputmethod.keyboard.KeyboardId;
-import com.android.inputmethod.keyboard.layout.LayoutBase.EuroCustomizer;
 import com.android.inputmethod.keyboard.layout.Nordic;
 import com.android.inputmethod.keyboard.layout.Symbols;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
@@ -25,10 +24,8 @@
 
 import java.util.Locale;
 
-class EstonianEECustomizer extends EuroCustomizer {
-    public EstonianEECustomizer(final Locale locale) {
-        super(locale);
-    }
+public class EstonianEECustomizer extends EuroCustomizer {
+    public EstonianEECustomizer(final Locale locale) { super(locale); }
 
     @Override
     public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_R9L; }
@@ -167,4 +164,4 @@
                 // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
                 .setMoreKeysOf("n", "\u0146", "\u00F1", "\u0144");
     }
-}
\ No newline at end of file
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/customizer/EuroCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/customizer/EuroCustomizer.java
new file mode 100644
index 0000000..ee0236d
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/customizer/EuroCustomizer.java
@@ -0,0 +1,40 @@
+/*
+ * 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.layout.customizer;
+
+import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.SymbolsShifted;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+
+import java.util.Locale;
+
+/**
+ * The layout customize class for countries that use Euro.
+ */
+public class EuroCustomizer extends LayoutCustomizer {
+    public EuroCustomizer(final Locale locale) {
+        super(locale);
+    }
+
+    @Override
+    public final ExpectedKey getCurrencyKey() { return Symbols.CURRENCY_EURO; }
+
+    @Override
+    public final ExpectedKey[] getOtherCurrencyKeys() {
+        return SymbolsShifted.CURRENCIES_OTHER_THAN_EURO;
+    }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/FinnishCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/customizer/FinnishCustomizer.java
similarity index 95%
rename from tests/src/com/android/inputmethod/keyboard/layout/tests/FinnishCustomizer.java
rename to tests/src/com/android/inputmethod/keyboard/layout/customizer/FinnishCustomizer.java
index 912aec4..a792f91 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/FinnishCustomizer.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/customizer/FinnishCustomizer.java
@@ -14,15 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.inputmethod.keyboard.layout.tests;
+package com.android.inputmethod.keyboard.layout.customizer;
 
 import com.android.inputmethod.keyboard.layout.Nordic;
-import com.android.inputmethod.keyboard.layout.LayoutBase.EuroCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
 import java.util.Locale;
 
-class FinnishCustomizer extends EuroCustomizer {
+public class FinnishCustomizer extends EuroCustomizer {
     public FinnishCustomizer(final Locale locale) { super(locale); }
 
     protected void setNordicKeys(final ExpectedKeyboardBuilder builder) {
@@ -80,4 +79,4 @@
                 // U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE
                 .setMoreKeysOf("z", "\u017E", "\u017A", "\u017C");
     }
-}
\ No newline at end of file
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/FrenchCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/customizer/FrenchCustomizer.java
similarity index 84%
rename from tests/src/com/android/inputmethod/keyboard/layout/tests/FrenchCustomizer.java
rename to tests/src/com/android/inputmethod/keyboard/layout/customizer/FrenchCustomizer.java
index ab90267..d7798cc 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/FrenchCustomizer.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/customizer/FrenchCustomizer.java
@@ -14,15 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.inputmethod.keyboard.layout.tests;
+package com.android.inputmethod.keyboard.layout.customizer;
 
-import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
 import java.util.Locale;
 
-class FrenchCustomizer extends LayoutCustomizer {
-    FrenchCustomizer(final Locale locale) { super(locale); }
+public class FrenchCustomizer extends LayoutCustomizer {
+    public FrenchCustomizer(final Locale locale) { super(locale); }
 
     @Override
     public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
@@ -86,4 +86,21 @@
                 .setMoreKeysOf("c", "\u00E7", "\u0107", "\u010D")
                 .setAdditionalMoreKeysPositionOf("c", 2);
     }
+
+    public static final class FrenchEuroCustomizer extends FrenchCustomizer {
+        private final EuroCustomizer mEuroCustomizer;
+
+        public FrenchEuroCustomizer(final Locale locale) {
+            super(locale);
+            mEuroCustomizer = new EuroCustomizer(locale);
+        }
+
+        @Override
+        public final ExpectedKey getCurrencyKey() { return mEuroCustomizer.getCurrencyKey(); }
+
+        @Override
+        public final ExpectedKey[] getOtherCurrencyKeys() {
+            return mEuroCustomizer.getOtherCurrencyKeys();
+        }
+    }
 }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/GermanCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/customizer/GermanCustomizer.java
similarity index 86%
rename from tests/src/com/android/inputmethod/keyboard/layout/tests/GermanCustomizer.java
rename to tests/src/com/android/inputmethod/keyboard/layout/customizer/GermanCustomizer.java
index 6d38937..e0e4c78 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/GermanCustomizer.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/customizer/GermanCustomizer.java
@@ -14,16 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.inputmethod.keyboard.layout.tests;
+package com.android.inputmethod.keyboard.layout.customizer;
 
 import com.android.inputmethod.keyboard.layout.Symbols;
-import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
 import java.util.Locale;
 
-class GermanCustomizer extends LayoutCustomizer {
+public class GermanCustomizer extends LayoutCustomizer {
     public GermanCustomizer(final Locale locale) { super(locale); }
 
     @Override
@@ -86,4 +85,21 @@
                 // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
                 .setMoreKeysOf("n", "\u00F1", "\u0144");
     }
+
+    public static class GermanEuroCustomizer extends GermanCustomizer {
+        private final EuroCustomizer mEuroCustomizer;
+
+        public GermanEuroCustomizer(final Locale locale) {
+            super(locale);
+            mEuroCustomizer = new EuroCustomizer(locale);
+        }
+
+        @Override
+        public ExpectedKey getCurrencyKey() { return mEuroCustomizer.getCurrencyKey(); }
+
+        @Override
+        public ExpectedKey[] getOtherCurrencyKeys() {
+            return mEuroCustomizer.getOtherCurrencyKeys();
+        }
+    }
 }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/customizer/HindiCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/customizer/HindiCustomizer.java
new file mode 100644
index 0000000..c7fe9db
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/customizer/HindiCustomizer.java
@@ -0,0 +1,65 @@
+/*
+ * 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.layout.customizer;
+
+import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.SymbolsShifted;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+
+import java.util.Locale;
+
+public class HindiCustomizer extends DevanagariCustomizer {
+    public HindiCustomizer(final Locale locale) { super(locale); }
+
+    @Override
+    public ExpectedKey getCurrencyKey() { return CURRENCY_RUPEE; }
+
+    @Override
+    public ExpectedKey[] getOtherCurrencyKeys() {
+        return SymbolsShifted.CURRENCIES_OTHER_GENERIC;
+    }
+
+    @Override
+    public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
+        return isPhone ? EMPTY_KEYS : EXCLAMATION_AND_QUESTION_MARKS;
+    }
+
+    @Override
+    public ExpectedKey[] getKeysRightToSpacebar(final boolean isPhone) {
+        // U+0964: "।" DEVANAGARI DANDA
+        final ExpectedKey periodKey = key("\u0964", getPunctuationMoreKeys(isPhone));
+        return joinKeys(periodKey);
+    }
+
+    @Override
+    public ExpectedKey[] getPunctuationMoreKeys(final boolean isPhone) {
+        return isPhone ? HINDI_PHONE_PUNCTUATION_MORE_KEYS : HINDI_TABLET_PUNCTUATION_MORE_KEYS;
+    }
+
+    // U+20B9: "₹" INDIAN RUPEE SIGN
+    private static final ExpectedKey CURRENCY_RUPEE = key("\u20B9",
+            Symbols.CURRENCY_GENERIC_MORE_KEYS);
+
+    // Punctuation more keys for phone form factor.
+    private static final ExpectedKey[] HINDI_PHONE_PUNCTUATION_MORE_KEYS = joinKeys(
+            ",", ".", "?", "!", "#", ")", "(", "/", ";",
+            "'", "@", ":", "-", "\"", "+", "%", "&");
+    // Punctuation more keys for tablet form factor.
+    private static final ExpectedKey[] HINDI_TABLET_PUNCTUATION_MORE_KEYS = joinKeys(
+            ",", ".", "'", "#", ")", "(", "/", ";",
+            "@", ":", "-", "\"", "+", "%", "&");
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/ItalianCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/customizer/ItalianCustomizer.java
similarity index 95%
rename from tests/src/com/android/inputmethod/keyboard/layout/tests/ItalianCustomizer.java
rename to tests/src/com/android/inputmethod/keyboard/layout/customizer/ItalianCustomizer.java
index 7350709..3b547fd 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/ItalianCustomizer.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/customizer/ItalianCustomizer.java
@@ -14,14 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.inputmethod.keyboard.layout.tests;
+package com.android.inputmethod.keyboard.layout.customizer;
 
-import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
 import java.util.Locale;
 
-class ItalianCustomizer extends LayoutCustomizer {
+public class ItalianCustomizer extends LayoutCustomizer {
     public ItalianCustomizer(final Locale locale) { super(locale); }
 
     @Override
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/customizer/LayoutCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/customizer/LayoutCustomizer.java
new file mode 100644
index 0000000..27f5534
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/customizer/LayoutCustomizer.java
@@ -0,0 +1,214 @@
+/*
+ * 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.layout.customizer;
+
+import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.SymbolsShifted;
+import com.android.inputmethod.keyboard.layout.expected.AbstractLayoutBase;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * This class is used to customize common keyboard layout to language specific layout.
+ */
+public class LayoutCustomizer extends AbstractLayoutBase {
+    private final Locale mLocale;
+
+    // Empty keys definition to remove keys by adding this.
+    protected static final ExpectedKey[] EMPTY_KEYS = joinKeys();
+
+    public LayoutCustomizer(final Locale locale) {  mLocale = locale; }
+
+    public final Locale getLocale() { return mLocale; }
+
+    public int getNumberOfRows() { return 4; }
+
+    /**
+     * Set accented letters to a specific keyboard element.
+     * @param builder the {@link ExpectedKeyboardBuilder} object that contains common keyboard
+     *        layout.
+     * @param elementId the element id of keyboard
+     * @return the {@link ExpectedKeyboardBuilder} object that contains accented letters as
+     *        "more keys".
+     */
+    public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder,
+            final int elementId) {
+        // This method can be overridden by an extended class to provide customized expected
+        // accented letters depending on the shift state of keyboard.
+        // This is a default behavior to call a shift-state-independent
+        // {@link #setAccentedLetters(ExpectedKeyboardBuilder)} implementation, so that
+        // <code>elementId</code> is ignored here.
+        return setAccentedLetters(builder);
+    }
+
+    /**
+     * Set accented letters to common layout.
+     * @param builder the {@link ExpectedKeyboardBuilder} object that contains common keyboard
+     *        layout.
+     * @return the {@link ExpectedKeyboardBuilder} object that contains accented letters as
+     *        "more keys".
+     */
+    public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+        return builder;
+    }
+
+    /**
+     * Get the function key to switch to alphabet layout.
+     * @return the {@link ExpectedKey} of the alphabet key.
+     */
+    public ExpectedKey getAlphabetKey() { return ALPHABET_KEY; }
+
+    /**
+     * Get the function key to switch to symbols layout.
+     * @return the {@link ExpectedKey} of the symbols key.
+     */
+    public ExpectedKey getSymbolsKey() { return SYMBOLS_KEY; }
+
+    /**
+     * Get the function key to switch to symbols shift layout.
+     * @param isPhone true if requesting phone's key.
+     * @return the {@link ExpectedKey} of the symbols shift key.
+     */
+    public ExpectedKey getSymbolsShiftKey(boolean isPhone) {
+        return isPhone ? SYMBOLS_SHIFT_KEY : TABLET_SYMBOLS_SHIFT_KEY;
+    }
+
+    /**
+     * Get the function key to switch from symbols shift to symbols layout.
+     * @return the {@link ExpectedKey} of the back to symbols key.
+     */
+    public ExpectedKey getBackToSymbolsKey() { return BACK_TO_SYMBOLS_KEY; }
+
+    /**
+     * Get the currency key.
+     * @return the {@link ExpectedKey} of the currency key.
+     */
+    public ExpectedKey getCurrencyKey() { return Symbols.CURRENCY_DOLLAR; }
+
+    /**
+     * Get other currencies keys.
+     * @return the array of {@link ExpectedKey} that represents other currency keys.
+     */
+    public ExpectedKey[] getOtherCurrencyKeys() {
+        return SymbolsShifted.CURRENCIES_OTHER_THAN_DOLLAR;
+    }
+
+    /**
+     * Get "more keys" of double quotation mark.
+     * @return the array of {@link ExpectedKey} of more double quotation marks in natural order.
+     */
+    public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_9LR; }
+
+    /**
+     * Get "more keys" of single quotation mark.
+     * @return the array of {@link ExpectedKey} of more single quotation marks in natural order.
+     */
+    public ExpectedKey[] getSingleQuoteMoreKeys() { return Symbols.SINGLE_QUOTES_9LR; }
+
+    /**
+     * Get double angle quotation marks in natural order.
+     * @return the array of {@link ExpectedKey} of double angle quotation marks in natural
+     *         order.
+     */
+    public ExpectedKey[] getDoubleAngleQuoteKeys() { return Symbols.DOUBLE_ANGLE_QUOTES_LR; }
+
+    /**
+     * Get single angle quotation marks in natural order.
+     * @return the array of {@link ExpectedKey} of single angle quotation marks in natural
+     *         order.
+     */
+    public ExpectedKey[] getSingleAngleQuoteKeys() { return Symbols.SINGLE_ANGLE_QUOTES_LR; }
+
+    /**
+     * Get the left shift keys.
+     * @param isPhone true if requesting phone's keys.
+     * @return the array of {@link ExpectedKey} that should be placed at left edge of the
+     *         keyboard.
+     */
+    public ExpectedKey[] getLeftShiftKeys(final boolean isPhone) {
+        return joinKeys(SHIFT_KEY);
+    }
+
+    /**
+     * Get the right shift keys.
+     * @param isPhone true if requesting phone's keys.
+     * @return the array of {@link ExpectedKey} that should be placed at right edge of the
+     *         keyboard.
+     */
+    public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
+        return isPhone ? EMPTY_KEYS : joinKeys(EXCLAMATION_AND_QUESTION_MARKS, SHIFT_KEY);
+    }
+
+    /**
+     * Get the enter key.
+     * @param isPhone true if requesting phone's key.
+     * @return the array of {@link ExpectedKey} that should be placed as an enter key.
+     */
+    public ExpectedKey getEnterKey(final boolean isPhone) {
+        return isPhone ? key(ENTER_KEY, EMOJI_ACTION_KEY) : ENTER_KEY;
+    }
+
+    /**
+     * Get the emoji key.
+     * @param isPhone true if requesting phone's key.
+     * @return the array of {@link ExpectedKey} that should be placed as an emoji key.
+     */
+    public ExpectedKey getEmojiKey(final boolean isPhone) {
+        return EMOJI_NORMAL_KEY;
+    }
+
+    /**
+     * Get the space keys.
+     * @param isPhone true if requesting phone's keys.
+     * @return the array of {@link ExpectedKey} that should be placed at the center of the
+     *         keyboard.
+     */
+    public ExpectedKey[] getSpaceKeys(final boolean isPhone) {
+        return joinKeys(LANGUAGE_SWITCH_KEY, SPACE_KEY);
+    }
+
+    /**
+     * Get the keys left to the spacebar.
+     * @param isPhone true if requesting phone's keys.
+     * @return the array of {@link ExpectedKey} that should be placed at left of the spacebar.
+     */
+    public ExpectedKey[] getKeysLeftToSpacebar(final boolean isPhone) {
+        // U+002C: "," COMMA
+        return joinKeys(key("\u002C", SETTINGS_KEY));
+    }
+
+    /**
+     * Get the keys right to the spacebar.
+     * @param isPhone true if requesting phone's keys.
+     * @return the array of {@link ExpectedKey} that should be placed at right of the spacebar.
+     */
+    public ExpectedKey[] getKeysRightToSpacebar(final boolean isPhone) {
+        final ExpectedKey periodKey = key(".", getPunctuationMoreKeys(isPhone));
+        return joinKeys(periodKey);
+    }
+
+    /**
+     * Get "more keys" for the punctuation key (usually the period key).
+     * @param isPhone true if requesting phone's keys.
+     * @return the array of {@link ExpectedKey} that are "more keys" of the punctuation key.
+     */
+    public ExpectedKey[] getPunctuationMoreKeys(final boolean isPhone) {
+        return isPhone ? PHONE_PUNCTUATION_MORE_KEYS : TABLET_PUNCTUATION_MORE_KEYS;
+    }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/customizer/NepaliCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/customizer/NepaliCustomizer.java
new file mode 100644
index 0000000..264322f
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/customizer/NepaliCustomizer.java
@@ -0,0 +1,67 @@
+/*
+ * 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.layout.customizer;
+
+import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.SymbolsShifted;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+
+import java.util.Locale;
+
+public class NepaliCustomizer extends DevanagariCustomizer {
+    public NepaliCustomizer(final Locale locale) { super(locale); }
+
+    @Override
+    public ExpectedKey getCurrencyKey() { return CURRENCY_NEPALI; }
+
+    @Override
+    public ExpectedKey[] getOtherCurrencyKeys() {
+        return SymbolsShifted.CURRENCIES_OTHER_GENERIC;
+    }
+
+    @Override
+    public ExpectedKey[] getSpaceKeys(final boolean isPhone) {
+        return joinKeys(LANGUAGE_SWITCH_KEY, SPACE_KEY, key(ZWNJ_KEY, ZWJ_KEY));
+    }
+
+    @Override
+    public ExpectedKey[] getKeysRightToSpacebar(final boolean isPhone) {
+        // U+0964: "।" DEVANAGARI DANDA
+        final ExpectedKey periodKey = key("\u0964", getPunctuationMoreKeys(isPhone));
+        return joinKeys(periodKey);
+    }
+
+    @Override
+    public ExpectedKey[] getPunctuationMoreKeys(final boolean isPhone) {
+        return isPhone ? NEPALI_PHONE_PUNCTUATION_MORE_KEYS
+                : NEPALI_TABLET_PUNCTUATION_MORE_KEYS;
+    }
+
+    // U+0930/U+0941/U+002E "रु." NEPALESE RUPEE SIGN
+    private static final ExpectedKey CURRENCY_NEPALI = key("\u0930\u0941\u002E",
+            Symbols.DOLLAR_SIGN, Symbols.CENT_SIGN, Symbols.EURO_SIGN, Symbols.POUND_SIGN,
+            Symbols.YEN_SIGN, Symbols.PESO_SIGN);
+
+    // Punctuation more keys for phone form factor.
+    private static final ExpectedKey[] NEPALI_PHONE_PUNCTUATION_MORE_KEYS = joinKeys(
+            ".", ",", "?", "!", "#", ")", "(", "/", ";",
+            "'", "@", ":", "-", "\"", "+", "%", "&");
+    // Punctuation more keys for tablet form factor.
+    private static final ExpectedKey[] NEPALI_TABLET_PUNCTUATION_MORE_KEYS = joinKeys(
+            ".", ",", "'", "#", ")", "(", "/", ";",
+            "@", ":", "-", "\"", "+", "%", "&");
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/NoLanguageCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/customizer/NoLanguageCustomizer.java
similarity index 97%
rename from tests/src/com/android/inputmethod/keyboard/layout/tests/NoLanguageCustomizer.java
rename to tests/src/com/android/inputmethod/keyboard/layout/customizer/NoLanguageCustomizer.java
index 9edbcab..b6bf5bf 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/NoLanguageCustomizer.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/customizer/NoLanguageCustomizer.java
@@ -14,15 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.inputmethod.keyboard.layout.tests;
+package com.android.inputmethod.keyboard.layout.customizer;
 
-import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
 import java.util.Locale;
 
-class NoLanguageCustomizer extends LayoutCustomizer {
-    NoLanguageCustomizer(final Locale locale) { super(locale); }
+public class NoLanguageCustomizer extends LayoutCustomizer {
+    public NoLanguageCustomizer(final Locale locale) { super(locale); }
 
     @Override
     public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/NorwegianCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/customizer/NorwegianCustomizer.java
similarity index 95%
rename from tests/src/com/android/inputmethod/keyboard/layout/tests/NorwegianCustomizer.java
rename to tests/src/com/android/inputmethod/keyboard/layout/customizer/NorwegianCustomizer.java
index 4be7a57..6cc44e5 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/NorwegianCustomizer.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/customizer/NorwegianCustomizer.java
@@ -14,17 +14,16 @@
  * limitations under the License.
  */
 
-package com.android.inputmethod.keyboard.layout.tests;
+package com.android.inputmethod.keyboard.layout.customizer;
 
 import com.android.inputmethod.keyboard.layout.Nordic;
 import com.android.inputmethod.keyboard.layout.Symbols;
-import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
 import java.util.Locale;
 
-class NorwegianCustomizer extends LayoutCustomizer {
+public class NorwegianCustomizer extends LayoutCustomizer {
     public NorwegianCustomizer(final Locale locale) { super(locale); }
 
     @Override
@@ -93,4 +92,4 @@
                 // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
                 .setMoreKeysOf("u", "\u00FC", "\u00FB", "\u00F9", "\u00FA", "\u016B");
     }
-}
\ No newline at end of file
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/customizer/PcQwertyCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/customizer/PcQwertyCustomizer.java
new file mode 100644
index 0000000..9a0f764
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/customizer/PcQwertyCustomizer.java
@@ -0,0 +1,50 @@
+/*
+ * 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.layout.customizer;
+
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+
+import java.util.Locale;
+
+public class PcQwertyCustomizer extends LayoutCustomizer {
+    public PcQwertyCustomizer(final Locale locale) { super(locale); }
+
+    @Override
+    public int getNumberOfRows() { return 5; }
+
+    @Override
+    public ExpectedKey[] getLeftShiftKeys(final boolean isPhone) {
+        return joinKeys(SHIFT_KEY);
+    }
+
+    @Override
+    public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
+        return joinKeys(SHIFT_KEY);
+    }
+
+    @Override
+    public ExpectedKey[] getKeysLeftToSpacebar(final boolean isPhone) {
+        return joinKeys(SETTINGS_KEY);
+    }
+
+    @Override
+    public ExpectedKey[] getKeysRightToSpacebar(final boolean isPhone) {
+        return isPhone
+                ? joinKeys(key(ENTER_KEY, EMOJI_ACTION_KEY))
+                : joinKeys(EMOJI_NORMAL_KEY);
+    }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/PortugueseCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/customizer/PortugueseCustomizer.java
similarity index 93%
rename from tests/src/com/android/inputmethod/keyboard/layout/tests/PortugueseCustomizer.java
rename to tests/src/com/android/inputmethod/keyboard/layout/customizer/PortugueseCustomizer.java
index 629e8cb..4fc64cc 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/PortugueseCustomizer.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/customizer/PortugueseCustomizer.java
@@ -14,15 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.inputmethod.keyboard.layout.tests;
+package com.android.inputmethod.keyboard.layout.customizer;
 
-import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
 import java.util.Locale;
 
-class PortugueseCustomizer extends LayoutCustomizer {
-    PortugueseCustomizer(final Locale locale) { super(locale); }
+public class PortugueseCustomizer extends LayoutCustomizer {
+    public PortugueseCustomizer(final Locale locale) { super(locale); }
 
     @Override
     public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/SerbianLatinCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/customizer/SerbianLatinCustomizer.java
similarity index 89%
rename from tests/src/com/android/inputmethod/keyboard/layout/tests/SerbianLatinCustomizer.java
rename to tests/src/com/android/inputmethod/keyboard/layout/customizer/SerbianLatinCustomizer.java
index 3660ce4..4d03c8b 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/SerbianLatinCustomizer.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/customizer/SerbianLatinCustomizer.java
@@ -14,22 +14,20 @@
  * limitations under the License.
  */
 
-package com.android.inputmethod.keyboard.layout.tests;
+package com.android.inputmethod.keyboard.layout.customizer;
 
-import com.android.inputmethod.keyboard.layout.LayoutBase;
-import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.SerbianQwertz;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
 import java.util.Locale;
 
-class SerbianLatinCustomizer extends LayoutCustomizer {
+public class SerbianLatinCustomizer extends LayoutCustomizer {
     public SerbianLatinCustomizer(final Locale locale) { super(locale); }
 
     @Override
     public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
-        return isPhone ? EMPTY_KEYS : LayoutBase.EXCLAMATION_AND_QUESTION_MARKS;
+        return isPhone ? EMPTY_KEYS : EXCLAMATION_AND_QUESTION_MARKS;
     }
 
     protected void setSerbianKeys(final ExpectedKeyboardBuilder builder) {
@@ -79,4 +77,4 @@
                 // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
                 .setMoreKeysOf("i", "\u00EC");
     }
-}
\ No newline at end of file
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/customizer/SouthSlavicLayoutCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/customizer/SouthSlavicLayoutCustomizer.java
new file mode 100644
index 0000000..cc41d37
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/customizer/SouthSlavicLayoutCustomizer.java
@@ -0,0 +1,43 @@
+/*
+ * 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.layout.customizer;
+
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.latin.Constants;
+
+import java.util.Locale;
+
+public class SouthSlavicLayoutCustomizer extends LayoutCustomizer {
+    public SouthSlavicLayoutCustomizer(final Locale locale) {
+        super(locale);
+    }
+
+    @Override
+    public final ExpectedKey getAlphabetKey() { return SOUTH_SLAVIC_ALPHABET_KEY; }
+
+    @Override
+    public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
+        return isPhone ? EMPTY_KEYS : EXCLAMATION_AND_QUESTION_MARKS;
+    }
+
+    // U+0410: "А" CYRILLIC CAPITAL LETTER A
+    // U+0411: "Б" CYRILLIC CAPITAL LETTER BE
+    // U+0412: "В" CYRILLIC CAPITAL LETTER VE
+    private static final ExpectedKey SOUTH_SLAVIC_ALPHABET_KEY = key(
+            "\u0410\u0411\u0412", Constants.CODE_SWITCH_ALPHA_SYMBOL);
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/SpanishCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/customizer/SpanishCustomizer.java
similarity index 89%
rename from tests/src/com/android/inputmethod/keyboard/layout/tests/SpanishCustomizer.java
rename to tests/src/com/android/inputmethod/keyboard/layout/customizer/SpanishCustomizer.java
index 8974ad6..1284f05 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/SpanishCustomizer.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/customizer/SpanishCustomizer.java
@@ -14,28 +14,24 @@
  * limitations under the License.
  */
 
-package com.android.inputmethod.keyboard.layout.tests;
+package com.android.inputmethod.keyboard.layout.customizer;
 
-import com.android.inputmethod.keyboard.layout.LayoutBase;
-import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.Spanish;
-import com.android.inputmethod.keyboard.layout.expected.AbstractLayoutBase;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
 import java.util.Locale;
 
-class SpanishCustomizer extends LayoutCustomizer {
-    SpanishCustomizer(final Locale locale) { super(locale); }
+public class SpanishCustomizer extends LayoutCustomizer {
+    public SpanishCustomizer(final Locale locale) { super(locale); }
 
     @Override
     public ExpectedKey[] getPunctuationMoreKeys(final boolean isPhone) {
-        return isPhone ? PHONE_PUNCTUATION_MORE_KEYS
-                : LayoutBase.TABLET_PUNCTUATION_MORE_KEYS;
+        return isPhone ? SPANISH_PHONE_PUNCTUATION_MORE_KEYS : TABLET_PUNCTUATION_MORE_KEYS;
     }
 
     // Punctuation more keys for phone form factor.
-    private static final ExpectedKey[] PHONE_PUNCTUATION_MORE_KEYS = AbstractLayoutBase.joinKeys(
+    private static final ExpectedKey[] SPANISH_PHONE_PUNCTUATION_MORE_KEYS = joinKeys(
             // U+00A1: "¡" INVERTED EXCLAMATION MARK
             // U+00BF: "¿" INVERTED QUESTION MARK
             ",", "?", "!", "#", ")", "(", "/", ";", "\u00A1",
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/SwedishCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/customizer/SwedishCustomizer.java
similarity index 96%
rename from tests/src/com/android/inputmethod/keyboard/layout/tests/SwedishCustomizer.java
rename to tests/src/com/android/inputmethod/keyboard/layout/customizer/SwedishCustomizer.java
index af4a971..f4ff594 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/SwedishCustomizer.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/customizer/SwedishCustomizer.java
@@ -14,10 +14,8 @@
  * limitations under the License.
  */
 
-package com.android.inputmethod.keyboard.layout.tests;
+package com.android.inputmethod.keyboard.layout.customizer;
 
-import com.android.inputmethod.keyboard.layout.LayoutBase.EuroCustomizer;
-import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.Nordic;
 import com.android.inputmethod.keyboard.layout.Symbols;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
@@ -25,7 +23,7 @@
 
 import java.util.Locale;
 
-class SwedishCustomizer extends LayoutCustomizer {
+public class SwedishCustomizer extends LayoutCustomizer {
     private final LayoutCustomizer mEuroCustomizer;
 
     public SwedishCustomizer(final Locale locale) {
@@ -142,4 +140,4 @@
                 // U+0148: "ň" LATIN SMALL LETTER N WITH CARON
                 .setMoreKeysOf("n", "\u0144", "\u00F1", "\u0148");
     }
-}
\ No newline at end of file
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/customizer/TamilCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/customizer/TamilCustomizer.java
new file mode 100644
index 0000000..91fd21e
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/customizer/TamilCustomizer.java
@@ -0,0 +1,45 @@
+/*
+ * 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.layout.customizer;
+
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.latin.Constants;
+
+import java.util.Locale;
+
+public class TamilCustomizer extends LayoutCustomizer {
+    public TamilCustomizer(final Locale locale) { super(locale); }
+
+    @Override
+    public ExpectedKey getAlphabetKey() { return TAMIL_ALPHABET_KEY; }
+
+    @Override
+    public ExpectedKey[] getLeftShiftKeys(final boolean isPhone) {
+        return EMPTY_KEYS;
+    }
+
+    @Override
+    public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
+        return isPhone ? EMPTY_KEYS : EXCLAMATION_AND_QUESTION_MARKS;
+    }
+
+    // U+0BA4: "த" TAMIL LETTER TA
+    // U+0BAE/U+0BBF: "மி" TAMIL LETTER MA/TAMIL VOWEL SIGN I
+    // U+0BB4/U+0BCD: "ழ்" TAMIL LETTER LLLA/TAMIL SIGN VIRAMA
+    private static final ExpectedKey TAMIL_ALPHABET_KEY = key(
+            "\u0BA4\u0BAE\u0BBF\u0BB4\u0BCD", Constants.CODE_SWITCH_ALPHA_SYMBOL);
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TurkicCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/customizer/TurkicCustomizer.java
similarity index 95%
rename from tests/src/com/android/inputmethod/keyboard/layout/tests/TurkicCustomizer.java
rename to tests/src/com/android/inputmethod/keyboard/layout/customizer/TurkicCustomizer.java
index 1a98f6c..3fd3aa2 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TurkicCustomizer.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/customizer/TurkicCustomizer.java
@@ -14,9 +14,8 @@
  * limitations under the License.
  */
 
-package com.android.inputmethod.keyboard.layout.tests;
+package com.android.inputmethod.keyboard.layout.customizer;
 
-import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
 import java.util.Locale;
@@ -24,7 +23,7 @@
 /**
  * Turkic languages layout customizer.
  */
-class TurkicCustomizer extends LayoutCustomizer {
+public class TurkicCustomizer extends LayoutCustomizer {
     public TurkicCustomizer(final Locale locale) { super(locale); }
 
     @Override
@@ -82,4 +81,4 @@
                 // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
                 .setMoreKeysOf("n", "\u0148", "\u00F1");
     }
-}
\ No newline at end of file
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/customizer/UzbekCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/customizer/UzbekCustomizer.java
new file mode 100644
index 0000000..9948616
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/customizer/UzbekCustomizer.java
@@ -0,0 +1,42 @@
+/*
+ * 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.layout.customizer;
+
+import com.android.inputmethod.keyboard.layout.Nordic;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+public class UzbekCustomizer extends TurkicCustomizer {
+    public UzbekCustomizer(final Locale locale) { super(locale); }
+
+    protected void setUzbekKeys(final ExpectedKeyboardBuilder builder) {
+        builder
+                // U+006F/U+02BB: "oʻ" LATIN SMALL LETTER O/MODIFIER LETTER TURNED COMMA
+                .replaceKeyOfLabel(Nordic.ROW1_11, "o\u02BB")
+                // U+0067/U+02BB: "gʻ" LATIN SMALL LETTER G/MODIFIER LETTER TURNED COMMA
+                .replaceKeyOfLabel(Nordic.ROW2_10, "g\u02BB")
+                // U+02BC: "ʼ" MODIFIER LETTER APOSTROPHE
+                .replaceKeyOfLabel(Nordic.ROW2_11, "\u02BC");
+    }
+
+    @Override
+    public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
+        setUzbekKeys(builder);
+        return super.setAccentedLetters(builder);
+    }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractLayoutBase.java b/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractLayoutBase.java
index 3556cb4..b15ef5d 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractLayoutBase.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractLayoutBase.java
@@ -16,7 +16,9 @@
 
 package com.android.inputmethod.keyboard.layout.expected;
 
+import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey.ExpectedAdditionalMoreKey;
+import com.android.inputmethod.latin.Constants;
 
 /**
  * Base class to create an expected keyboard for unit test.
@@ -102,4 +104,75 @@
     public static ExpectedKey[] joinKeys(final Object ... keys) {
         return ExpectedKeyboardBuilder.joinKeys(keys);
     }
+
+    // Icon ids.
+    private static final int ICON_DELETE = KeyboardIconsSet.getIconId(
+            KeyboardIconsSet.NAME_DELETE_KEY);
+    private static final int ICON_SPACE = KeyboardIconsSet.getIconId(
+            KeyboardIconsSet.NAME_SPACE_KEY);
+    private static final int ICON_TAB = KeyboardIconsSet.getIconId(
+            KeyboardIconsSet.NAME_TAB_KEY);
+    private static final int ICON_SHORTCUT = KeyboardIconsSet.getIconId(
+            KeyboardIconsSet.NAME_SHORTCUT_KEY);
+    private static final int ICON_SETTINGS = KeyboardIconsSet.getIconId(
+            KeyboardIconsSet.NAME_SETTINGS_KEY);
+    private static final int ICON_LANGUAGE_SWITCH = KeyboardIconsSet.getIconId(
+            KeyboardIconsSet.NAME_LANGUAGE_SWITCH_KEY);
+    private static final int ICON_ENTER = KeyboardIconsSet.getIconId(
+            KeyboardIconsSet.NAME_ENTER_KEY);
+    private static final int ICON_EMOJI_ACTION = KeyboardIconsSet.getIconId(
+            KeyboardIconsSet.NAME_EMOJI_ACTION_KEY);
+    private static final int ICON_EMOJI_NORMAL = KeyboardIconsSet.getIconId(
+            KeyboardIconsSet.NAME_EMOJI_NORMAL_KEY);
+    private static final int ICON_SHIFT = KeyboardIconsSet.getIconId(
+            KeyboardIconsSet.NAME_SHIFT_KEY);
+    private static final int ICON_SHIFTED_SHIFT = KeyboardIconsSet.getIconId(
+            KeyboardIconsSet.NAME_SHIFT_KEY_SHIFTED);
+    private static final int ICON_ZWNJ = KeyboardIconsSet.getIconId(
+            KeyboardIconsSet.NAME_ZWNJ_KEY);
+    private static final int ICON_ZWJ = KeyboardIconsSet.getIconId(
+            KeyboardIconsSet.NAME_ZWJ_KEY);
+
+    // Functional keys.
+    protected static final ExpectedKey DELETE_KEY = key(ICON_DELETE, Constants.CODE_DELETE);
+    protected static final ExpectedKey TAB_KEY = key(ICON_TAB, Constants.CODE_TAB);
+    protected static final ExpectedKey SHORTCUT_KEY = key(ICON_SHORTCUT, Constants.CODE_SHORTCUT);
+    protected static final ExpectedKey SETTINGS_KEY = key(ICON_SETTINGS, Constants.CODE_SETTINGS);
+    protected static final ExpectedKey LANGUAGE_SWITCH_KEY = key(
+            ICON_LANGUAGE_SWITCH, Constants.CODE_LANGUAGE_SWITCH);
+    protected static final ExpectedKey ENTER_KEY = key(ICON_ENTER, Constants.CODE_ENTER);
+    protected static final ExpectedKey EMOJI_ACTION_KEY = key(ICON_EMOJI_ACTION, Constants.CODE_EMOJI);
+    protected static final ExpectedKey EMOJI_NORMAL_KEY = key(ICON_EMOJI_NORMAL, Constants.CODE_EMOJI);
+    protected static final ExpectedKey SPACE_KEY = key(ICON_SPACE, Constants.CODE_SPACE);
+    protected static final ExpectedKey CAPSLOCK_MORE_KEY = key(" ", Constants.CODE_CAPSLOCK);
+    protected static final ExpectedKey SHIFT_KEY = key(ICON_SHIFT,
+            Constants.CODE_SHIFT, CAPSLOCK_MORE_KEY);
+    protected static final ExpectedKey SHIFTED_SHIFT_KEY = key(ICON_SHIFTED_SHIFT,
+            Constants.CODE_SHIFT, CAPSLOCK_MORE_KEY);
+    protected static final ExpectedKey ALPHABET_KEY = key("ABC", Constants.CODE_SWITCH_ALPHA_SYMBOL);
+    protected static final ExpectedKey SYMBOLS_KEY = key("?123", Constants.CODE_SWITCH_ALPHA_SYMBOL);
+    protected static final ExpectedKey BACK_TO_SYMBOLS_KEY = key("?123", Constants.CODE_SHIFT);
+    protected static final ExpectedKey SYMBOLS_SHIFT_KEY = key("= \\ <", Constants.CODE_SHIFT);
+    protected static final ExpectedKey TABLET_SYMBOLS_SHIFT_KEY = key("~ [ <", Constants.CODE_SHIFT);
+
+    // U+00A1: "¡" INVERTED EXCLAMATION MARK
+    // U+00BF: "¿" INVERTED QUESTION MARK
+    protected static final ExpectedKey[] EXCLAMATION_AND_QUESTION_MARKS = joinKeys(
+            key("!", moreKey("\u00A1")), key("?", moreKey("\u00BF")));
+    // U+200C: ZERO WIDTH NON-JOINER
+    // U+200D: ZERO WIDTH JOINER
+    protected static final ExpectedKey ZWNJ_KEY = key(ICON_ZWNJ, "\u200C");
+    protected static final ExpectedKey ZWJ_KEY = key(ICON_ZWJ, "\u200D");
+    // Domain key
+    protected static final ExpectedKey DOMAIN_KEY =
+            key(".com", joinMoreKeys(".net", ".org", ".gov", ".edu")).preserveCase();
+
+    // Punctuation more keys for phone form factor.
+    protected static final ExpectedKey[] PHONE_PUNCTUATION_MORE_KEYS = joinKeys(
+            ",", "?", "!", "#", ")", "(", "/", ";",
+            "'", "@", ":", "-", "\"", "+", "%", "&");
+    // Punctuation more keys for tablet form factor.
+    protected static final ExpectedKey[] TABLET_PUNCTUATION_MORE_KEYS = joinKeys(
+            ",", "'", "#", ")", "(", "/", ";",
+            "@", ":", "-", "\"", "+", "%", "&");
 }
diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetSubtypesCountTests.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/KeyboardLayoutSetSubtypesCountTests.java
similarity index 91%
rename from tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetSubtypesCountTests.java
rename to tests/src/com/android/inputmethod/keyboard/layout/tests/KeyboardLayoutSetSubtypesCountTests.java
index 25bac9d..2134eb5 100644
--- a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetSubtypesCountTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/KeyboardLayoutSetSubtypesCountTests.java
@@ -14,19 +14,21 @@
  * limitations under the License.
  */
 
-package com.android.inputmethod.keyboard;
+package com.android.inputmethod.keyboard.layout.tests;
 
 import android.test.suitebuilder.annotation.SmallTest;
 import android.view.inputmethod.InputMethodSubtype;
 
+import com.android.inputmethod.keyboard.KeyboardLayoutSetTestsBase;
+import com.android.inputmethod.keyboard.KeyboardTheme;
 import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
 
 import java.util.ArrayList;
 
 @SmallTest
 public class KeyboardLayoutSetSubtypesCountTests extends KeyboardLayoutSetTestsBase {
-    private static final int NUMBER_OF_SUBTYPES = 81;
-    private static final int NUMBER_OF_ASCII_CAPABLE_SUBTYPES = 49;
+    private static final int NUMBER_OF_SUBTYPES = 78;
+    private static final int NUMBER_OF_ASCII_CAPABLE_SUBTYPES = 47;
     private static final int NUMBER_OF_PREDEFINED_ADDITIONAL_SUBTYPES = 2;
 
     @Override
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsAfrikaans.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsAfrikaans.java
index cd22598..f534cd6 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsAfrikaans.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsAfrikaans.java
@@ -19,8 +19,8 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
-import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.Qwerty;
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
 import java.util.Locale;
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsArabic.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsArabic.java
index fd76708..a9be163 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsArabic.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsArabic.java
@@ -19,7 +19,6 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.Arabic;
-import com.android.inputmethod.keyboard.layout.Arabic.ArabicCustomizer;
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 
 import java.util.Locale;
@@ -30,7 +29,7 @@
 @SmallTest
 public class TestsArabic extends LayoutTestsBase {
     private static final Locale LOCALE = new Locale("ar");
-    private static final LayoutBase LAYOUT = new Arabic(new ArabicCustomizer(LOCALE));
+    private static final LayoutBase LAYOUT = new Arabic(LOCALE);
 
     @Override
     LayoutBase getLayout() { return LAYOUT; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsArmenianAMPhonetic.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsArmenianAMPhonetic.java
index 327e943..8ef97d2 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsArmenianAMPhonetic.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsArmenianAMPhonetic.java
@@ -19,7 +19,6 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.ArmenianPhonetic;
-import com.android.inputmethod.keyboard.layout.ArmenianPhonetic.ArmenianPhoneticCustomizer;
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 
 import java.util.Locale;
@@ -30,8 +29,7 @@
 @SmallTest
 public final class TestsArmenianAMPhonetic extends LayoutTestsBase {
     private static final Locale LOCALE = new Locale("hy", "AM");
-    private static final LayoutBase LAYOUT = new ArmenianPhonetic(
-            new ArmenianPhoneticCustomizer(LOCALE));
+    private static final LayoutBase LAYOUT = new ArmenianPhonetic(LOCALE);
 
     @Override
     LayoutBase getLayout() { return LAYOUT; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsAzerbaijaniAZ.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsAzerbaijaniAZ.java
index 8e0e2bb..a10023c 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsAzerbaijaniAZ.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsAzerbaijaniAZ.java
@@ -20,6 +20,7 @@
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Qwerty;
+import com.android.inputmethod.keyboard.layout.customizer.TurkicCustomizer;
 
 import java.util.Locale;
 
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBasqueES.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBasqueES.java
index bef18c5..103fac1 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBasqueES.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBasqueES.java
@@ -19,8 +19,9 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
-import com.android.inputmethod.keyboard.layout.LayoutBase.EuroCustomizer;
 import com.android.inputmethod.keyboard.layout.Spanish;
+import com.android.inputmethod.keyboard.layout.customizer.EuroCustomizer;
+import com.android.inputmethod.keyboard.layout.customizer.SpanishCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
 import java.util.Locale;
@@ -39,7 +40,7 @@
     private static class BasqueESCustomizer extends EuroCustomizer {
         private final SpanishCustomizer mSpanishCustomizer;
 
-        public BasqueESCustomizer(final Locale locale) {
+        BasqueESCustomizer(final Locale locale) {
             super(locale);
             mSpanishCustomizer = new SpanishCustomizer(locale);
         }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBelarusianBY.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBelarusianBY.java
index c5238d5..0b23933 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBelarusianBY.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBelarusianBY.java
@@ -19,9 +19,9 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.EastSlavic;
-import com.android.inputmethod.keyboard.layout.EastSlavic.EastSlavicCustomizer;
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.customizer.EastSlavicCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
@@ -39,7 +39,7 @@
     LayoutBase getLayout() { return LAYOUT; }
 
     private static class BelarusianBYCustomizer extends EastSlavicCustomizer {
-        public BelarusianBYCustomizer(final Locale locale) { super(locale); }
+        BelarusianBYCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKey[] getDoubleQuoteMoreKeys() {
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBengaliBD.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBengaliBD.java
new file mode 100644
index 0000000..c39a392
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBengaliBD.java
@@ -0,0 +1,55 @@
+/*
+ * 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.layout.tests;
+
+import android.test.suitebuilder.annotation.Suppress;
+
+import com.android.inputmethod.keyboard.layout.BengaliAkkhor;
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.customizer.BengaliCustomizer;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+
+import java.util.Locale;
+
+/**
+ * bn_BD: Bengali (Bangladesh)/bengali_akkhor
+ */
+@Suppress
+public final class TestsBengaliBD extends LayoutTestsBase {
+    private static final Locale LOCALE = new Locale("bn", "BD");
+    private static final LayoutBase LAYOUT = new BengaliAkkhor(new BengaliBDCustomzier(LOCALE));
+
+    @Override
+    LayoutBase getLayout() { return LAYOUT; }
+
+    private static class BengaliBDCustomzier extends BengaliCustomizer {
+        BengaliBDCustomzier(final Locale locale) { super(locale); }
+
+        @Override
+        public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
+            return isPhone ? EMPTY_KEYS : EXCLAMATION_AND_QUESTION_MARKS;
+        }
+
+        @Override
+        public ExpectedKey getCurrencyKey() { return CURRENCY_RUPEE; }
+
+        // U+09F3: "৳" BENGALI RUPEE SIGN
+        private static final ExpectedKey CURRENCY_RUPEE = key("\u09F3",
+                Symbols.CURRENCY_GENERIC_MORE_KEYS);
+    }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBengaliIN.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBengaliIN.java
index d642632..022b085 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBengaliIN.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBengaliIN.java
@@ -19,9 +19,9 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.Bengali;
-import com.android.inputmethod.keyboard.layout.Bengali.BengaliCustomizer;
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.customizer.BengaliCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 
 import java.util.Locale;
@@ -38,7 +38,10 @@
     LayoutBase getLayout() { return LAYOUT; }
 
     private static class BengaliINCustomzier extends BengaliCustomizer {
-        public BengaliINCustomzier(final Locale locale) { super(locale); }
+        BengaliINCustomzier(final Locale locale) { super(locale); }
+
+        @Override
+        public ExpectedKey[] getLeftShiftKeys(final boolean isPhone) { return EMPTY_KEYS; }
 
         @Override
         public ExpectedKey getCurrencyKey() { return CURRENCY_RUPEE; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBulgarian.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBulgarian.java
index ded8d72..1530827 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBulgarian.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBulgarian.java
@@ -19,7 +19,6 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.Bulgarian;
-import com.android.inputmethod.keyboard.layout.Bulgarian.BulgarianCustomizer;
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 
 import java.util.Locale;
@@ -30,7 +29,7 @@
 @SmallTest
 public final class TestsBulgarian extends LayoutTestsBase {
     private static final Locale LOCALE = new Locale("bg");
-    private static final LayoutBase LAYOUT = new Bulgarian(new BulgarianCustomizer(LOCALE));
+    private static final LayoutBase LAYOUT = new Bulgarian(LOCALE);
 
     @Override
     LayoutBase getLayout() { return LAYOUT; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBulgarianBds.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBulgarianBds.java
index 22b2011..558b1d4 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBulgarianBds.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBulgarianBds.java
@@ -19,7 +19,6 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.BulgarianBds;
-import com.android.inputmethod.keyboard.layout.BulgarianBds.BulgarianBdsCustomizer;
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 
 import java.util.Locale;
@@ -30,7 +29,7 @@
 @SmallTest
 public final class TestsBulgarianBds extends LayoutTestsBase {
     private static final Locale LOCALE = new Locale("bg");
-    private static final LayoutBase LAYOUT = new BulgarianBds(new BulgarianBdsCustomizer(LOCALE));
+    private static final LayoutBase LAYOUT = new BulgarianBds(LOCALE);
 
     @Override
     LayoutBase getLayout() { return LAYOUT; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsCatalan.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsCatalan.java
index 151a0a6..a323ffd 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsCatalan.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsCatalan.java
@@ -19,8 +19,8 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
-import com.android.inputmethod.keyboard.layout.LayoutBase.EuroCustomizer;
 import com.android.inputmethod.keyboard.layout.Spanish;
+import com.android.inputmethod.keyboard.layout.customizer.EuroCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
@@ -38,20 +38,20 @@
     LayoutBase getLayout() { return LAYOUT; }
 
     private static class CatalanCustomizer extends EuroCustomizer {
-        public CatalanCustomizer(final Locale locale) { super(locale); }
+        CatalanCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKey[] getPunctuationMoreKeys(final boolean isPhone) {
-            return isPhone ? PHONE_PUNCTUATION_MORE_KEYS
-                    : TABLET_PUNCTUATION_MORE_KEYS;
+            return isPhone ? CATALAN_PHONE_PUNCTUATION_MORE_KEYS
+                    : CATALAN_TABLET_PUNCTUATION_MORE_KEYS;
         }
 
         // U+00B7: "·" MIDDLE DOT
-        private static final ExpectedKey[] PHONE_PUNCTUATION_MORE_KEYS = joinKeys(
+        private static final ExpectedKey[] CATALAN_PHONE_PUNCTUATION_MORE_KEYS = joinKeys(
                 ",", "?", "!", "\u00B7", "#", ")", "(", "/", ";",
                 "'", "@", ":", "-", "\"", "+", "%", "&");
 
-        private static final ExpectedKey[] TABLET_PUNCTUATION_MORE_KEYS = joinKeys(
+        private static final ExpectedKey[] CATALAN_TABLET_PUNCTUATION_MORE_KEYS = joinKeys(
                 ",", "'", "\u00B7", "#", ")", "(", "/", ";",
                 "@", ":", "-", "\"", "+", "%", "&");
 
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsCroatian.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsCroatian.java
index 8575ef2..d8af75b 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsCroatian.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsCroatian.java
@@ -19,9 +19,9 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
-import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.Qwertz;
 import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
@@ -39,7 +39,7 @@
     LayoutBase getLayout() { return LAYOUT; }
 
     private static class CroatianCustomizer extends LayoutCustomizer {
-        public CroatianCustomizer(final Locale locale) { super(locale); }
+        CroatianCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_L9R; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsCzech.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsCzech.java
index f479470..f05b8eb 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsCzech.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsCzech.java
@@ -19,9 +19,9 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
-import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.Qwertz;
 import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
@@ -39,7 +39,7 @@
     LayoutBase getLayout() { return LAYOUT; }
 
     private static class CzechCustomizer extends LayoutCustomizer {
-        public CzechCustomizer(final Locale locale) { super(locale); }
+        CzechCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_R9L; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDanish.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDanish.java
index bf43e52..fb7338d 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDanish.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDanish.java
@@ -20,6 +20,7 @@
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Nordic;
+import com.android.inputmethod.keyboard.layout.customizer.DanishCustomizer;
 
 import java.util.Locale;
 
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDanishQwertz.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDanishQwertz.java
index 886b3de..718bf8a 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDanishQwertz.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDanishQwertz.java
@@ -20,6 +20,7 @@
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Qwertz;
+import com.android.inputmethod.keyboard.layout.customizer.DanishCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
 import java.util.Locale;
@@ -36,9 +37,7 @@
     LayoutBase getLayout() { return LAYOUT; }
 
     private static class DanishQwertyCustomizer extends DanishCustomizer {
-        public DanishQwertyCustomizer(final Locale locale) {
-            super(locale);
-        }
+        DanishQwertyCustomizer(final Locale locale) { super(locale); }
 
         @Override
         protected void setNordicKeys(final ExpectedKeyboardBuilder builder) {
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDutch.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDutch.java
index 1730f66..12bd1b1 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDutch.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDutch.java
@@ -19,11 +19,8 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
-import com.android.inputmethod.keyboard.layout.LayoutBase.EuroCustomizer;
 import com.android.inputmethod.keyboard.layout.Qwerty;
-import com.android.inputmethod.keyboard.layout.Symbols;
-import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
-import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+import com.android.inputmethod.keyboard.layout.customizer.DutchCustomizer;
 
 import java.util.Locale;
 
@@ -37,70 +34,4 @@
 
     @Override
     LayoutBase getLayout() { return LAYOUT; }
-
-    static class DutchCustomizer extends EuroCustomizer {
-        public DutchCustomizer(final Locale locale) { super(locale); }
-
-        @Override
-        public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_L9R; }
-
-        @Override
-        public ExpectedKey[] getSingleQuoteMoreKeys() { return Symbols.SINGLE_QUOTES_L9R; }
-
-        @Override
-        public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
-            return builder
-                    // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
-                    // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
-                    // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
-                    // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
-                    // U+00E6: "æ" LATIN SMALL LETTER AE
-                    // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
-                    // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
-                    // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
-                    .setMoreKeysOf("a",
-                            "\u00E1", "\u00E4", "\u00E2", "\u00E0", "\u00E6", "\u00E3", "\u00E5",
-                            "\u0101")
-                    // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
-                    // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
-                    // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
-                    // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
-                    // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
-                    // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
-                    // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
-                    .setMoreKeysOf("e",
-                            "\u00E9", "\u00EB", "\u00EA", "\u00E8", "\u0119", "\u0117", "\u0113")
-                    // U+0133: "ij" LATIN SMALL LIGATURE IJ
-                    .setMoreKeysOf("y", "\u0133")
-                    // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
-                    // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
-                    // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
-                    // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
-                    // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
-                    .setMoreKeysOf("u", "\u00FA", "\u00FC", "\u00FB", "\u00F9", "\u016B")
-                    // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
-                    // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
-                    // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
-                    // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
-                    // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
-                    // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
-                    // U+0133: "ij" LATIN SMALL LIGATURE IJ
-                    .setMoreKeysOf("i",
-                            "\u00ED", "\u00EF", "\u00EC", "\u00EE", "\u012F", "\u012B", "\u0133")
-                    // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
-                    // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
-                    // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
-                    // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
-                    // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
-                    // U+0153: "œ" LATIN SMALL LIGATURE OE
-                    // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
-                    // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
-                    .setMoreKeysOf("o",
-                            "\u00F3", "\u00F6", "\u00F4", "\u00F2", "\u00F5", "\u0153", "\u00F8",
-                            "\u014D")
-                    // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
-                    // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
-                    .setMoreKeysOf("n", "\u00F1", "\u0144");
-        }
-    }
 }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDutchBE.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDutchBE.java
index 31adf7a..1464118 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDutchBE.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDutchBE.java
@@ -20,7 +20,7 @@
 
 import com.android.inputmethod.keyboard.layout.Azerty;
 import com.android.inputmethod.keyboard.layout.LayoutBase;
-import com.android.inputmethod.keyboard.layout.tests.TestsDutch.DutchCustomizer;
+import com.android.inputmethod.keyboard.layout.customizer.DutchCustomizer;
 
 import java.util.Locale;
 
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDvorakEmail.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDvorakEmail.java
index b25b846..3966ebc 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDvorakEmail.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDvorakEmail.java
@@ -25,8 +25,9 @@
 import com.android.inputmethod.keyboard.KeyboardLayoutSet;
 import com.android.inputmethod.keyboard.layout.Dvorak;
 import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.customizer.DvorakCustomizer.EnglishDvorakCustomizer;
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
-import com.android.inputmethod.keyboard.layout.tests.TestsEnglishDvorak.EnglishDvorakCustomizer;
 
 import java.util.Locale;
 
@@ -60,23 +61,23 @@
 
         @Override
         public ExpectedKey getEnterKey(final boolean isPhone) {
-            return isPhone ? LayoutBase.ENTER_KEY : super.getEnterKey(isPhone);
+            return isPhone ? ENTER_KEY : super.getEnterKey(isPhone);
         }
 
         @Override
         public ExpectedKey getEmojiKey(final boolean isPhone) {
-            return LayoutBase.DOMAIN_KEY;
+            return DOMAIN_KEY;
         }
 
         @Override
         public ExpectedKey[] getKeysLeftToSpacebar(final boolean isPhone) {
             return isPhone ? super.getKeysLeftToSpacebar(isPhone)
-                    : joinKeys(key("@", LayoutBase.SETTINGS_KEY));
+                    : joinKeys(key("@", SETTINGS_KEY));
         }
     }
 
     private static class DvorakEmail extends Dvorak {
-        public DvorakEmail(final LayoutCustomizer customizer) {
+        DvorakEmail(final LayoutCustomizer customizer) {
             super(customizer);
         }
 
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDvorakUrl.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDvorakUrl.java
index ba22f37..67e0d91 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDvorakUrl.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDvorakUrl.java
@@ -25,8 +25,9 @@
 import com.android.inputmethod.keyboard.KeyboardLayoutSet;
 import com.android.inputmethod.keyboard.layout.Dvorak;
 import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.customizer.DvorakCustomizer.EnglishDvorakCustomizer;
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
-import com.android.inputmethod.keyboard.layout.tests.TestsEnglishDvorak.EnglishDvorakCustomizer;
 
 import java.util.Locale;
 
@@ -54,31 +55,27 @@
     }
 
     private static class DvorakUrlCustomizer extends EnglishDvorakCustomizer {
-        DvorakUrlCustomizer(final Locale locale) {
-            super(locale);
-        }
+        DvorakUrlCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKey getEnterKey(final boolean isPhone) {
-            return isPhone ? LayoutBase.ENTER_KEY : super.getEnterKey(isPhone);
+            return isPhone ? ENTER_KEY : super.getEnterKey(isPhone);
         }
 
         @Override
         public ExpectedKey getEmojiKey(final boolean isPhone) {
-            return LayoutBase.DOMAIN_KEY;
+            return DOMAIN_KEY;
         }
 
         @Override
         public ExpectedKey[] getKeysLeftToSpacebar(final boolean isPhone) {
             return isPhone ? super.getKeysLeftToSpacebar(isPhone)
-                    : joinKeys(key("/", LayoutBase.SETTINGS_KEY));
+                    : joinKeys(key("/", SETTINGS_KEY));
         }
     }
 
     private static class DvorakEmail extends Dvorak {
-        public DvorakEmail(final LayoutCustomizer customizer) {
-            super(customizer);
-        }
+        DvorakEmail(final LayoutCustomizer customizer) { super(customizer); }
 
         @Override
         protected ExpectedKey getRow1_1Key(final boolean isPhone, final int elementId) {
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishDvorak.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishDvorak.java
index e647f8a..2b25f81 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishDvorak.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishDvorak.java
@@ -19,9 +19,8 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.Dvorak;
-import com.android.inputmethod.keyboard.layout.Dvorak.DvorakCustomizer;
 import com.android.inputmethod.keyboard.layout.LayoutBase;
-import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+import com.android.inputmethod.keyboard.layout.customizer.DvorakCustomizer.EnglishDvorakCustomizer;
 
 import java.util.Locale;
 
@@ -35,18 +34,4 @@
 
     @Override
     LayoutBase getLayout() { return LAYOUT; }
-
-    public static class EnglishDvorakCustomizer extends DvorakCustomizer {
-        private final EnglishCustomizer mEnglishCustomizer;
-
-        EnglishDvorakCustomizer(final Locale locale) {
-            super(locale);
-            mEnglishCustomizer = new EnglishCustomizer(locale);
-        }
-
-        @Override
-        public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
-            return mEnglishCustomizer.setAccentedLetters(builder);
-        }
-    }
 }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishIN.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishIN.java
index c80b250..54759ce 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishIN.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishIN.java
@@ -22,6 +22,7 @@
 import com.android.inputmethod.keyboard.layout.Qwerty;
 import com.android.inputmethod.keyboard.layout.Symbols;
 import com.android.inputmethod.keyboard.layout.SymbolsShifted;
+import com.android.inputmethod.keyboard.layout.customizer.EnglishCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 
 import java.util.Locale;
@@ -38,7 +39,7 @@
     LayoutBase getLayout() { return LAYOUT; }
 
     private static class EnglishINCustomizer extends EnglishCustomizer {
-        public EnglishINCustomizer(final Locale locale) { super(locale); }
+        EnglishINCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKey getCurrencyKey() { return CURRENCY_RUPEE; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishUK.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishUK.java
index c0dcbdc..714a600 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishUK.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishUK.java
@@ -21,6 +21,7 @@
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Qwerty;
 import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.customizer.EnglishCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 
 import java.util.Locale;
@@ -37,7 +38,7 @@
     LayoutBase getLayout() { return LAYOUT; }
 
     private static class EnglishUKCustomizer extends EnglishCustomizer {
-        public EnglishUKCustomizer(final Locale locale) { super(locale); }
+        EnglishUKCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKey getCurrencyKey() { return CURRENCY_POUND; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishUS.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishUS.java
index 6ea8f60..570ee9d 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishUS.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishUS.java
@@ -20,6 +20,7 @@
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Qwerty;
+import com.android.inputmethod.keyboard.layout.customizer.EnglishCustomizer;
 
 import java.util.Locale;
 
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEsperanto.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEsperanto.java
index 6a44187..a6792e2 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEsperanto.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEsperanto.java
@@ -19,8 +19,8 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
-import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.Spanish;
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
 import java.util.Locale;
@@ -37,7 +37,7 @@
     LayoutBase getLayout() { return LAYOUT; }
 
     private static class EsperantoCustomizer extends LayoutCustomizer {
-        public EsperantoCustomizer(final Locale locale) { super(locale); }
+        EsperantoCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEstonianEE.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEstonianEE.java
index 28c2eb3..8cb67e2 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEstonianEE.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEstonianEE.java
@@ -20,6 +20,7 @@
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Nordic;
+import com.android.inputmethod.keyboard.layout.customizer.EstonianEECustomizer;
 
 import java.util.Locale;
 
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEstonianEEQwerty.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEstonianEEQwerty.java
index ab8960b..fb7c243 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEstonianEEQwerty.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEstonianEEQwerty.java
@@ -20,6 +20,7 @@
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Qwerty;
+import com.android.inputmethod.keyboard.layout.customizer.EstonianEECustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
 import java.util.Locale;
@@ -36,9 +37,7 @@
     LayoutBase getLayout() { return LAYOUT; }
 
     private static class EstonianEEQwertyCustomizer extends EstonianEECustomizer {
-        public EstonianEEQwertyCustomizer(final Locale locale) {
-            super(locale);
-        }
+        EstonianEEQwertyCustomizer(final Locale locale) { super(locale); }
 
         @Override
         protected void setNordicKeys(final ExpectedKeyboardBuilder builder) {
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFinnish.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFinnish.java
index 05def96..3d42f30 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFinnish.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFinnish.java
@@ -20,6 +20,7 @@
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Nordic;
+import com.android.inputmethod.keyboard.layout.customizer.FinnishCustomizer;
 
 import java.util.Locale;
 
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFinnishQwerty.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFinnishQwerty.java
index c3df9d1..c81b2a2 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFinnishQwerty.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFinnishQwerty.java
@@ -20,6 +20,7 @@
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Qwerty;
+import com.android.inputmethod.keyboard.layout.customizer.FinnishCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
 import java.util.Locale;
@@ -36,9 +37,7 @@
     LayoutBase getLayout() { return LAYOUT; }
 
     private static class FinnishQwertyCustomizer extends FinnishCustomizer {
-        public FinnishQwertyCustomizer(final Locale locale) {
-            super(locale);
-        }
+        FinnishQwertyCustomizer(final Locale locale) { super(locale); }
 
         @Override
         protected void setNordicKeys(final ExpectedKeyboardBuilder builder) {
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrench.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrench.java
index 7ced1fb..ca663f4 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrench.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrench.java
@@ -20,8 +20,7 @@
 
 import com.android.inputmethod.keyboard.layout.Azerty;
 import com.android.inputmethod.keyboard.layout.LayoutBase;
-import com.android.inputmethod.keyboard.layout.LayoutBase.EuroCustomizer;
-import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.customizer.FrenchCustomizer.FrenchEuroCustomizer;
 
 import java.util.Locale;
 
@@ -35,21 +34,4 @@
 
     @Override
     LayoutBase getLayout() { return LAYOUT; }
-
-    static final class FrenchEuroCustomizer extends FrenchCustomizer {
-        private final EuroCustomizer mEuroCustomizer;
-
-        public FrenchEuroCustomizer(final Locale locale) {
-            super(locale);
-            mEuroCustomizer = new EuroCustomizer(locale);
-        }
-
-        @Override
-        public final ExpectedKey getCurrencyKey() { return mEuroCustomizer.getCurrencyKey(); }
-
-        @Override
-        public final ExpectedKey[] getOtherCurrencyKeys() {
-            return mEuroCustomizer.getOtherCurrencyKeys();
-        }
-    }
-}
\ No newline at end of file
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrenchCA.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrenchCA.java
index 9b3cd1e..12c94b1 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrenchCA.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrenchCA.java
@@ -20,6 +20,7 @@
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Qwerty;
+import com.android.inputmethod.keyboard.layout.customizer.FrenchCustomizer;
 
 import java.util.Locale;
 
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrenchCH.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrenchCH.java
index 2598aa3..2461157 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrenchCH.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrenchCH.java
@@ -20,6 +20,7 @@
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Swiss;
+import com.android.inputmethod.keyboard.layout.customizer.FrenchCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
 import java.util.Locale;
@@ -36,7 +37,7 @@
     LayoutBase getLayout() { return LAYOUT; }
 
     private static class FrenchCHCustomizer extends FrenchCustomizer {
-        public FrenchCHCustomizer(final Locale locale) { super(locale); }
+        FrenchCHCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrenchDvorak.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrenchDvorak.java
index 33d1445..4053063 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrenchDvorak.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrenchDvorak.java
@@ -19,11 +19,11 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.Dvorak;
-import com.android.inputmethod.keyboard.layout.Dvorak.DvorakCustomizer;
 import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.customizer.DvorakCustomizer;
+import com.android.inputmethod.keyboard.layout.customizer.FrenchCustomizer.FrenchEuroCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
-import com.android.inputmethod.keyboard.layout.tests.TestsFrench.FrenchEuroCustomizer;
 
 import java.util.Locale;
 
@@ -41,7 +41,7 @@
     private static class FrenchDvorakCustomizer extends DvorakCustomizer {
         private final FrenchEuroCustomizer mFrenchEuroCustomizer;
 
-        public FrenchDvorakCustomizer(final Locale locale) {
+        FrenchDvorakCustomizer(final Locale locale) {
             super(locale);
             mFrenchEuroCustomizer = new FrenchEuroCustomizer(locale);
         }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrenchQwertz.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrenchQwertz.java
index 6ab2870..67edace 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrenchQwertz.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsFrenchQwertz.java
@@ -20,7 +20,7 @@
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Qwertz;
-import com.android.inputmethod.keyboard.layout.tests.TestsFrench.FrenchEuroCustomizer;
+import com.android.inputmethod.keyboard.layout.customizer.FrenchCustomizer.FrenchEuroCustomizer;
 
 import java.util.Locale;
 
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGalicianES.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGalicianES.java
index 1472828..c37fff2 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGalicianES.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGalicianES.java
@@ -19,8 +19,9 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
-import com.android.inputmethod.keyboard.layout.LayoutBase.EuroCustomizer;
 import com.android.inputmethod.keyboard.layout.Spanish;
+import com.android.inputmethod.keyboard.layout.customizer.EuroCustomizer;
+import com.android.inputmethod.keyboard.layout.customizer.SpanishCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
 import java.util.Locale;
@@ -39,7 +40,7 @@
     private static class GalicianESCustomizer extends EuroCustomizer {
         private final SpanishCustomizer mSpanishCustomizer;
 
-        public GalicianESCustomizer(final Locale locale) {
+        GalicianESCustomizer(final Locale locale) {
             super(locale);
             mSpanishCustomizer = new SpanishCustomizer(locale);
         }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGeorgianGE.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGeorgianGE.java
index f25942f..f6e3080 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGeorgianGE.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGeorgianGE.java
@@ -19,7 +19,6 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.Georgian;
-import com.android.inputmethod.keyboard.layout.Georgian.GeorgianCustomizer;
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 
 import java.util.Locale;
@@ -30,7 +29,7 @@
 @SmallTest
 public final class TestsGeorgianGE extends LayoutTestsBase {
     private static final Locale LOCALE = new Locale("ka", "GE");
-    private static final LayoutBase LAYOUT = new Georgian(new GeorgianCustomizer(LOCALE));
+    private static final LayoutBase LAYOUT = new Georgian(LOCALE);
 
     @Override
     LayoutBase getLayout() { return LAYOUT; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGerman.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGerman.java
index 6f75711..52c5a06 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGerman.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGerman.java
@@ -19,9 +19,8 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
-import com.android.inputmethod.keyboard.layout.LayoutBase.EuroCustomizer;
 import com.android.inputmethod.keyboard.layout.Qwertz;
-import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
+import com.android.inputmethod.keyboard.layout.customizer.GermanCustomizer.GermanEuroCustomizer;
 
 import java.util.Locale;
 
@@ -35,21 +34,4 @@
 
     @Override
     LayoutBase getLayout() { return LAYOUT; }
-
-    static class GermanEuroCustomizer extends GermanCustomizer {
-        final EuroCustomizer mEuroCustomizer;
-
-        public GermanEuroCustomizer(final Locale locale) {
-            super(locale);
-            mEuroCustomizer = new EuroCustomizer(locale);
-        }
-
-        @Override
-        public ExpectedKey getCurrencyKey() { return mEuroCustomizer.getCurrencyKey(); }
-
-        @Override
-        public ExpectedKey[] getOtherCurrencyKeys() {
-            return mEuroCustomizer.getOtherCurrencyKeys();
-        }
-    }
 }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGermanCH.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGermanCH.java
index 7deb00b..10981d9 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGermanCH.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGermanCH.java
@@ -20,6 +20,7 @@
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Swiss;
+import com.android.inputmethod.keyboard.layout.customizer.GermanCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
 import java.util.Locale;
@@ -36,7 +37,7 @@
     LayoutBase getLayout() { return LAYOUT; }
 
     private static class GermanCHCustomizer extends GermanCustomizer {
-        public GermanCHCustomizer(final Locale locale) { super(locale); }
+        GermanCHCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGermanDvorak.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGermanDvorak.java
index b28d5cf..36d1cb8 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGermanDvorak.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGermanDvorak.java
@@ -19,10 +19,11 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.Dvorak;
-import com.android.inputmethod.keyboard.layout.Dvorak.DvorakCustomizer;
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Symbols;
 import com.android.inputmethod.keyboard.layout.SymbolsShifted;
+import com.android.inputmethod.keyboard.layout.customizer.DvorakCustomizer;
+import com.android.inputmethod.keyboard.layout.customizer.GermanCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
@@ -39,10 +40,10 @@
     @Override
     LayoutBase getLayout() { return LAYOUT; }
 
-    static class GermanDvorakCustomizer extends DvorakCustomizer {
-        final GermanCustomizer mGermanCustomizer;
+    private static class GermanDvorakCustomizer extends DvorakCustomizer {
+        private final GermanCustomizer mGermanCustomizer;
 
-        public GermanDvorakCustomizer(final Locale locale) {
+        GermanDvorakCustomizer(final Locale locale) {
             super(locale);
             mGermanCustomizer = new GermanCustomizer(locale);
         }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGermanQwerty.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGermanQwerty.java
index 19ae5a3..6db942f 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGermanQwerty.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGermanQwerty.java
@@ -20,7 +20,7 @@
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Qwerty;
-import com.android.inputmethod.keyboard.layout.tests.TestsGerman.GermanEuroCustomizer;
+import com.android.inputmethod.keyboard.layout.customizer.GermanCustomizer.GermanEuroCustomizer;
 
 import java.util.Locale;
 
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGreek.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGreek.java
index 4acb119..fd21d52 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGreek.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsGreek.java
@@ -19,7 +19,6 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.Greek;
-import com.android.inputmethod.keyboard.layout.Greek.GreekCustomizer;
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 
 import java.util.Locale;
@@ -30,7 +29,7 @@
 @SmallTest
 public class TestsGreek extends LayoutTestsBase {
     private static final Locale LOCALE = new Locale("el");
-    private static final LayoutBase LAYOUT = new Greek(new GreekCustomizer(LOCALE));
+    private static final LayoutBase LAYOUT = new Greek(LOCALE);
 
     @Override
     LayoutBase getLayout() { return LAYOUT; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHebrew.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHebrew.java
index c0243a8..cc63048 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHebrew.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHebrew.java
@@ -19,7 +19,6 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.Hebrew;
-import com.android.inputmethod.keyboard.layout.Hebrew.HebrewCustomizer;
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 
 import java.util.Locale;
@@ -30,7 +29,7 @@
 @SmallTest
 public class TestsHebrew extends LayoutTestsBase {
     private static final Locale LOCALE = new Locale("iw");
-    private static final LayoutBase LAYOUT = new Hebrew(new HebrewCustomizer(LOCALE));
+    private static final LayoutBase LAYOUT = new Hebrew(LOCALE);
 
     @Override
     LayoutBase getLayout() { return LAYOUT; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHindi.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHindi.java
index 84053b5..53652fc 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHindi.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHindi.java
@@ -19,8 +19,8 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.Hindi;
-import com.android.inputmethod.keyboard.layout.Hindi.HindiCustomizer;
 import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.customizer.HindiCustomizer;
 
 import java.util.Locale;
 
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHindiCompact.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHindiCompact.java
index 2e676df..cfe3e9e 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHindiCompact.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHindiCompact.java
@@ -19,7 +19,6 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.HindiCompact;
-import com.android.inputmethod.keyboard.layout.HindiCompact.HindiCompactCustomizer;
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 
 import java.util.Locale;
@@ -30,7 +29,7 @@
 @SmallTest
 public final class TestsHindiCompact extends LayoutTestsBase {
     private static final Locale LOCALE = new Locale("hi");
-    private static final LayoutBase LAYOUT = new HindiCompact(new HindiCompactCustomizer(LOCALE));
+    private static final LayoutBase LAYOUT = new HindiCompact(LOCALE);
 
     @Override
     LayoutBase getLayout() { return LAYOUT; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHinglish.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHinglish.java
index 2a6e46f..a8e8723 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHinglish.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHinglish.java
@@ -16,13 +16,13 @@
 
 package com.android.inputmethod.keyboard.layout.tests;
 
-import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.Suppress;
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
-import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.Qwerty;
 import com.android.inputmethod.keyboard.layout.Symbols;
 import com.android.inputmethod.keyboard.layout.SymbolsShifted;
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 
 import java.util.Locale;
@@ -30,7 +30,7 @@
 /*
  * hi_ZZ: Hinglish/qwerty
  */
-@SmallTest
+@Suppress
 public final class TestsHinglish extends LayoutTestsBase {
     private static final Locale LOCALE = new Locale("hi", "ZZ");
     private static final LayoutBase LAYOUT = new Qwerty(new HinglishCustomizer(LOCALE));
@@ -39,7 +39,7 @@
     LayoutBase getLayout() { return LAYOUT; }
 
     private static class HinglishCustomizer extends LayoutCustomizer {
-        public HinglishCustomizer(final Locale locale) { super(locale); }
+        HinglishCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKey getCurrencyKey() { return CURRENCY_RUPEE; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHungarian.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHungarian.java
index efc95dc..a32e2b5 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHungarian.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHungarian.java
@@ -19,9 +19,9 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
-import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.Qwertz;
 import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
@@ -39,7 +39,7 @@
     LayoutBase getLayout() { return LAYOUT; }
 
     private static class HungarianCustomizer extends LayoutCustomizer {
-        public HungarianCustomizer(final Locale locale) { super(locale); }
+        HungarianCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_L9R; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsIcelandic.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsIcelandic.java
index 62b111e..8eabf18 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsIcelandic.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsIcelandic.java
@@ -19,9 +19,9 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
-import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.Qwerty;
 import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
@@ -39,7 +39,7 @@
     LayoutBase getLayout() { return LAYOUT; }
 
     private static class IcelandicCustomizer extends LayoutCustomizer {
-        public IcelandicCustomizer(final Locale locale) { super(locale); }
+        IcelandicCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_R9L; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsIndonesian.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsIndonesian.java
index 9b23bfe..8371171 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsIndonesian.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsIndonesian.java
@@ -19,8 +19,8 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
-import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.Qwerty;
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 
 import java.util.Locale;
 
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsItalian.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsItalian.java
index f3c610c..2acc967 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsItalian.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsItalian.java
@@ -19,8 +19,9 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
-import com.android.inputmethod.keyboard.layout.LayoutBase.EuroCustomizer;
 import com.android.inputmethod.keyboard.layout.Qwerty;
+import com.android.inputmethod.keyboard.layout.customizer.EuroCustomizer;
+import com.android.inputmethod.keyboard.layout.customizer.ItalianCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
 import java.util.Locale;
@@ -39,7 +40,7 @@
     private static class ItalianITCustomizer extends EuroCustomizer {
         private final ItalianCustomizer mItalianCustomizer;
 
-        public ItalianITCustomizer(final Locale locale) {
+        ItalianITCustomizer(final Locale locale) {
             super(locale);
             mItalianCustomizer = new ItalianCustomizer(locale);
         }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsItalianCH.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsItalianCH.java
index d32f9e9..f98545a 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsItalianCH.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsItalianCH.java
@@ -20,6 +20,7 @@
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Swiss;
+import com.android.inputmethod.keyboard.layout.customizer.ItalianCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
 import java.util.Locale;
@@ -36,7 +37,7 @@
     LayoutBase getLayout() { return LAYOUT; }
 
     private static class ItalianCHCustomizer extends ItalianCustomizer {
-        public ItalianCHCustomizer(final Locale locale) { super(locale); }
+        ItalianCHCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsKannadaIN.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsKannadaIN.java
index d1866e8..a0ff1d0 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsKannadaIN.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsKannadaIN.java
@@ -19,7 +19,6 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.Kannada;
-import com.android.inputmethod.keyboard.layout.Kannada.KannadaCustomizer;
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 
 import java.util.Locale;
@@ -30,7 +29,7 @@
 @SmallTest
 public final class TestsKannadaIN extends LayoutTestsBase {
     private static final Locale LOCALE = new Locale("kn", "IN");
-    private static final LayoutBase LAYOUT = new Kannada(new KannadaCustomizer(LOCALE));
+    private static final LayoutBase LAYOUT = new Kannada(LOCALE);
 
     @Override
     LayoutBase getLayout() { return LAYOUT; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsKazakh.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsKazakh.java
index d255a0f..66ece29 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsKazakh.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsKazakh.java
@@ -19,8 +19,8 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.EastSlavic;
-import com.android.inputmethod.keyboard.layout.EastSlavic.EastSlavicCustomizer;
 import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.customizer.EastSlavicCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
 import java.util.Locale;
@@ -37,7 +37,7 @@
     LayoutBase getLayout() { return LAYOUT; }
 
     private static class KazakhCustomizer extends EastSlavicCustomizer {
-        public KazakhCustomizer(final Locale locale) { super(locale); }
+        KazakhCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsKhmerKH.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsKhmerKH.java
index df2f40d..dde9b87 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsKhmerKH.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsKhmerKH.java
@@ -19,7 +19,6 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.Khmer;
-import com.android.inputmethod.keyboard.layout.Khmer.KhmerCustomizer;
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 
 import java.util.Locale;
@@ -30,7 +29,7 @@
 @SmallTest
 public final class TestsKhmerKH extends LayoutTestsBase {
     private static final Locale LOCALE = new Locale("km", "KH");
-    private static final LayoutBase LAYOUT = new Khmer(new KhmerCustomizer(LOCALE));
+    private static final LayoutBase LAYOUT = new Khmer(LOCALE);
 
     @Override
     LayoutBase getLayout() { return LAYOUT; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsKyrgyz.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsKyrgyz.java
index 9797b4b..3faf2f6 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsKyrgyz.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsKyrgyz.java
@@ -19,8 +19,8 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.EastSlavic;
-import com.android.inputmethod.keyboard.layout.EastSlavic.EastSlavicCustomizer;
 import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.customizer.EastSlavicCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
 import java.util.Locale;
@@ -37,7 +37,7 @@
     LayoutBase getLayout() { return LAYOUT; }
 
     private static class KyrgyzCustomizer extends EastSlavicCustomizer {
-        public KyrgyzCustomizer(final Locale locale) { super(locale); }
+        KyrgyzCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsLaoLA.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsLaoLA.java
index 34ad1fb..ffa3372 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsLaoLA.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsLaoLA.java
@@ -19,7 +19,6 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.Lao;
-import com.android.inputmethod.keyboard.layout.Lao.LaoCustomizer;
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 
 import java.util.Locale;
@@ -30,7 +29,7 @@
 @SmallTest
 public final class TestsLaoLA extends LayoutTestsBase {
     private static final Locale LOCALE = new Locale("lo", "LA");
-    private static final LayoutBase LAYOUT = new Lao(new LaoCustomizer(LOCALE));
+    private static final LayoutBase LAYOUT = new Lao(LOCALE);
 
     @Override
     LayoutBase getLayout() { return LAYOUT; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsLatvian.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsLatvian.java
index dc1736c..dbab16a 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsLatvian.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsLatvian.java
@@ -19,9 +19,9 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
-import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.Qwerty;
 import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
@@ -39,7 +39,7 @@
     LayoutBase getLayout() { return LAYOUT; }
 
     private static class LatvianCustomizer extends LayoutCustomizer {
-        public LatvianCustomizer(final Locale locale) { super(locale); }
+        LatvianCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_R9L; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsLithuanian.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsLithuanian.java
index 55ac37a..248014a 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsLithuanian.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsLithuanian.java
@@ -19,9 +19,9 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
-import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.Qwerty;
 import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
@@ -39,7 +39,7 @@
     LayoutBase getLayout() { return LAYOUT; }
 
     private static class LithuanianCustomizer extends LayoutCustomizer {
-        public LithuanianCustomizer(final Locale locale) { super(locale); }
+        LithuanianCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_R9L; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMacedonian.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMacedonian.java
index 1d7d856..46b1c39 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMacedonian.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMacedonian.java
@@ -20,8 +20,8 @@
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.SouthSlavic;
-import com.android.inputmethod.keyboard.layout.SouthSlavic.SouthSlavicLayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.customizer.SouthSlavicLayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
@@ -39,7 +39,7 @@
     LayoutBase getLayout() { return LAYOUT; }
 
     private static class MacedonianCustomizer extends SouthSlavicLayoutCustomizer {
-        public MacedonianCustomizer(final Locale locale) { super(locale); }
+        MacedonianCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_R9L; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMalayMY.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMalayMY.java
index 9792af9..a6e6cd5 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMalayMY.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMalayMY.java
@@ -19,8 +19,8 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
-import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.Qwerty;
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 
 import java.util.Locale;
 
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMalayalamIN.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMalayalamIN.java
index b494ad3..e3fcb53 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMalayalamIN.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMalayalamIN.java
@@ -20,7 +20,6 @@
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Malayalam;
-import com.android.inputmethod.keyboard.layout.Malayalam.MalayalamCustomizer;
 
 import java.util.Locale;
 
@@ -30,7 +29,7 @@
 @SmallTest
 public final class TestsMalayalamIN extends LayoutTestsBase {
     private static final Locale LOCALE = new Locale("ml", "IN");
-    private static final LayoutBase LAYOUT = new Malayalam(new MalayalamCustomizer(LOCALE));
+    private static final LayoutBase LAYOUT = new Malayalam(LOCALE);
 
     @Override
     LayoutBase getLayout() { return LAYOUT; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMarathiIN.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMarathiIN.java
index b937629..3e54f6e 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMarathiIN.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMarathiIN.java
@@ -20,7 +20,6 @@
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Marathi;
-import com.android.inputmethod.keyboard.layout.Marathi.MarathiCustomizer;
 
 import java.util.Locale;
 
@@ -30,7 +29,7 @@
 @SmallTest
 public final class TestsMarathiIN extends LayoutTestsBase {
     private static final Locale LOCALE = new Locale("mr", "IN");
-    private static final LayoutBase LAYOUT = new Marathi(new MarathiCustomizer(LOCALE));
+    private static final LayoutBase LAYOUT = new Marathi(LOCALE);
 
     @Override
     LayoutBase getLayout() { return LAYOUT; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMongolianMN.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMongolianMN.java
index e28e962..6dafe77 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMongolianMN.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMongolianMN.java
@@ -20,7 +20,6 @@
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Mongolian;
-import com.android.inputmethod.keyboard.layout.Mongolian.MongolianMNCustomizer;
 
 import java.util.Locale;
 
@@ -30,7 +29,7 @@
 @SmallTest
 public final class TestsMongolianMN extends LayoutTestsBase {
     private static final Locale LOCALE = new Locale("mn", "MN");
-    private static final LayoutBase LAYOUT = new Mongolian(new MongolianMNCustomizer(LOCALE));
+    private static final LayoutBase LAYOUT = new Mongolian(LOCALE);
 
     @Override
     LayoutBase getLayout() { return LAYOUT; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMyanmarMM.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMyanmarMM.java
index e6d3b3b..18baa61 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMyanmarMM.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMyanmarMM.java
@@ -16,21 +16,20 @@
 
 package com.android.inputmethod.keyboard.layout.tests;
 
-import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.Suppress;
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Myanmar;
-import com.android.inputmethod.keyboard.layout.Myanmar.MyanmarCustomizer;
 
 import java.util.Locale;
 
 /**
  * my_MM: Myanmar (Myanmar)/myanmar
  */
-@SmallTest
+@Suppress
 public final class TestsMyanmarMM extends LayoutTestsBase {
     private static final Locale LOCALE = new Locale("my", "MM");
-    private static final LayoutBase LAYOUT = new Myanmar(new MyanmarCustomizer(LOCALE));
+    private static final LayoutBase LAYOUT = new Myanmar(LOCALE);
 
     @Override
     LayoutBase getLayout() { return LAYOUT; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNepaliRomanized.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNepaliRomanized.java
index 971976a..f646db3 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNepaliRomanized.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNepaliRomanized.java
@@ -20,7 +20,6 @@
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.NepaliRomanized;
-import com.android.inputmethod.keyboard.layout.NepaliRomanized.NepaliRomanizedCustomizer;
 
 import java.util.Locale;
 
@@ -30,8 +29,7 @@
 @SmallTest
 public final class TestsNepaliRomanized extends LayoutTestsBase {
     private static final Locale LOCALE = new Locale("ne", "NP");
-    private static final LayoutBase LAYOUT = new NepaliRomanized(
-            new NepaliRomanizedCustomizer(LOCALE));
+    private static final LayoutBase LAYOUT = new NepaliRomanized(LOCALE);
 
     @Override
     LayoutBase getLayout() { return LAYOUT; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNepaliTraditional.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNepaliTraditional.java
index 724c430..99d87b1 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNepaliTraditional.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNepaliTraditional.java
@@ -20,7 +20,6 @@
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.NepaliTraditional;
-import com.android.inputmethod.keyboard.layout.NepaliTraditional.NepaliTraditionalCustomizer;
 
 import java.util.Locale;
 
@@ -30,8 +29,7 @@
 @SmallTest
 public final class TestsNepaliTraditional extends LayoutTestsBase {
     private static final Locale LOCALE = new Locale("ne", "NP");
-    private static final LayoutBase LAYOUT = new NepaliTraditional(
-            new NepaliTraditionalCustomizer(LOCALE));
+    private static final LayoutBase LAYOUT = new NepaliTraditional(LOCALE);
 
     @Override
     LayoutBase getLayout() { return LAYOUT; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguage.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguage.java
index 3ed6315..149d520 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguage.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguage.java
@@ -20,6 +20,7 @@
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Qwerty;
+import com.android.inputmethod.keyboard.layout.customizer.NoLanguageCustomizer;
 
 import java.util.Locale;
 
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguageColemak.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguageColemak.java
index 8d627e3..979947f 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguageColemak.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguageColemak.java
@@ -20,7 +20,8 @@
 
 import com.android.inputmethod.keyboard.layout.Colemak;
 import com.android.inputmethod.keyboard.layout.LayoutBase;
-import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
+import com.android.inputmethod.keyboard.layout.customizer.NoLanguageCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
 import java.util.Locale;
@@ -39,7 +40,7 @@
     private static class NoLanguageColemakCustomizer extends LayoutCustomizer {
         private final NoLanguageCustomizer mNoLanguageCustomizer;
 
-        public NoLanguageColemakCustomizer(final Locale locale) {
+        NoLanguageColemakCustomizer(final Locale locale) {
             super(locale);
             mNoLanguageCustomizer = new NoLanguageCustomizer(locale);
         }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguageDvorak.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguageDvorak.java
index 9bf47ed..5423193 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguageDvorak.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguageDvorak.java
@@ -19,8 +19,9 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.Dvorak;
-import com.android.inputmethod.keyboard.layout.Dvorak.DvorakCustomizer;
 import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.customizer.DvorakCustomizer;
+import com.android.inputmethod.keyboard.layout.customizer.NoLanguageCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
 import java.util.Locale;
@@ -39,7 +40,7 @@
     private static class NoLanguageDvorakCustomizer extends DvorakCustomizer {
         private final NoLanguageCustomizer mNoLanguageCustomizer;
 
-        public NoLanguageDvorakCustomizer(final Locale locale) {
+        NoLanguageDvorakCustomizer(final Locale locale) {
             super(locale);
             mNoLanguageCustomizer = new NoLanguageCustomizer(locale);
         }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguagePcQwerty.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguagePcQwerty.java
index cd8d43c..20b587b 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguagePcQwerty.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNoLanguagePcQwerty.java
@@ -20,7 +20,8 @@
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.PcQwerty;
-import com.android.inputmethod.keyboard.layout.PcQwerty.PcQwertyCustomizer;
+import com.android.inputmethod.keyboard.layout.customizer.NoLanguageCustomizer;
+import com.android.inputmethod.keyboard.layout.customizer.PcQwertyCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
 import java.util.Locale;
@@ -39,7 +40,7 @@
     private static class NoLanguagePcQwertyCustomizer extends PcQwertyCustomizer {
         private final NoLanguageCustomizer mNoLanguageCustomizer;
 
-        public NoLanguagePcQwertyCustomizer(final Locale locale) {
+        NoLanguagePcQwertyCustomizer(final Locale locale) {
             super(locale);
             mNoLanguageCustomizer = new NoLanguageCustomizer(locale);
         }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNorwegian.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNorwegian.java
index 6c1921a..910512c 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNorwegian.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNorwegian.java
@@ -20,6 +20,7 @@
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Nordic;
+import com.android.inputmethod.keyboard.layout.customizer.NorwegianCustomizer;
 
 import java.util.Locale;
 
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNorwegianColemak.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNorwegianColemak.java
index a481796..689c38e 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNorwegianColemak.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsNorwegianColemak.java
@@ -20,6 +20,7 @@
 
 import com.android.inputmethod.keyboard.layout.Colemak;
 import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.customizer.NorwegianCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
 import java.util.Locale;
@@ -36,9 +37,7 @@
     LayoutBase getLayout() { return LAYOUT; }
 
     private static class NorwegianColemakCustomizer extends NorwegianCustomizer {
-        public NorwegianColemakCustomizer(final Locale locale) {
-            super(locale);
-        }
+        NorwegianColemakCustomizer(final Locale locale) { super(locale); }
 
         @Override
         protected void setNordicKeys(final ExpectedKeyboardBuilder builder) {
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsPersian.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsPersian.java
index b7d75c9..8ea8075 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsPersian.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsPersian.java
@@ -19,7 +19,6 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.Farsi;
-import com.android.inputmethod.keyboard.layout.Farsi.FarsiCustomizer;
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 
 import java.util.Locale;
@@ -30,7 +29,7 @@
 @SmallTest
 public class TestsPersian extends LayoutTestsBase {
     private static final Locale LOCALE = new Locale("fa");
-    private static final LayoutBase LAYOUT = new Farsi(new FarsiCustomizer(LOCALE));
+    private static final LayoutBase LAYOUT = new Farsi(LOCALE);
 
     @Override
     LayoutBase getLayout() { return LAYOUT; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsPolish.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsPolish.java
index 04f88c3..4f1170a 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsPolish.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsPolish.java
@@ -19,9 +19,9 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
-import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.Qwerty;
 import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
@@ -39,7 +39,7 @@
     LayoutBase getLayout() { return LAYOUT; }
 
     private static class PolishCustomizer extends LayoutCustomizer {
-        public PolishCustomizer(final Locale locale) { super(locale); }
+        PolishCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_L9R; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsPortugueseBR.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsPortugueseBR.java
index 8a984a7..7fadaac 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsPortugueseBR.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsPortugueseBR.java
@@ -20,6 +20,7 @@
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Qwerty;
+import com.android.inputmethod.keyboard.layout.customizer.PortugueseCustomizer;
 
 import java.util.Locale;
 
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsPortuguesePT.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsPortuguesePT.java
index e15e811..5936e8d 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsPortuguesePT.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsPortuguesePT.java
@@ -19,8 +19,9 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
-import com.android.inputmethod.keyboard.layout.LayoutBase.EuroCustomizer;
 import com.android.inputmethod.keyboard.layout.Qwerty;
+import com.android.inputmethod.keyboard.layout.customizer.EuroCustomizer;
+import com.android.inputmethod.keyboard.layout.customizer.PortugueseCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 
 import java.util.Locale;
@@ -39,7 +40,7 @@
     private static class PortuguesePTCustomizer extends PortugueseCustomizer {
         private final EuroCustomizer mEuroCustomizer;
 
-        public PortuguesePTCustomizer(final Locale locale) {
+        PortuguesePTCustomizer(final Locale locale) {
             super(locale);
             mEuroCustomizer = new EuroCustomizer(locale);
         }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsQwertyEmail.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsQwertyEmail.java
index f898632..cc204de 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsQwertyEmail.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsQwertyEmail.java
@@ -24,6 +24,7 @@
 import com.android.inputmethod.keyboard.KeyboardLayoutSet;
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Qwerty;
+import com.android.inputmethod.keyboard.layout.customizer.EnglishCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 
 import java.util.Locale;
@@ -52,23 +53,21 @@
     }
 
     private static class EnglishEmailCustomizer extends EnglishCustomizer {
-        EnglishEmailCustomizer(final Locale locale) {
-            super(locale);
-        }
+        EnglishEmailCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKey getEnterKey(final boolean isPhone) {
-            return isPhone ? LayoutBase.ENTER_KEY : super.getEnterKey(isPhone);
+            return isPhone ? ENTER_KEY : super.getEnterKey(isPhone);
         }
 
         @Override
         public ExpectedKey getEmojiKey(final boolean isPhone) {
-            return LayoutBase.DOMAIN_KEY;
+            return DOMAIN_KEY;
         }
 
         @Override
         public ExpectedKey[] getKeysLeftToSpacebar(final boolean isPhone) {
-            return joinKeys(key("@", LayoutBase.SETTINGS_KEY));
+            return joinKeys(key("@", SETTINGS_KEY));
         }
     }
 }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsQwertyUrl.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsQwertyUrl.java
index 0b69c7b..acd09d2 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsQwertyUrl.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsQwertyUrl.java
@@ -24,6 +24,7 @@
 import com.android.inputmethod.keyboard.KeyboardLayoutSet;
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Qwerty;
+import com.android.inputmethod.keyboard.layout.customizer.EnglishCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 
 import java.util.Locale;
@@ -52,23 +53,21 @@
     }
 
     private static class EnglishUrlCustomizer extends EnglishCustomizer {
-        EnglishUrlCustomizer(final Locale locale) {
-            super(locale);
-        }
+        EnglishUrlCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKey getEnterKey(final boolean isPhone) {
-            return isPhone ? LayoutBase.ENTER_KEY : super.getEnterKey(isPhone);
+            return isPhone ? ENTER_KEY : super.getEnterKey(isPhone);
         }
 
         @Override
         public ExpectedKey getEmojiKey(final boolean isPhone) {
-            return LayoutBase.DOMAIN_KEY;
+            return DOMAIN_KEY;
         }
 
         @Override
         public ExpectedKey[] getKeysLeftToSpacebar(final boolean isPhone) {
-            return joinKeys(key("/", LayoutBase.SETTINGS_KEY));
+            return joinKeys(key("/", SETTINGS_KEY));
         }
     }
 }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsRomanian.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsRomanian.java
index d7b858e..af4fbca 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsRomanian.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsRomanian.java
@@ -19,9 +19,9 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
-import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.Qwerty;
 import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
@@ -39,7 +39,7 @@
     LayoutBase getLayout() { return LAYOUT; }
 
     private static class RomanianCustomizer extends LayoutCustomizer {
-        public RomanianCustomizer(final Locale locale) { super(locale); }
+        RomanianCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_L9R; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsRussian.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsRussian.java
index 9919207..75ef481 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsRussian.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsRussian.java
@@ -19,9 +19,9 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.EastSlavic;
-import com.android.inputmethod.keyboard.layout.EastSlavic.EastSlavicCustomizer;
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.customizer.EastSlavicCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
@@ -39,7 +39,7 @@
     LayoutBase getLayout() { return LAYOUT; }
 
     private static class RussianCustomizer extends EastSlavicCustomizer {
-        public RussianCustomizer(final Locale locale) { super(locale); }
+        RussianCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_R9L; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSerbian.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSerbian.java
index 41f1690..9495706 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSerbian.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSerbian.java
@@ -20,8 +20,8 @@
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.SouthSlavic;
-import com.android.inputmethod.keyboard.layout.SouthSlavic.SouthSlavicLayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.customizer.SouthSlavicLayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
@@ -39,7 +39,7 @@
     LayoutBase getLayout() { return LAYOUT; }
 
     private static class SerbianCustomizer extends SouthSlavicLayoutCustomizer {
-        public SerbianCustomizer(final Locale locale) { super(locale); }
+        SerbianCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_R9L; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSerbianLatin.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSerbianLatin.java
index 70f4bce..ea957e4 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSerbianLatin.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSerbianLatin.java
@@ -16,17 +16,18 @@
 
 package com.android.inputmethod.keyboard.layout.tests;
 
-import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.Suppress;
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.SerbianQwertz;
+import com.android.inputmethod.keyboard.layout.customizer.SerbianLatinCustomizer;
 
 import java.util.Locale;
 
 /**
  * sr_ZZ: Serbian (Latin)/serbian_qwertz
  */
-@SmallTest
+@Suppress
 public final class TestsSerbianLatin extends LayoutTestsBase {
     private static final Locale LOCALE = new Locale("sr", "ZZ");
     private static final LayoutBase LAYOUT = new SerbianQwertz(new SerbianLatinCustomizer(LOCALE));
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSerbianLatinQwerty.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSerbianLatinQwerty.java
index d8ef51b..a198473 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSerbianLatinQwerty.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSerbianLatinQwerty.java
@@ -16,10 +16,11 @@
 
 package com.android.inputmethod.keyboard.layout.tests;
 
-import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.Suppress;
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Qwerty;
+import com.android.inputmethod.keyboard.layout.customizer.SerbianLatinCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
@@ -28,7 +29,7 @@
 /**
  * sr_ZZ: Serbian (Latin)/qwerty
  */
-@SmallTest
+@Suppress
 public final class TestsSerbianLatinQwerty extends LayoutTestsBase {
     private static final Locale LOCALE = new Locale("sr", "ZZ");
     private static final LayoutBase LAYOUT = new Qwerty(new SerbianLatinQwertyCustomizer(LOCALE));
@@ -37,14 +38,12 @@
     LayoutBase getLayout() { return LAYOUT; }
 
     private static class SerbianLatinQwertyCustomizer extends SerbianLatinCustomizer {
-        public SerbianLatinQwertyCustomizer(final Locale locale) {
-            super(locale);
-        }
+        SerbianLatinQwertyCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKey[] getRightShiftKeys(final boolean isPhone) {
             return isPhone ? EMPTY_KEYS
-                    : joinKeys(LayoutBase.EXCLAMATION_AND_QUESTION_MARKS, LayoutBase.SHIFT_KEY);
+                    : joinKeys(EXCLAMATION_AND_QUESTION_MARKS, SHIFT_KEY);
         }
 
         @Override
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSinhalaLK.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSinhalaLK.java
index 8b86135..c55c17c 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSinhalaLK.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSinhalaLK.java
@@ -20,7 +20,6 @@
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Sinhala;
-import com.android.inputmethod.keyboard.layout.Sinhala.SinhalaCustomizer;
 
 import java.util.Locale;
 
@@ -30,7 +29,7 @@
 @Suppress
 public final class TestsSinhalaLK extends LayoutTestsBase {
     private static final Locale LOCALE = new Locale("si", "LK");
-    private static final LayoutBase LAYOUT = new Sinhala(new SinhalaCustomizer(LOCALE));
+    private static final LayoutBase LAYOUT = new Sinhala(LOCALE);
 
     @Override
     LayoutBase getLayout() { return LAYOUT; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSlovak.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSlovak.java
index bdaf0ca..c944716 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSlovak.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSlovak.java
@@ -19,9 +19,9 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
-import com.android.inputmethod.keyboard.layout.LayoutBase.EuroCustomizer;
 import com.android.inputmethod.keyboard.layout.Qwerty;
 import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.customizer.EuroCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
@@ -39,7 +39,7 @@
     LayoutBase getLayout() { return LAYOUT; }
 
     private static class SlovakCustomizer extends EuroCustomizer {
-        public SlovakCustomizer(final Locale locale) { super(locale); }
+        SlovakCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_R9L; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSlovenian.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSlovenian.java
index cdb1bee..e49a27b 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSlovenian.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSlovenian.java
@@ -19,9 +19,9 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
-import com.android.inputmethod.keyboard.layout.LayoutBase.EuroCustomizer;
 import com.android.inputmethod.keyboard.layout.Qwerty;
 import com.android.inputmethod.keyboard.layout.Symbols;
+import com.android.inputmethod.keyboard.layout.customizer.EuroCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
@@ -39,7 +39,7 @@
     LayoutBase getLayout() { return LAYOUT; }
 
     private static class SlovenianCustomizer extends EuroCustomizer {
-        public SlovenianCustomizer(final Locale locale) { super(locale); }
+        SlovenianCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKey[] getDoubleQuoteMoreKeys() { return Symbols.DOUBLE_QUOTES_R9L; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSpanish.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSpanish.java
index 12e8676..6c86931 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSpanish.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSpanish.java
@@ -19,8 +19,9 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
-import com.android.inputmethod.keyboard.layout.LayoutBase.EuroCustomizer;
 import com.android.inputmethod.keyboard.layout.Spanish;
+import com.android.inputmethod.keyboard.layout.customizer.EuroCustomizer;
+import com.android.inputmethod.keyboard.layout.customizer.SpanishCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 
 import java.util.Locale;
@@ -39,7 +40,7 @@
     private static class SpanishESCustomizer extends SpanishCustomizer {
         private final EuroCustomizer mEuroCustomizer;
 
-        public SpanishESCustomizer(final Locale locale) {
+        SpanishESCustomizer(final Locale locale) {
             super(locale);
             mEuroCustomizer = new EuroCustomizer(locale);
         }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSpanish419.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSpanish419.java
index 75aad13..828f4c4 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSpanish419.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSpanish419.java
@@ -20,6 +20,7 @@
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Spanish;
+import com.android.inputmethod.keyboard.layout.customizer.SpanishCustomizer;
 
 import java.util.Locale;
 
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSpanishUS.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSpanishUS.java
index c3ac0a0..b5bfbe4 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSpanishUS.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSpanishUS.java
@@ -20,6 +20,7 @@
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Spanish;
+import com.android.inputmethod.keyboard.layout.customizer.SpanishCustomizer;
 
 import java.util.Locale;
 
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSplitLayoutQwertyEnglishUS.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSplitLayoutQwertyEnglishUS.java
index b3340aa..c401d3d 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSplitLayoutQwertyEnglishUS.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSplitLayoutQwertyEnglishUS.java
@@ -23,6 +23,7 @@
 import com.android.inputmethod.keyboard.KeyboardLayoutSet;
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Qwerty;
+import com.android.inputmethod.keyboard.layout.customizer.EnglishCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 
 import java.util.Locale;
@@ -47,18 +48,14 @@
     LayoutBase getLayout() { return LAYOUT; }
 
     private static class EnglishSplitCustomizer extends EnglishCustomizer {
-        EnglishSplitCustomizer(Locale locale) {
-            super(locale);
-        }
+        EnglishSplitCustomizer(Locale locale) { super(locale); }
 
         @Override
         public ExpectedKey[] getSpaceKeys(final boolean isPhone) {
             if (isPhone) {
                 return super.getSpaceKeys(isPhone);
-            } else {
-                return LayoutBase.joinKeys(
-                        LayoutBase.LANGUAGE_SWITCH_KEY, LayoutBase.SPACE_KEY, LayoutBase.SPACE_KEY);
             }
+            return joinKeys(LANGUAGE_SWITCH_KEY, SPACE_KEY, SPACE_KEY);
         }
     }
 }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSwahili.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSwahili.java
index 13b9741..a8ab966 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSwahili.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSwahili.java
@@ -19,8 +19,8 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
-import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.Qwerty;
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
 import java.util.Locale;
@@ -37,7 +37,7 @@
     LayoutBase getLayout() { return LAYOUT; }
 
     private static class SwahiliCustomizer extends LayoutCustomizer {
-        public SwahiliCustomizer(final Locale locale) { super(locale); }
+        SwahiliCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) {
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSwedish.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSwedish.java
index 79cb3dc..061001d 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSwedish.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSwedish.java
@@ -20,6 +20,7 @@
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Nordic;
+import com.android.inputmethod.keyboard.layout.customizer.SwedishCustomizer;
 
 import java.util.Locale;
 
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSwedishPcQwerty.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSwedishPcQwerty.java
index ed74d6d..dc3d831 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSwedishPcQwerty.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSwedishPcQwerty.java
@@ -19,9 +19,10 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
-import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.PcQwerty;
-import com.android.inputmethod.keyboard.layout.PcQwerty.PcQwertyCustomizer;
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
+import com.android.inputmethod.keyboard.layout.customizer.PcQwertyCustomizer;
+import com.android.inputmethod.keyboard.layout.customizer.SwedishCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
@@ -41,7 +42,7 @@
     private static class SwedishPcQwertyCustomizer extends SwedishCustomizer {
         private final LayoutCustomizer mPcQwertyCustomizer;
 
-        public SwedishPcQwertyCustomizer(final Locale locale) {
+        SwedishPcQwertyCustomizer(final Locale locale) {
             super(locale);
             mPcQwertyCustomizer = new PcQwertyCustomizer(locale);
         }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTagalog.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTagalog.java
index 38d5364..ecef2d7 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTagalog.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTagalog.java
@@ -20,6 +20,7 @@
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Spanish;
+import com.android.inputmethod.keyboard.layout.customizer.SpanishCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 
 import java.util.Locale;
@@ -36,15 +37,11 @@
     LayoutBase getLayout() { return LAYOUT; }
 
     private static class TagalogCustomizer extends SpanishCustomizer {
-
-        public TagalogCustomizer(final Locale locale) {
-            super(locale);
-        }
+        TagalogCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKey[] getPunctuationMoreKeys(final boolean isPhone) {
-            return isPhone ? LayoutBase.PHONE_PUNCTUATION_MORE_KEYS
-                    : LayoutBase.TABLET_PUNCTUATION_MORE_KEYS;
+            return isPhone ? PHONE_PUNCTUATION_MORE_KEYS : TABLET_PUNCTUATION_MORE_KEYS;
         }
     }
 }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTamilIN.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTamilIN.java
index 31df53c..3297d39 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTamilIN.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTamilIN.java
@@ -22,7 +22,7 @@
 import com.android.inputmethod.keyboard.layout.Symbols;
 import com.android.inputmethod.keyboard.layout.SymbolsShifted;
 import com.android.inputmethod.keyboard.layout.Tamil;
-import com.android.inputmethod.keyboard.layout.Tamil.TamilCustomizer;
+import com.android.inputmethod.keyboard.layout.customizer.TamilCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 
 import java.util.Locale;
@@ -39,7 +39,7 @@
     LayoutBase getLayout() { return LAYOUT; }
 
     private static class TamilINCustomizer extends TamilCustomizer {
-        public TamilINCustomizer(final Locale locale) { super(locale); }
+        TamilINCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKey getCurrencyKey() { return CURRENCY_RUPEE; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTamilLK.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTamilLK.java
index 65ec0b0..72872ba 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTamilLK.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTamilLK.java
@@ -22,7 +22,7 @@
 import com.android.inputmethod.keyboard.layout.Symbols;
 import com.android.inputmethod.keyboard.layout.SymbolsShifted;
 import com.android.inputmethod.keyboard.layout.Tamil;
-import com.android.inputmethod.keyboard.layout.Tamil.TamilCustomizer;
+import com.android.inputmethod.keyboard.layout.customizer.TamilCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 
 import java.util.Locale;
@@ -39,7 +39,7 @@
     LayoutBase getLayout() { return LAYOUT; }
 
     private static class TamilLKCustomizer extends TamilCustomizer {
-        public TamilLKCustomizer(final Locale locale) { super(locale); }
+        TamilLKCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKey getCurrencyKey() { return CURRENCY_RUPEE; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTamilSG.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTamilSG.java
index ade7aba..a7a041b 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTamilSG.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTamilSG.java
@@ -20,7 +20,7 @@
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Tamil;
-import com.android.inputmethod.keyboard.layout.Tamil.TamilCustomizer;
+import com.android.inputmethod.keyboard.layout.customizer.TamilCustomizer;
 
 import java.util.Locale;
 
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTeluguIN.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTeluguIN.java
index 04996d9..2b202a9 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTeluguIN.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTeluguIN.java
@@ -20,7 +20,6 @@
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Telugu;
-import com.android.inputmethod.keyboard.layout.Telugu.TeluguCustomizer;
 
 import java.util.Locale;
 
@@ -30,7 +29,7 @@
 @SmallTest
 public final class TestsTeluguIN extends LayoutTestsBase {
     private static final Locale LOCALE = new Locale("te", "IN");
-    private static final LayoutBase LAYOUT = new Telugu(new TeluguCustomizer(LOCALE));
+    private static final LayoutBase LAYOUT = new Telugu(LOCALE);
 
     @Override
     LayoutBase getLayout() { return LAYOUT; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsThai.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsThai.java
index 3c87272..2c1a29e 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsThai.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsThai.java
@@ -20,7 +20,6 @@
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Thai;
-import com.android.inputmethod.keyboard.layout.Thai.ThaiCustomizer;
 
 import java.util.Locale;
 
@@ -30,7 +29,7 @@
 @SmallTest
 public final class TestsThai extends LayoutTestsBase {
     private static final Locale LOCALE = new Locale("th");
-    private static final LayoutBase LAYOUT = new Thai(new ThaiCustomizer(LOCALE));
+    private static final LayoutBase LAYOUT = new Thai(LOCALE);
 
     @Override
     LayoutBase getLayout() { return LAYOUT; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTurkish.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTurkish.java
index bf427cd..95f86e4 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTurkish.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTurkish.java
@@ -19,8 +19,9 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
-import com.android.inputmethod.keyboard.layout.LayoutBase.EuroCustomizer;
 import com.android.inputmethod.keyboard.layout.Qwerty;
+import com.android.inputmethod.keyboard.layout.customizer.EuroCustomizer;
+import com.android.inputmethod.keyboard.layout.customizer.TurkicCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
 import java.util.Locale;
@@ -39,7 +40,7 @@
     private static class TurkishCustomizer extends EuroCustomizer {
         private final TurkicCustomizer mTurkicCustomizer;
 
-        public TurkishCustomizer(final Locale locale) {
+        TurkishCustomizer(final Locale locale) {
             super(locale);
             mTurkicCustomizer = new TurkicCustomizer(locale);
         }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsUkrainian.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsUkrainian.java
index a6bcacc..da93d6c 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsUkrainian.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsUkrainian.java
@@ -19,10 +19,10 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.EastSlavic;
-import com.android.inputmethod.keyboard.layout.EastSlavic.EastSlavicCustomizer;
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Symbols;
 import com.android.inputmethod.keyboard.layout.SymbolsShifted;
+import com.android.inputmethod.keyboard.layout.customizer.EastSlavicCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
@@ -40,7 +40,7 @@
     LayoutBase getLayout() { return LAYOUT; }
 
     private static class UkrainianCustomizer extends EastSlavicCustomizer {
-        public UkrainianCustomizer(final Locale locale) { super(locale); }
+        UkrainianCustomizer(final Locale locale) { super(locale); }
 
         @Override
         public ExpectedKey getCurrencyKey() { return CURRENCY_HRYVNIA; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsUzbek.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsUzbek.java
new file mode 100644
index 0000000..169de1f
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsUzbek.java
@@ -0,0 +1,37 @@
+/*
+ * 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.layout.tests;
+
+import android.test.suitebuilder.annotation.Suppress;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.Uzbek;
+import com.android.inputmethod.keyboard.layout.customizer.UzbekCustomizer;
+
+import java.util.Locale;
+
+/**
+ * uz_UZ: Uzbek (Uzbekistan)/uzbek
+ */
+@Suppress
+public final class TestsUzbek extends LayoutTestsBase {
+    private static final Locale LOCALE = new Locale("uz", "UZ");
+    private static final LayoutBase LAYOUT = new Uzbek(new UzbekCustomizer(LOCALE));
+
+    @Override
+    LayoutBase getLayout() { return LAYOUT; }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsUzbekQwerty.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsUzbekQwerty.java
new file mode 100644
index 0000000..c210da1
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsUzbekQwerty.java
@@ -0,0 +1,47 @@
+/*
+ * 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.layout.tests;
+
+import android.test.suitebuilder.annotation.Suppress;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.Qwerty;
+import com.android.inputmethod.keyboard.layout.customizer.UzbekCustomizer;
+import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
+
+import java.util.Locale;
+
+/**
+ * uz_UZ: Uzbek (Uzbekistan)/qwerty
+ */
+@Suppress
+public final class TestsUzbekQwerty extends LayoutTestsBase {
+    private static final Locale LOCALE = new Locale("uz", "UZ");
+    private static final LayoutBase LAYOUT = new Qwerty(new UzbekQwertyCustomizer(LOCALE));
+
+    @Override
+    LayoutBase getLayout() { return LAYOUT; }
+
+    private static class UzbekQwertyCustomizer extends UzbekCustomizer {
+        UzbekQwertyCustomizer(final Locale locale) { super(locale); }
+
+        @Override
+        protected void setUzbekKeys(final ExpectedKeyboardBuilder builder) {
+            // QWERTY layout doesn't have Uzebk keys.
+        }
+    }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsVietnamese.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsVietnamese.java
index 83d86ac..356b042 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsVietnamese.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsVietnamese.java
@@ -19,10 +19,10 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
-import com.android.inputmethod.keyboard.layout.LayoutBase.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.Qwerty;
 import com.android.inputmethod.keyboard.layout.Symbols;
 import com.android.inputmethod.keyboard.layout.SymbolsShifted;
+import com.android.inputmethod.keyboard.layout.customizer.LayoutCustomizer;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKey;
 import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder;
 
@@ -40,9 +40,7 @@
     LayoutBase getLayout() { return LAYOUT; }
 
     private static class VietnameseCustomizer extends LayoutCustomizer {
-        public VietnameseCustomizer(final Locale locale) {
-            super(locale);
-        }
+        VietnameseCustomizer(final Locale locale) { super(locale);  }
 
         @Override
         public ExpectedKey getCurrencyKey() { return CURRENCY_DONG; }
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsZulu.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsZulu.java
index e048e92..abf3cad 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsZulu.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsZulu.java
@@ -20,6 +20,7 @@
 
 import com.android.inputmethod.keyboard.layout.LayoutBase;
 import com.android.inputmethod.keyboard.layout.Qwerty;
+import com.android.inputmethod.keyboard.layout.customizer.EnglishCustomizer;
 
 import java.util.Locale;
 
diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
index 8d5d6cc..0e58b72 100644
--- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
+++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
@@ -37,6 +37,7 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Random;
@@ -49,6 +50,7 @@
     private static final int DUMMY_PROBABILITY = 0;
     private static final int[] DICT_FORMAT_VERSIONS =
             new int[] { FormatSpec.VERSION4, FormatSpec.VERSION4_DEV };
+    private static final String DICTIONARY_ID = "TestDecayingBinaryDictionary";
 
     private int mCurrentTime = 0;
 
@@ -56,10 +58,15 @@
     protected void setUp() throws Exception {
         super.setUp();
         mCurrentTime = 0;
+        mDictFilesToBeDeleted.clear();
     }
 
     @Override
     protected void tearDown() throws Exception {
+        for (final File dictFile : mDictFilesToBeDeleted) {
+            dictFile.delete();
+        }
+        mDictFilesToBeDeleted.clear();
         stopTestModeInNativeCode();
         super.tearDown();
     }
@@ -68,20 +75,25 @@
         return formatVersion > FormatSpec.VERSION401;
     }
 
-    private void addUnigramWord(final BinaryDictionary binaryDictionary, final String word,
-            final int probability) {
-        binaryDictionary.addUnigramEntry(word, probability, "" /* shortcutTarget */,
-                BinaryDictionary.NOT_A_PROBABILITY /* shortcutProbability */,
-                false /* isBeginningOfSentence */, false /* isNotAWord */,
-                false /* isBlacklisted */, mCurrentTime /* timestamp */);
+    private void onInputWord(final BinaryDictionary binaryDictionary, final String word,
+            final boolean isValidWord) {
+        binaryDictionary.updateEntriesForWordWithNgramContext(NgramContext.EMPTY_PREV_WORDS_INFO,
+                word, isValidWord, 1 /* count */, mCurrentTime /* timestamp */);
     }
 
-    private void addBigramWords(final BinaryDictionary binaryDictionary, final String word0,
-            final String word1, final int probability) {
-        binaryDictionary.addNgramEntry(new NgramContext(new WordInfo(word0)), word1, probability,
+    private void onInputWordWithPrevWord(final BinaryDictionary binaryDictionary, final String word,
+            final boolean isValidWord, final String prevWord) {
+        binaryDictionary.updateEntriesForWordWithNgramContext(
+                new NgramContext(new WordInfo(prevWord)), word, isValidWord, 1 /* count */,
                 mCurrentTime /* timestamp */);
     }
 
+    private void onInputWordWithBeginningOfSentenceContext(
+            final BinaryDictionary binaryDictionary, final String word, final boolean isValidWord) {
+        binaryDictionary.updateEntriesForWordWithNgramContext(NgramContext.BEGINNING_OF_SENTENCE,
+                word, isValidWord, 1 /* count */, mCurrentTime /* timestamp */);
+    }
+
     private static boolean isValidBigram(final BinaryDictionary binaryDictionary,
             final String word0, final String word1) {
         return binaryDictionary.isValidNgram(new NgramContext(new WordInfo(word0)), word1);
@@ -103,25 +115,32 @@
         binaryDictionary.flushWithGC();
     }
 
-    private File createEmptyDictionaryAndGetFile(final String dictId,
-            final int formatVersion) throws IOException {
+    private HashSet<File> mDictFilesToBeDeleted = new HashSet<>();
+
+    private File createEmptyDictionaryAndGetFile(final int formatVersion) {
         if (formatVersion == FormatSpec.VERSION4
                 || formatVersion == FormatSpec.VERSION4_ONLY_FOR_TESTING
                 || formatVersion == FormatSpec.VERSION4_DEV) {
-            return createEmptyVer4DictionaryAndGetFile(dictId, formatVersion);
+            try {
+                final File dictFile = createEmptyVer4DictionaryAndGetFile(formatVersion);
+                mDictFilesToBeDeleted.add(dictFile);
+                return dictFile;
+            } catch (final IOException e) {
+                fail(e.toString());
+            }
         } else {
-            throw new IOException("Dictionary format version " + formatVersion
-                    + " is not supported.");
+            fail("Dictionary format version " + formatVersion + " is not supported.");
         }
+        return null;
     }
 
-    private File createEmptyVer4DictionaryAndGetFile(final String dictId, final int formatVersion)
+    private File createEmptyVer4DictionaryAndGetFile(final int formatVersion)
             throws IOException {
-        final File file = File.createTempFile(dictId, TEST_DICT_FILE_EXTENSION,
+        final File file = File.createTempFile(DICTIONARY_ID, TEST_DICT_FILE_EXTENSION,
                 getContext().getCacheDir());
         FileUtils.deleteRecursively(file);
         Map<String, String> attributeMap = new HashMap<>();
-        attributeMap.put(DictionaryHeader.DICTIONARY_ID_KEY, dictId);
+        attributeMap.put(DictionaryHeader.DICTIONARY_ID_KEY, DICTIONARY_ID);
         attributeMap.put(DictionaryHeader.DICTIONARY_VERSION_KEY,
                 String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())));
         attributeMap.put(DictionaryHeader.USES_FORGETTING_CURVE_KEY,
@@ -137,6 +156,12 @@
         }
     }
 
+    private BinaryDictionary getBinaryDictionary(final File dictFile) {
+        return new BinaryDictionary(dictFile.getAbsolutePath(),
+                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+    }
+
     private static int setCurrentTimeForTestMode(final int currentTime) {
         return BinaryDictionaryUtils.setCurrentTimeForTest(currentTime);
     }
@@ -153,19 +178,11 @@
 
     private void testReadDictInJavaSide(final int formatVersion) {
         setCurrentTimeForTestMode(mCurrentTime);
-        File dictFile = null;
-        try {
-            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
-        } catch (IOException e) {
-            fail("IOException while writing an initial dictionary : " + e);
-        }
-        BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
-                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
-                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
-        addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY);
-        addUnigramWord(binaryDictionary, "ab", DUMMY_PROBABILITY);
-        addUnigramWord(binaryDictionary, "aaa", DUMMY_PROBABILITY);
-        addBigramWords(binaryDictionary, "a", "aaa", DUMMY_PROBABILITY);
+        final File dictFile = createEmptyDictionaryAndGetFile(formatVersion);
+        final BinaryDictionary binaryDictionary = getBinaryDictionary(dictFile);
+        onInputWord(binaryDictionary, "a", true /* isValidWord */);
+        onInputWord(binaryDictionary, "ab", true /* isValidWord */);
+        onInputWordWithPrevWord(binaryDictionary, "aaa", true /* isValidWord */, "a");
         binaryDictionary.flushWithGC();
         binaryDictionary.close();
 
@@ -189,7 +206,6 @@
         } catch (UnsupportedFormatException e) {
             fail("Unsupported format: " + e);
         }
-        dictFile.delete();
     }
 
     public void testControlCurrentTime() {
@@ -214,42 +230,33 @@
     }
 
     private void testAddValidAndInvalidWords(final int formatVersion) {
-        File dictFile = null;
-        try {
-            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
-        } catch (IOException e) {
-            fail("IOException while writing an initial dictionary : " + e);
-        }
-        BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
-                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
-                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+        final File dictFile = createEmptyDictionaryAndGetFile(formatVersion);
+        final BinaryDictionary binaryDictionary = getBinaryDictionary(dictFile);
 
-        addUnigramWord(binaryDictionary, "a", Dictionary.NOT_A_PROBABILITY);
+        onInputWord(binaryDictionary, "a", false /* isValidWord */);
         assertFalse(binaryDictionary.isValidWord("a"));
-        addUnigramWord(binaryDictionary, "a", Dictionary.NOT_A_PROBABILITY);
-        addUnigramWord(binaryDictionary, "a", Dictionary.NOT_A_PROBABILITY);
+        onInputWord(binaryDictionary, "a", false /* isValidWord */);
+        onInputWord(binaryDictionary, "a", false /* isValidWord */);
         assertTrue(binaryDictionary.isValidWord("a"));
 
-        addUnigramWord(binaryDictionary, "b", DUMMY_PROBABILITY);
+        onInputWord(binaryDictionary, "b", true /* isValidWord */);
         assertTrue(binaryDictionary.isValidWord("b"));
 
-        addBigramWords(binaryDictionary, "a", "b", Dictionary.NOT_A_PROBABILITY);
+        onInputWordWithPrevWord(binaryDictionary, "b", false /* isValidWord */, "a");
         assertFalse(isValidBigram(binaryDictionary, "a", "b"));
-        addBigramWords(binaryDictionary, "a", "b", Dictionary.NOT_A_PROBABILITY);
+        onInputWordWithPrevWord(binaryDictionary, "b", false /* isValidWord */, "a");
         assertTrue(isValidBigram(binaryDictionary, "a", "b"));
 
-        addUnigramWord(binaryDictionary, "c", DUMMY_PROBABILITY);
-        addBigramWords(binaryDictionary, "a", "c", DUMMY_PROBABILITY);
+        onInputWordWithPrevWord(binaryDictionary, "c", true /* isValidWord */, "a");
         assertTrue(isValidBigram(binaryDictionary, "a", "c"));
 
         // Add bigrams of not valid unigrams.
-        addBigramWords(binaryDictionary, "x", "y", Dictionary.NOT_A_PROBABILITY);
+        onInputWordWithPrevWord(binaryDictionary, "y", false /* isValidWord */, "x");
         assertFalse(isValidBigram(binaryDictionary, "x", "y"));
-        addBigramWords(binaryDictionary, "x", "y", DUMMY_PROBABILITY);
+        onInputWordWithPrevWord(binaryDictionary, "y", true /* isValidWord */, "x");
         assertFalse(isValidBigram(binaryDictionary, "x", "y"));
 
         binaryDictionary.close();
-        dictFile.delete();
     }
 
     public void testDecayingProbability() {
@@ -259,46 +266,35 @@
     }
 
     private void testDecayingProbability(final int formatVersion) {
-        File dictFile = null;
-        try {
-            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
-        } catch (IOException e) {
-            fail("IOException while writing an initial dictionary : " + e);
-        }
-        BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
-                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
-                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+        final File dictFile = createEmptyDictionaryAndGetFile(formatVersion);
+        final BinaryDictionary binaryDictionary = getBinaryDictionary(dictFile);
 
-        addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY);
+        onInputWord(binaryDictionary, "a", true /* isValidWord */);
         assertTrue(binaryDictionary.isValidWord("a"));
         forcePassingShortTime(binaryDictionary);
         assertFalse(binaryDictionary.isValidWord("a"));
 
-        addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY);
-        addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY);
-        addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY);
+        onInputWord(binaryDictionary, "a", true /* isValidWord */);
+        onInputWord(binaryDictionary, "a", true /* isValidWord */);
+        onInputWord(binaryDictionary, "a", true /* isValidWord */);
         assertTrue(binaryDictionary.isValidWord("a"));
         forcePassingShortTime(binaryDictionary);
         assertTrue(binaryDictionary.isValidWord("a"));
         forcePassingLongTime(binaryDictionary);
         assertFalse(binaryDictionary.isValidWord("a"));
 
-        addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY);
-        addUnigramWord(binaryDictionary, "b", DUMMY_PROBABILITY);
-        addBigramWords(binaryDictionary, "a", "b", DUMMY_PROBABILITY);
+        onInputWord(binaryDictionary, "a", true /* isValidWord */);
+        onInputWordWithPrevWord(binaryDictionary, "b", true /* isValidWord */, "a");
         assertTrue(isValidBigram(binaryDictionary, "a", "b"));
         forcePassingShortTime(binaryDictionary);
         assertFalse(isValidBigram(binaryDictionary, "a", "b"));
 
-        addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY);
-        addUnigramWord(binaryDictionary, "b", DUMMY_PROBABILITY);
-        addBigramWords(binaryDictionary, "a", "b", DUMMY_PROBABILITY);
-        addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY);
-        addUnigramWord(binaryDictionary, "b", DUMMY_PROBABILITY);
-        addBigramWords(binaryDictionary, "a", "b", DUMMY_PROBABILITY);
-        addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY);
-        addUnigramWord(binaryDictionary, "b", DUMMY_PROBABILITY);
-        addBigramWords(binaryDictionary, "a", "b", DUMMY_PROBABILITY);
+        onInputWord(binaryDictionary, "a", true /* isValidWord */);
+        onInputWordWithPrevWord(binaryDictionary, "b", true /* isValidWord */, "a");
+        onInputWord(binaryDictionary, "a", true /* isValidWord */);
+        onInputWordWithPrevWord(binaryDictionary, "b", true /* isValidWord */, "a");
+        onInputWord(binaryDictionary, "a", true /* isValidWord */);
+        onInputWordWithPrevWord(binaryDictionary, "b", true /* isValidWord */, "a");
         assertTrue(isValidBigram(binaryDictionary, "a", "b"));
         forcePassingShortTime(binaryDictionary);
         assertTrue(isValidBigram(binaryDictionary, "a", "b"));
@@ -306,7 +302,6 @@
         assertFalse(isValidBigram(binaryDictionary, "a", "b"));
 
         binaryDictionary.close();
-        dictFile.delete();
     }
 
     public void testAddManyUnigramsToDecayingDict() {
@@ -321,16 +316,8 @@
         final int codePointSetSize = 50;
         final long seed = System.currentTimeMillis();
         final Random random = new Random(seed);
-
-        File dictFile = null;
-        try {
-            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
-        } catch (IOException e) {
-            fail("IOException while writing an initial dictionary : " + e);
-        }
-        BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
-                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
-                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+        final File dictFile = createEmptyDictionaryAndGetFile(formatVersion);
+        final BinaryDictionary binaryDictionary = getBinaryDictionary(dictFile);
         setCurrentTimeForTestMode(mCurrentTime);
 
         final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random);
@@ -345,7 +332,7 @@
                 binaryDictionary.getPropertyForGettingStats(BinaryDictionary.MAX_UNIGRAM_COUNT_QUERY));
         for (int i = 0; i < unigramTypedCount; i++) {
             final String word = words.get(random.nextInt(words.size()));
-            addUnigramWord(binaryDictionary, word, DUMMY_PROBABILITY);
+            onInputWord(binaryDictionary, word, true /* isValidWord */);
 
             if (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) {
                 final int unigramCountBeforeGC =
@@ -384,26 +371,18 @@
         final int codePointSetSize = 50;
         final long seed = System.currentTimeMillis();
         final Random random = new Random(seed);
-
-        File dictFile = null;
-        try {
-            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
-        } catch (IOException e) {
-            fail("IOException while writing an initial dictionary : " + e);
-        }
-        BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
-                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
-                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+        final File dictFile = createEmptyDictionaryAndGetFile(formatVersion);
+        final BinaryDictionary binaryDictionary = getBinaryDictionary(dictFile);
         setCurrentTimeForTestMode(mCurrentTime);
         final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random);
 
         final String strong = "strong";
         final String weak = "weak";
         for (int j = 0; j < strongUnigramTypedCount; j++) {
-            addUnigramWord(binaryDictionary, strong, DUMMY_PROBABILITY);
+            onInputWord(binaryDictionary, strong, true /* isValidWord */);
         }
         for (int j = 0; j < weakUnigramTypedCount; j++) {
-            addUnigramWord(binaryDictionary, weak, DUMMY_PROBABILITY);
+            onInputWord(binaryDictionary, weak, true /* isValidWord */);
         }
         assertTrue(binaryDictionary.isValidWord(strong));
         assertTrue(binaryDictionary.isValidWord(weak));
@@ -411,7 +390,7 @@
         for (int i = 0; i < unigramCount; i++) {
             final String word = CodePointUtils.generateWord(random, codePointSet);
             for (int j = 0; j < eachUnigramTypedCount; j++) {
-                addUnigramWord(binaryDictionary, word, DUMMY_PROBABILITY);
+                onInputWord(binaryDictionary, word, true /* isValidWord */);
             }
             if (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) {
                 final int unigramCountBeforeGC =
@@ -445,16 +424,9 @@
         final long seed = System.currentTimeMillis();
         final Random random = new Random(seed);
 
-        File dictFile = null;
-        try {
-            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
-        } catch (IOException e) {
-            fail("IOException while writing an initial dictionary : " + e);
-        }
-        BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
-                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
-                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
         setCurrentTimeForTestMode(mCurrentTime);
+        final File dictFile = createEmptyDictionaryAndGetFile(formatVersion);
+        final BinaryDictionary binaryDictionary = getBinaryDictionary(dictFile);
 
         final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random);
         final ArrayList<String> words = new ArrayList<>();
@@ -477,12 +449,13 @@
         }
 
         final int maxBigramCount = Integer.parseInt(
-                binaryDictionary.getPropertyForGettingStats(BinaryDictionary.MAX_BIGRAM_COUNT_QUERY));
+                binaryDictionary.getPropertyForGettingStats(
+                        BinaryDictionary.MAX_BIGRAM_COUNT_QUERY));
         for (int i = 0; i < bigramTypedCount; ++i) {
             final Pair<String, String> bigram = bigrams.get(random.nextInt(bigrams.size()));
-            addUnigramWord(binaryDictionary, bigram.first, DUMMY_PROBABILITY);
-            addUnigramWord(binaryDictionary, bigram.second, DUMMY_PROBABILITY);
-            addBigramWords(binaryDictionary, bigram.first, bigram.second, DUMMY_PROBABILITY);
+            onInputWord(binaryDictionary, bigram.first, true /* isValidWord */);
+            onInputWordWithPrevWord(binaryDictionary, bigram.second, true /* isValidWord */,
+                    bigram.first);
 
             if (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) {
                 final int bigramCountBeforeGC =
@@ -523,17 +496,9 @@
         final int codePointSetSize = 50;
         final long seed = System.currentTimeMillis();
         final Random random = new Random(seed);
-
-        File dictFile = null;
-        try {
-            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
-        } catch (IOException e) {
-            fail("IOException while writing an initial dictionary : " + e);
-        }
-        BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
-                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
-                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
         setCurrentTimeForTestMode(mCurrentTime);
+        final File dictFile = createEmptyDictionaryAndGetFile(formatVersion);
+        final BinaryDictionary binaryDictionary = getBinaryDictionary(dictFile);
         final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random);
 
         final ArrayList<String> words = new ArrayList<>();
@@ -541,23 +506,22 @@
             final String word = CodePointUtils.generateWord(random, codePointSet);
             words.add(word);
             for (int j = 0; j < unigramTypedCount; j++) {
-                addUnigramWord(binaryDictionary, word, DUMMY_PROBABILITY);
+                onInputWord(binaryDictionary, word, true /* isValidWord */);
             }
         }
         final String strong = "strong";
         final String weak = "weak";
         final String target = "target";
         for (int j = 0; j < unigramTypedCount; j++) {
-            addUnigramWord(binaryDictionary, strong, DUMMY_PROBABILITY);
-            addUnigramWord(binaryDictionary, weak, DUMMY_PROBABILITY);
-            addUnigramWord(binaryDictionary, target, DUMMY_PROBABILITY);
+            onInputWord(binaryDictionary, weak, true /* isValidWord */);
+            onInputWord(binaryDictionary, strong, true /* isValidWord */);
         }
         binaryDictionary.flushWithGC();
         for (int j = 0; j < strongBigramTypedCount; j++) {
-            addBigramWords(binaryDictionary, strong, target, DUMMY_PROBABILITY);
+            onInputWordWithPrevWord(binaryDictionary, target, true /* isValidWord */, strong);
         }
         for (int j = 0; j < weakBigramTypedCount; j++) {
-            addBigramWords(binaryDictionary, weak, target, DUMMY_PROBABILITY);
+            onInputWordWithPrevWord(binaryDictionary, target, true /* isValidWord */, weak);
         }
         assertTrue(isValidBigram(binaryDictionary, strong, target));
         assertTrue(isValidBigram(binaryDictionary, weak, target));
@@ -570,7 +534,7 @@
             final String word1 = words.get(word1Index);
 
             for (int j = 0; j < eachBigramTypedCount; j++) {
-                addBigramWords(binaryDictionary, word0, word1, DUMMY_PROBABILITY);
+                onInputWordWithPrevWord(binaryDictionary, word1, true /* isValidWord */, word0);
             }
             if (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) {
                 final int bigramCountBeforeGC =
@@ -596,28 +560,20 @@
 
     private void testDictMigration(final int fromFormatVersion, final int toFormatVersion) {
         setCurrentTimeForTestMode(mCurrentTime);
-        File dictFile = null;
-        try {
-            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", fromFormatVersion);
-        } catch (IOException e) {
-            fail("IOException while writing an initial dictionary : " + e);
-        }
-        final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
-                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
-                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
-        addUnigramWord(binaryDictionary, "aaa", DUMMY_PROBABILITY);
+        final File dictFile = createEmptyDictionaryAndGetFile(fromFormatVersion);
+        final BinaryDictionary binaryDictionary = getBinaryDictionary(dictFile);
+        onInputWord(binaryDictionary, "aaa", true /* isValidWord */);
         assertTrue(binaryDictionary.isValidWord("aaa"));
-        addUnigramWord(binaryDictionary, "bbb", Dictionary.NOT_A_PROBABILITY);
-        assertFalse(binaryDictionary.isValidWord("bbb"));
-        addUnigramWord(binaryDictionary, "ccc", DUMMY_PROBABILITY);
-        addUnigramWord(binaryDictionary, "ccc", DUMMY_PROBABILITY);
-        addUnigramWord(binaryDictionary, "ccc", DUMMY_PROBABILITY);
-        addUnigramWord(binaryDictionary, "ccc", DUMMY_PROBABILITY);
-        addUnigramWord(binaryDictionary, "ccc", DUMMY_PROBABILITY);
-        addUnigramWord(binaryDictionary, "abc", DUMMY_PROBABILITY);
-        addBigramWords(binaryDictionary, "aaa", "abc", DUMMY_PROBABILITY);
+        onInputWord(binaryDictionary, "ccc", true /* isValidWord */);
+        onInputWord(binaryDictionary, "ccc", true /* isValidWord */);
+        onInputWord(binaryDictionary, "ccc", true /* isValidWord */);
+        onInputWord(binaryDictionary, "ccc", true /* isValidWord */);
+        onInputWord(binaryDictionary, "ccc", true /* isValidWord */);
+
+        onInputWordWithPrevWord(binaryDictionary, "abc", true /* isValidWord */, "aaa");
         assertTrue(isValidBigram(binaryDictionary, "aaa", "abc"));
-        addBigramWords(binaryDictionary, "aaa", "bbb", Dictionary.NOT_A_PROBABILITY);
+        onInputWordWithPrevWord(binaryDictionary, "bbb", false /* isValidWord */, "aaa");
+        assertFalse(binaryDictionary.isValidWord("bbb"));
         assertFalse(isValidBigram(binaryDictionary, "aaa", "bbb"));
 
         assertEquals(fromFormatVersion, binaryDictionary.getFormatVersion());
@@ -627,14 +583,13 @@
         assertTrue(binaryDictionary.isValidWord("aaa"));
         assertFalse(binaryDictionary.isValidWord("bbb"));
         assertTrue(binaryDictionary.getFrequency("aaa") < binaryDictionary.getFrequency("ccc"));
-        addUnigramWord(binaryDictionary, "bbb", Dictionary.NOT_A_PROBABILITY);
+        onInputWord(binaryDictionary, "bbb", false /* isValidWord */);
         assertTrue(binaryDictionary.isValidWord("bbb"));
         assertTrue(isValidBigram(binaryDictionary, "aaa", "abc"));
         assertFalse(isValidBigram(binaryDictionary, "aaa", "bbb"));
-        addBigramWords(binaryDictionary, "aaa", "bbb", Dictionary.NOT_A_PROBABILITY);
+        onInputWordWithPrevWord(binaryDictionary, "bbb", false /* isValidWord */, "aaa");
         assertTrue(isValidBigram(binaryDictionary, "aaa", "bbb"));
         binaryDictionary.close();
-        dictFile.delete();
     }
 
     public void testBeginningOfSentence() {
@@ -647,47 +602,36 @@
 
     private void testBeginningOfSentence(final int formatVersion) {
         setCurrentTimeForTestMode(mCurrentTime);
-        File dictFile = null;
-        try {
-            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
-        } catch (IOException e) {
-            fail("IOException while writing an initial dictionary : " + e);
-        }
-        final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
-                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
-                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+        final File dictFile = createEmptyDictionaryAndGetFile(formatVersion);
+        final BinaryDictionary binaryDictionary = getBinaryDictionary(dictFile);
 
         binaryDictionary.addUnigramEntry("", DUMMY_PROBABILITY, "" /* shortcutTarget */,
                 BinaryDictionary.NOT_A_PROBABILITY /* shortcutProbability */,
                 true /* isBeginningOfSentence */, true /* isNotAWord */, false /* isBlacklisted */,
                 mCurrentTime);
         final NgramContext beginningOfSentenceContext = NgramContext.BEGINNING_OF_SENTENCE;
-        addUnigramWord(binaryDictionary, "aaa", DUMMY_PROBABILITY);
-        binaryDictionary.addNgramEntry(beginningOfSentenceContext, "aaa", DUMMY_PROBABILITY,
-                mCurrentTime);
+        onInputWordWithBeginningOfSentenceContext(binaryDictionary, "aaa", true /* isValidWord */);
+        assertFalse(binaryDictionary.isValidNgram(beginningOfSentenceContext, "aaa"));
+        onInputWordWithBeginningOfSentenceContext(binaryDictionary, "aaa", true /* isValidWord */);
         assertTrue(binaryDictionary.isValidNgram(beginningOfSentenceContext, "aaa"));
-        binaryDictionary.addNgramEntry(beginningOfSentenceContext, "aaa", DUMMY_PROBABILITY,
-                mCurrentTime);
-        addUnigramWord(binaryDictionary, "bbb", DUMMY_PROBABILITY);
-        binaryDictionary.addNgramEntry(beginningOfSentenceContext, "bbb", DUMMY_PROBABILITY,
-                mCurrentTime);
+        onInputWordWithBeginningOfSentenceContext(binaryDictionary, "aaa", true /* isValidWord */);
+        onInputWordWithBeginningOfSentenceContext(binaryDictionary, "bbb", true /* isValidWord */);
+        assertFalse(binaryDictionary.isValidNgram(beginningOfSentenceContext, "bbb"));
+        onInputWordWithBeginningOfSentenceContext(binaryDictionary, "bbb", true /* isValidWord */);
         assertTrue(binaryDictionary.isValidNgram(beginningOfSentenceContext, "aaa"));
         assertTrue(binaryDictionary.isValidNgram(beginningOfSentenceContext, "bbb"));
-
         forcePassingLongTime(binaryDictionary);
         assertFalse(binaryDictionary.isValidNgram(beginningOfSentenceContext, "aaa"));
         assertFalse(binaryDictionary.isValidNgram(beginningOfSentenceContext, "bbb"));
-
-        addUnigramWord(binaryDictionary, "aaa", DUMMY_PROBABILITY);
-        binaryDictionary.addNgramEntry(beginningOfSentenceContext, "aaa", DUMMY_PROBABILITY,
-                mCurrentTime);
-        addUnigramWord(binaryDictionary, "bbb", DUMMY_PROBABILITY);
-        binaryDictionary.addNgramEntry(beginningOfSentenceContext, "bbb", DUMMY_PROBABILITY,
-                mCurrentTime);
+        onInputWordWithBeginningOfSentenceContext(binaryDictionary, "aaa", true /* isValidWord */);
+        assertFalse(binaryDictionary.isValidNgram(beginningOfSentenceContext, "aaa"));
+        onInputWordWithBeginningOfSentenceContext(binaryDictionary, "aaa", true /* isValidWord */);
+        onInputWordWithBeginningOfSentenceContext(binaryDictionary, "bbb", true /* isValidWord */);
+        assertFalse(binaryDictionary.isValidNgram(beginningOfSentenceContext, "bbb"));
+        onInputWordWithBeginningOfSentenceContext(binaryDictionary, "bbb", true /* isValidWord */);
         assertTrue(binaryDictionary.isValidNgram(beginningOfSentenceContext, "aaa"));
         assertTrue(binaryDictionary.isValidNgram(beginningOfSentenceContext, "bbb"));
         binaryDictionary.close();
-        dictFile.delete();
     }
 
     public void testRemoveUnigrams() {
@@ -699,26 +643,18 @@
     private void testRemoveUnigrams(final int formatVersion) {
         final int unigramInputCount = 20;
         setCurrentTimeForTestMode(mCurrentTime);
-        File dictFile = null;
-        try {
-            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
-        } catch (IOException e) {
-            fail("IOException while writing an initial dictionary : " + e);
-        }
-        final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
-                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
-                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+        final File dictFile = createEmptyDictionaryAndGetFile(formatVersion);
+        final BinaryDictionary binaryDictionary = getBinaryDictionary(dictFile);
 
-        addUnigramWord(binaryDictionary, "aaa", Dictionary.NOT_A_PROBABILITY);
+        onInputWord(binaryDictionary, "aaa", false /* isValidWord */);
         assertFalse(binaryDictionary.isValidWord("aaa"));
         for (int i = 0; i < unigramInputCount; i++) {
-            addUnigramWord(binaryDictionary, "aaa", Dictionary.NOT_A_PROBABILITY);
+            onInputWord(binaryDictionary, "aaa", false /* isValidWord */);
         }
         assertTrue(binaryDictionary.isValidWord("aaa"));
         assertTrue(binaryDictionary.removeUnigramEntry("aaa"));
         assertFalse(binaryDictionary.isValidWord("aaa"));
 
         binaryDictionary.close();
-        dictFile.delete();
     }
 }
diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java
index e6f00b6..90dd436 100644
--- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java
+++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java
@@ -46,6 +46,7 @@
     private static final String TEST_LOCALE = "test";
     private static final int[] DICT_FORMAT_VERSIONS =
             new int[] { FormatSpec.VERSION4, FormatSpec.VERSION4_DEV };
+    private static final String DICTIONARY_ID = "TestBinaryDictionary";
 
     private static boolean canCheckBigramProbability(final int formatVersion) {
         return formatVersion > FormatSpec.VERSION401;
@@ -59,21 +60,42 @@
         return formatVersion >= FormatSpec.VERSION4_DEV;
     }
 
-    private File createEmptyDictionaryAndGetFile(final String dictId,
-            final int formatVersion) throws IOException {
+    private HashSet<File> mDictFilesToBeDeleted = new HashSet<>();
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mDictFilesToBeDeleted.clear();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        for (final File dictFile : mDictFilesToBeDeleted) {
+            dictFile.delete();
+        }
+        mDictFilesToBeDeleted.clear();
+        super.tearDown();
+    }
+
+    private File createEmptyDictionaryAndGetFile(final int formatVersion) {
         if (formatVersion == FormatSpec.VERSION4
                 || formatVersion == FormatSpec.VERSION4_ONLY_FOR_TESTING
                 || formatVersion == FormatSpec.VERSION4_DEV) {
-            return createEmptyVer4DictionaryAndGetFile(dictId, formatVersion);
+            try {
+                final File dictFile = createEmptyVer4DictionaryAndGetFile(formatVersion);
+                mDictFilesToBeDeleted.add(dictFile);
+                return dictFile;
+            } catch (final IOException e) {
+                fail(e.toString());
+            }
         } else {
-            throw new IOException("Dictionary format version " + formatVersion
-                    + " is not supported.");
+            fail("Dictionary format version " + formatVersion + " is not supported.");
         }
+        return null;
     }
 
-    private File createEmptyVer4DictionaryAndGetFile(final String dictId,
-            final int formatVersion) throws IOException {
-        final File file = File.createTempFile(dictId, TEST_DICT_FILE_EXTENSION,
+    private File createEmptyVer4DictionaryAndGetFile(final int formatVersion) throws IOException {
+        final File file = File.createTempFile(DICTIONARY_ID, TEST_DICT_FILE_EXTENSION,
                 getContext().getCacheDir());
         file.delete();
         file.mkdir();
@@ -87,6 +109,19 @@
         }
     }
 
+    private BinaryDictionary getBinaryDictionary(final File dictFile) {
+        return new BinaryDictionary(dictFile.getAbsolutePath(),
+                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+    }
+
+    private BinaryDictionary getEmptyBinaryDictionary(final int formatVersion) {
+        final File dictFile = createEmptyDictionaryAndGetFile(formatVersion);
+        return new BinaryDictionary(dictFile.getAbsolutePath(),
+                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+    }
+
     public void testIsValidDictionary() {
         for (final int formatVersion : DICT_FORMAT_VERSIONS) {
             testIsValidDictionary(formatVersion);
@@ -94,24 +129,15 @@
     }
 
     private void testIsValidDictionary(final int formatVersion) {
-        File dictFile = null;
-        try {
-            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
-        } catch (IOException e) {
-            fail("IOException while writing an initial dictionary : " + e);
-        }
-        BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
-                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
-                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+        final File dictFile = createEmptyDictionaryAndGetFile(formatVersion);
+        BinaryDictionary binaryDictionary = getBinaryDictionary(dictFile);
         assertTrue("binaryDictionary must be valid for existing valid dictionary file.",
                 binaryDictionary.isValidDictionary());
         binaryDictionary.close();
         assertFalse("binaryDictionary must be invalid after closing.",
                 binaryDictionary.isValidDictionary());
         FileUtils.deleteRecursively(dictFile);
-        binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), 0 /* offset */,
-                dictFile.length(), true /* useFullEditDistance */, Locale.getDefault(),
-                TEST_LOCALE, true /* isUpdatable */);
+        binaryDictionary = getBinaryDictionary(dictFile);
         assertFalse("binaryDictionary must be invalid for not existing dictionary file.",
                 binaryDictionary.isValidDictionary());
         binaryDictionary.close();
@@ -124,15 +150,10 @@
     }
 
     private void testConstructingDictionaryOnMemory(final int formatVersion) {
-        File dictFile = null;
-        try {
-            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
-        } catch (IOException e) {
-            fail("IOException while writing an initial dictionary : " + e);
-        }
+        final File dictFile = createEmptyDictionaryAndGetFile(formatVersion);
         FileUtils.deleteRecursively(dictFile);
         assertFalse(dictFile.exists());
-        BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+        final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
                 true /* useFullEditDistance */, Locale.getDefault(), TEST_LOCALE, formatVersion,
                 new HashMap<String, String>());
         assertTrue(binaryDictionary.isValidDictionary());
@@ -147,7 +168,6 @@
         assertEquals(formatVersion, binaryDictionary.getFormatVersion());
         assertEquals(probability, binaryDictionary.getFrequency("word"));
         binaryDictionary.close();
-        dictFile.delete();
     }
 
     public void testAddTooLongWord() {
@@ -157,16 +177,7 @@
     }
 
     private void testAddTooLongWord(final int formatVersion) {
-        File dictFile = null;
-        try {
-            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
-        } catch (IOException e) {
-            fail("IOException while writing an initial dictionary : " + e);
-        }
-        final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
-                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
-                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
-
+        final BinaryDictionary binaryDictionary = getEmptyBinaryDictionary(formatVersion);
         final StringBuffer stringBuilder = new StringBuffer();
         for (int i = 0; i < Constants.DICTIONARY_MAX_WORD_LENGTH; i++) {
             stringBuilder.append('a');
@@ -195,7 +206,6 @@
         assertEquals(BinaryDictionary.NOT_A_PROBABILITY,
                 binaryDictionary.getFrequency(invalidLongWord));
         assertEquals(updatedProbability, binaryDictionary.getFrequency("abc"));
-        dictFile.delete();
     }
 
     private static void addUnigramWord(final BinaryDictionary binaryDictionary, final String word,
@@ -256,16 +266,7 @@
     }
 
     private void testAddUnigramWord(final int formatVersion) {
-        File dictFile = null;
-        try {
-            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
-        } catch (IOException e) {
-            fail("IOException while writing an initial dictionary : " + e);
-        }
-        BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
-                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
-                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
-
+        final BinaryDictionary binaryDictionary = getEmptyBinaryDictionary(formatVersion);
         final int probability = 100;
         addUnigramWord(binaryDictionary, "aaa", probability);
         // Reallocate and create.
@@ -289,8 +290,6 @@
         assertEquals(probability, binaryDictionary.getFrequency("aaaa"));
         assertEquals(probability, binaryDictionary.getFrequency("a"));
         assertEquals(updatedProbability, binaryDictionary.getFrequency("aaa"));
-
-        dictFile.delete();
     }
 
     public void testRandomlyAddUnigramWord() {
@@ -303,16 +302,7 @@
         final int wordCount = 1000;
         final int codePointSetSize = 50;
         final long seed = System.currentTimeMillis();
-
-        File dictFile = null;
-        try {
-            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
-        } catch (IOException e) {
-            fail("IOException while writing an initial dictionary : " + e);
-        }
-        BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
-                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
-                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+        final BinaryDictionary binaryDictionary = getEmptyBinaryDictionary(formatVersion);
 
         final HashMap<String, Integer> probabilityMap = new HashMap<>();
         // Test a word that isn't contained within the dictionary.
@@ -328,7 +318,6 @@
         for (String word : probabilityMap.keySet()) {
             assertEquals(word, (int)probabilityMap.get(word), binaryDictionary.getFrequency(word));
         }
-        dictFile.delete();
     }
 
     public void testAddBigramWords() {
@@ -338,15 +327,7 @@
     }
 
     private void testAddBigramWords(final int formatVersion) {
-        File dictFile = null;
-        try {
-            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
-        } catch (IOException e) {
-            fail("IOException while writing an initial dictionary : " + e);
-        }
-        BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
-                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
-                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+        final BinaryDictionary binaryDictionary = getEmptyBinaryDictionary(formatVersion);
 
         final int unigramProbability = 100;
         final int bigramProbability = 150;
@@ -405,8 +386,6 @@
             assertEquals(updatedBigramProbability,
                     getBigramProbability(binaryDictionary, "abcde", "fghij"));
         }
-
-        dictFile.delete();
     }
 
     public void testRandomlyAddBigramWords() {
@@ -421,16 +400,7 @@
         final int codePointSetSize = 50;
         final long seed = System.currentTimeMillis();
         final Random random = new Random(seed);
-
-        File dictFile = null;
-        try {
-            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
-        } catch (IOException e) {
-            fail("IOException while writing an initial dictionary : " + e);
-        }
-        BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
-                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
-                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+        final BinaryDictionary binaryDictionary = getEmptyBinaryDictionary(formatVersion);
 
         final ArrayList<String> words = new ArrayList<>();
         final ArrayList<Pair<String, String>> bigramWords = new ArrayList<>();
@@ -470,8 +440,6 @@
                         getBigramProbability(binaryDictionary, bigram.first, bigram.second));
             }
         }
-
-        dictFile.delete();
     }
 
     public void testRemoveBigramWords() {
@@ -481,15 +449,7 @@
     }
 
     private void testRemoveBigramWords(final int formatVersion) {
-        File dictFile = null;
-        try {
-            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
-        } catch (IOException e) {
-            fail("IOException while writing an initial dictionary : " + e);
-        }
-        BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
-                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
-                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+        final BinaryDictionary binaryDictionary = getEmptyBinaryDictionary(formatVersion);
         final int unigramProbability = 100;
         final int bigramProbability = 150;
         addUnigramWord(binaryDictionary, "aaa", unigramProbability);
@@ -522,8 +482,6 @@
         // Test remove non-existing bigram operation.
         removeBigramEntry(binaryDictionary, "aaa", "abb");
         removeBigramEntry(binaryDictionary, "bcc", "aaa");
-
-        dictFile.delete();
     }
 
     public void testAddTrigramWords() {
@@ -535,16 +493,7 @@
     }
 
     private void testAddTrigramWords(final int formatVersion) {
-        File dictFile = null;
-        try {
-            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
-        } catch (IOException e) {
-            fail("IOException while writing an initial dictionary : " + e);
-        }
-        BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
-                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
-                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
-
+        final BinaryDictionary binaryDictionary = getEmptyBinaryDictionary(formatVersion);
         final int unigramProbability = 100;
         final int trigramProbability = 150;
         final int updatedTrigramProbability = 200;
@@ -572,8 +521,6 @@
         assertEquals(Dictionary.NOT_A_PROBABILITY,
                 getTrigramProbability(binaryDictionary, "aaa", "abb", "bcc"));
         assertTrue(isValidBigram(binaryDictionary, "abb", "bcc"));
-
-        dictFile.delete();
     }
 
     public void testFlushDictionary() {
@@ -583,15 +530,8 @@
     }
 
     private void testFlushDictionary(final int formatVersion) {
-        File dictFile = null;
-        try {
-            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
-        } catch (IOException e) {
-            fail("IOException while writing an initial dictionary : " + e);
-        }
-        BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
-                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
-                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+        final File dictFile = createEmptyDictionaryAndGetFile(formatVersion);
+        BinaryDictionary binaryDictionary = getBinaryDictionary(dictFile);
 
         final int probability = 100;
         addUnigramWord(binaryDictionary, "aaa", probability);
@@ -611,23 +551,16 @@
         binaryDictionary.flush();
         binaryDictionary.close();
 
-        binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
-                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
-                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
-
+        binaryDictionary = getBinaryDictionary(dictFile);
         assertEquals(probability, binaryDictionary.getFrequency("aaa"));
         assertEquals(probability, binaryDictionary.getFrequency("abcd"));
         addUnigramWord(binaryDictionary, "bcde", probability);
         binaryDictionary.flush();
         binaryDictionary.close();
 
-        binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
-                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
-                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+        binaryDictionary = getBinaryDictionary(dictFile);
         assertEquals(probability, binaryDictionary.getFrequency("bcde"));
         binaryDictionary.close();
-
-        dictFile.delete();
     }
 
     public void testFlushWithGCDictionary() {
@@ -637,16 +570,8 @@
     }
 
     private void testFlushWithGCDictionary(final int formatVersion) {
-        File dictFile = null;
-        try {
-            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
-        } catch (IOException e) {
-            fail("IOException while writing an initial dictionary : " + e);
-        }
-        BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
-                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
-                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
-
+        final File dictFile = createEmptyDictionaryAndGetFile(formatVersion);
+        BinaryDictionary binaryDictionary = getBinaryDictionary(dictFile);
         final int unigramProbability = 100;
         final int bigramProbability = 150;
         addUnigramWord(binaryDictionary, "aaa", unigramProbability);
@@ -659,9 +584,7 @@
         binaryDictionary.flushWithGC();
         binaryDictionary.close();
 
-        binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
-                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
-                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+        binaryDictionary = getBinaryDictionary(dictFile);
         assertEquals(unigramProbability, binaryDictionary.getFrequency("aaa"));
         assertEquals(unigramProbability, binaryDictionary.getFrequency("abb"));
         assertEquals(unigramProbability, binaryDictionary.getFrequency("bcc"));
@@ -676,8 +599,6 @@
         assertFalse(isValidBigram(binaryDictionary, "aaa", "aaa"));
         binaryDictionary.flushWithGC();
         binaryDictionary.close();
-
-        dictFile.delete();
     }
 
     public void testAddBigramWordsAndFlashWithGC() {
@@ -694,16 +615,8 @@
         final long seed = System.currentTimeMillis();
         final Random random = new Random(seed);
 
-        File dictFile = null;
-        try {
-            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
-        } catch (IOException e) {
-            fail("IOException while writing an initial dictionary : " + e);
-        }
-
-        BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
-                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
-                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+        final File dictFile = createEmptyDictionaryAndGetFile(formatVersion);
+        BinaryDictionary binaryDictionary = getBinaryDictionary(dictFile);
 
         final ArrayList<String> words = new ArrayList<>();
         final ArrayList<Pair<String, String>> bigramWords = new ArrayList<>();
@@ -736,10 +649,7 @@
 
         binaryDictionary.flushWithGC();
         binaryDictionary.close();
-        binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
-                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
-                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
-
+        binaryDictionary = getBinaryDictionary(dictFile);
 
         for (final Pair<String, String> bigram : bigramWords) {
             final int bigramProbability = bigramProbabilities.get(bigram);
@@ -750,8 +660,6 @@
                         getBigramProbability(binaryDictionary, bigram.first, bigram.second));
             }
         }
-
-        dictFile.delete();
     }
 
     public void testRandomOperationsAndFlashWithGC() {
@@ -771,17 +679,9 @@
 
         final long seed = System.currentTimeMillis();
         final Random random = new Random(seed);
+        final File dictFile = createEmptyDictionaryAndGetFile(formatVersion);
+        BinaryDictionary binaryDictionary = getBinaryDictionary(dictFile);
 
-        File dictFile = null;
-        try {
-            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
-        } catch (IOException e) {
-            fail("IOException while writing an initial dictionary : " + e);
-        }
-
-        BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
-                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
-                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
         final ArrayList<String> words = new ArrayList<>();
         final ArrayList<Pair<String, String>> bigramWords = new ArrayList<>();
         final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random);
@@ -798,9 +698,7 @@
         binaryDictionary.close();
 
         for (int gcCount = 0; gcCount < flashWithGCIterationCount; gcCount++) {
-            binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
-                    0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
-                    Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+            binaryDictionary = getBinaryDictionary(dictFile);
             for (int opCount = 0; opCount < operationCountInEachIteration; opCount++) {
                 // Add unigram.
                 if (random.nextFloat() < addUnigramProb) {
@@ -867,8 +765,6 @@
             binaryDictionary.flushWithGC();
             binaryDictionary.close();
         }
-
-        dictFile.delete();
     }
 
     public void testAddManyUnigramsAndFlushWithGC() {
@@ -884,12 +780,7 @@
         final long seed = System.currentTimeMillis();
         final Random random = new Random(seed);
 
-        File dictFile = null;
-        try {
-            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
-        } catch (IOException e) {
-            fail("IOException while writing an initial dictionary : " + e);
-        }
+        final File dictFile = createEmptyDictionaryAndGetFile(formatVersion);
 
         final ArrayList<String> words = new ArrayList<>();
         final HashMap<String, Integer> unigramProbabilities = new HashMap<>();
@@ -897,9 +788,7 @@
 
         BinaryDictionary binaryDictionary;
         for (int i = 0; i < flashWithGCIterationCount; i++) {
-            binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
-                    0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
-                    Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+            binaryDictionary = getBinaryDictionary(dictFile);
             while(!binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) {
                 final String word = CodePointUtils.generateWord(random, codePointSet);
                 words.add(word);
@@ -917,8 +806,6 @@
             binaryDictionary.flushWithGC();
             binaryDictionary.close();
         }
-
-        dictFile.delete();
     }
 
     public void testUnigramAndBigramCount() {
@@ -934,13 +821,7 @@
         final int bigramCountPerIteration = 2000;
         final long seed = System.currentTimeMillis();
         final Random random = new Random(seed);
-
-        File dictFile = null;
-        try {
-            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
-        } catch (IOException e) {
-            fail("IOException while writing an initial dictionary : " + e);
-        }
+        final File dictFile = createEmptyDictionaryAndGetFile(formatVersion);
 
         final ArrayList<String> words = new ArrayList<>();
         final HashSet<Pair<String, String>> bigrams = new HashSet<>();
@@ -948,9 +829,7 @@
 
         BinaryDictionary binaryDictionary;
         for (int i = 0; i < flashWithGCIterationCount; i++) {
-            binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
-                    0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
-                    Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+            binaryDictionary = getBinaryDictionary(dictFile);
             for (int j = 0; j < unigramCountPerIteration; j++) {
                 final String word = CodePointUtils.generateWord(random, codePointSet);
                 words.add(word);
@@ -982,8 +861,6 @@
                             BinaryDictionary.BIGRAM_COUNT_QUERY)));
             binaryDictionary.close();
         }
-
-        dictFile.delete();
     }
 
     public void testAddMultipleDictionaryEntries() {
@@ -998,13 +875,7 @@
         final double bigramContinueRate = 0.9;
         final long seed = System.currentTimeMillis();
         final Random random = new Random(seed);
-
-        File dictFile = null;
-        try {
-            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
-        } catch (IOException e) {
-            fail("IOException while writing an initial dictionary : " + e);
-        }
+        final File dictFile = createEmptyDictionaryAndGetFile(formatVersion);
 
         final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random);
         final HashMap<String, Integer> unigramProbabilities = new HashMap<>();
@@ -1029,9 +900,7 @@
             prevWord = (random.nextDouble() < bigramContinueRate) ? word : null;
         }
 
-        final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
-                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
-                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+        final BinaryDictionary binaryDictionary = getBinaryDictionary(dictFile);
         binaryDictionary.addMultipleDictionaryEntries(languageModelParams);
 
         for (Map.Entry<String, Integer> entry : unigramProbabilities.entrySet()) {
@@ -1064,16 +933,8 @@
         final int BIGRAM_COUNT = 1000;
         final int codePointSetSize = 20;
         final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random);
-
-        File dictFile = null;
-        try {
-            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
-        } catch (IOException e) {
-            fail("IOException while writing an initial dictionary : " + e);
-        }
-        final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
-                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
-                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+        final File dictFile = createEmptyDictionaryAndGetFile(formatVersion);
+        final BinaryDictionary binaryDictionary = getBinaryDictionary(dictFile);
 
         final WordProperty invalidWordProperty = binaryDictionary.getWordProperty("dummyWord",
                 false /* isBeginningOfSentence */);
@@ -1105,7 +966,7 @@
             assertTrue(wordProperty.isValid());
             assertEquals(isNotAWord, wordProperty.mIsNotAWord);
             assertEquals(isBlacklisted, wordProperty.mIsBlacklistEntry);
-            assertEquals(false, wordProperty.mHasBigrams);
+            assertEquals(false, wordProperty.mHasNgrams);
             assertEquals(false, wordProperty.mHasShortcuts);
             assertEquals(unigramProbability, wordProperty.mProbabilityInfo.mProbability);
             assertTrue(wordProperty.mShortcutTargets.isEmpty());
@@ -1142,13 +1003,14 @@
             final HashSet<String> bigramWord1s = bigrams.get(word0);
             final WordProperty wordProperty = binaryDictionary.getWordProperty(word0,
                     false /* isBeginningOfSentence */);
-            assertEquals(bigramWord1s.size(), wordProperty.mBigrams.size());
-            for (int j = 0; j < wordProperty.mBigrams.size(); j++) {
-                final String word1 = wordProperty.mBigrams.get(j).mWord;
+            assertEquals(bigramWord1s.size(), wordProperty.mNgrams.size());
+            // TODO: Support ngram.
+            for (final WeightedString bigramTarget : wordProperty.getBigrams()) {
+                final String word1 = bigramTarget.mWord;
                 assertTrue(bigramWord1s.contains(word1));
                 if (canCheckBigramProbability(formatVersion)) {
                     final int bigramProbability = bigramProbabilities.get(new Pair<>(word0, word1));
-                    assertEquals(bigramProbability, wordProperty.mBigrams.get(j).getProbability());
+                    assertEquals(bigramProbability, bigramTarget.getProbability());
                 }
             }
         }
@@ -1167,16 +1029,7 @@
         final int BIGRAM_COUNT = 1000;
         final int codePointSetSize = 20;
         final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random);
-
-        File dictFile = null;
-        try {
-            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
-        } catch (IOException e) {
-            fail("IOException while writing an initial dictionary : " + e);
-        }
-        final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
-                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
-                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+        final BinaryDictionary binaryDictionary = getEmptyBinaryDictionary(formatVersion);
 
         final WordProperty invalidWordProperty = binaryDictionary.getWordProperty("dummyWord",
                 false /* isBeginningOfSentence */);
@@ -1235,15 +1088,18 @@
                     wordProperty.mProbabilityInfo.mProbability);
             wordSet.remove(word0);
             final HashSet<String> bigramWord1s = bigrams.get(word0);
-            for (int j = 0; j < wordProperty.mBigrams.size(); j++) {
-                final String word1 = wordProperty.mBigrams.get(j).mWord;
-                assertTrue(bigramWord1s.contains(word1));
-                final Pair<String, String> bigram = new Pair<>(word0, word1);
-                if (canCheckBigramProbability(formatVersion)) {
-                    final int bigramProbability = bigramProbabilitiesToCheckLater.get(bigram);
-                    assertEquals(bigramProbability, wordProperty.mBigrams.get(j).getProbability());
+            // TODO: Support ngram.
+            if (wordProperty.mHasNgrams) {
+                for (final WeightedString bigramTarget : wordProperty.getBigrams()) {
+                    final String word1 = bigramTarget.mWord;
+                    assertTrue(bigramWord1s.contains(word1));
+                    final Pair<String, String> bigram = new Pair<>(word0, word1);
+                    if (canCheckBigramProbability(formatVersion)) {
+                        final int bigramProbability = bigramProbabilitiesToCheckLater.get(bigram);
+                        assertEquals(bigramProbability, bigramTarget.getProbability());
+                    }
+                    bigramSet.remove(bigram);
                 }
-                bigramSet.remove(bigram);
             }
             token = result.mNextToken;
         } while (token != 0);
@@ -1258,15 +1114,7 @@
     }
 
     private void testAddShortcuts(final int formatVersion) {
-        File dictFile = null;
-        try {
-            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
-        } catch (IOException e) {
-            fail("IOException while writing an initial dictionary : " + e);
-        }
-        final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
-                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
-                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+        final BinaryDictionary binaryDictionary = getEmptyBinaryDictionary(formatVersion);
 
         final int unigramProbability = 100;
         final int shortcutProbability = 10;
@@ -1334,16 +1182,7 @@
         final ArrayList<String> words = new ArrayList<>();
         final HashMap<String, Integer> unigramProbabilities = new HashMap<>();
         final HashMap<String, HashMap<String, Integer>> shortcutTargets = new HashMap<>();
-
-        File dictFile = null;
-        try {
-            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
-        } catch (IOException e) {
-            fail("IOException while writing an initial dictionary : " + e);
-        }
-        final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
-                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
-                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+        final BinaryDictionary binaryDictionary = getEmptyBinaryDictionary(formatVersion);
 
         for (int i = 0; i < UNIGRAM_COUNT; i++) {
             final String word = CodePointUtils.generateWord(random, codePointSet);
@@ -1401,15 +1240,7 @@
     }
 
     private void testDictMigration(final int fromFormatVersion, final int toFormatVersion) {
-        File dictFile = null;
-        try {
-            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", fromFormatVersion);
-        } catch (IOException e) {
-            fail("IOException while writing an initial dictionary : " + e);
-        }
-        final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
-                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
-                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+        final BinaryDictionary binaryDictionary = getEmptyBinaryDictionary(fromFormatVersion);
         final int unigramProbability = 100;
         addUnigramWord(binaryDictionary, "aaa", unigramProbability);
         addUnigramWord(binaryDictionary, "bbb", unigramProbability);
@@ -1461,16 +1292,7 @@
         final int codePointSetSize = 50;
         final long seed = System.currentTimeMillis();
         final Random random = new Random(seed);
-
-        File dictFile = null;
-        try {
-            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", fromFormatVersion);
-        } catch (IOException e) {
-            fail("IOException while writing an initial dictionary : " + e);
-        }
-        final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
-                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
-                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+        final BinaryDictionary binaryDictionary = getEmptyBinaryDictionary(fromFormatVersion);
 
         final ArrayList<String> words = new ArrayList<>();
         final ArrayList<Pair<String, String>> bigrams = new ArrayList<>();
@@ -1536,15 +1358,7 @@
     }
 
     private void testBeginningOfSentence(final int formatVersion) {
-        File dictFile = null;
-        try {
-            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
-        } catch (IOException e) {
-            fail("IOException while writing an initial dictionary : " + e);
-        }
-        final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
-                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
-                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+        final BinaryDictionary binaryDictionary = getEmptyBinaryDictionary(formatVersion);
         final int dummyProbability = 0;
         final NgramContext beginningOfSentenceContext = NgramContext.BEGINNING_OF_SENTENCE;
         final int bigramProbability = 200;
@@ -1572,15 +1386,7 @@
     }
 
     private void testGetMaxFrequencyOfExactMatches(final int formatVersion) {
-        File dictFile = null;
-        try {
-            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion);
-        } catch (IOException e) {
-            fail("IOException while writing an initial dictionary : " + e);
-        }
-        final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
-                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
-                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+        final BinaryDictionary binaryDictionary = getEmptyBinaryDictionary(formatVersion);
         addUnigramWord(binaryDictionary, "abc", 10);
         addUnigramWord(binaryDictionary, "aBc", 15);
         assertEquals(15, binaryDictionary.getMaxFrequencyOfExactMatches("abc"));
diff --git a/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java b/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java
index 6e894de..30b0881 100644
--- a/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java
+++ b/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java
@@ -63,7 +63,7 @@
         final int typedLength = STRING_TO_TYPE.length();
         final int EXPECTED_SUGGESTION_SPAN_START = -1;
         final int EXPECTED_UNDERLINE_SPAN_START = 0;
-        final int EXPECTED_UNDERLINE_SPAN_END = 4;
+        final int EXPECTED_UNDERLINE_SPAN_END = 3;
         type(STRING_TO_TYPE);
         sleep(DELAY_TO_WAIT_FOR_UNDERLINE);
         runMessages();
diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTests.java b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
index 59b858d..ec249da 100644
--- a/tests/src/com/android/inputmethod/latin/InputLogicTests.java
+++ b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
@@ -159,8 +159,11 @@
     }
 
     public void testAutoCorrectWithSpaceThenRevert() {
+        // Backspacing to cancel the "tgis"->"this" autocorrection should result in
+        // a "phantom space": if the user presses space immediately after,
+        // only one space will be inserted in total.
         final String STRING_TO_TYPE = "tgis ";
-        final String EXPECTED_RESULT = "tgis ";
+        final String EXPECTED_RESULT = "tgis";
         type(STRING_TO_TYPE);
         mLatinIME.onUpdateSelection(0, 0, STRING_TO_TYPE.length(), STRING_TO_TYPE.length(), -1, -1);
         type(Constants.CODE_DELETE);
@@ -168,6 +171,24 @@
                 mEditText.getText().toString());
     }
 
+    public void testAutoCorrectWithSpaceThenRevertThenTypeMore() {
+        final String STRING_TO_TYPE_FIRST = "tgis ";
+        final String STRING_TO_TYPE_SECOND = "a";
+        final String EXPECTED_RESULT = "tgis a";
+        type(STRING_TO_TYPE_FIRST);
+        mLatinIME.onUpdateSelection(0, 0,
+                STRING_TO_TYPE_FIRST.length(), STRING_TO_TYPE_FIRST.length(), -1, -1);
+        type(Constants.CODE_DELETE);
+
+        type(STRING_TO_TYPE_SECOND);
+        mLatinIME.onUpdateSelection(STRING_TO_TYPE_FIRST.length(), STRING_TO_TYPE_FIRST.length(),
+                STRING_TO_TYPE_FIRST.length() - 1 + STRING_TO_TYPE_SECOND.length(),
+                STRING_TO_TYPE_FIRST.length() - 1 + STRING_TO_TYPE_SECOND.length(),
+                -1, -1);
+        assertEquals("auto-correct with space then revert then type more", EXPECTED_RESULT,
+                mEditText.getText().toString());
+    }
+
     public void testAutoCorrectToSelfDoesNotRevert() {
         final String STRING_TO_TYPE = "this ";
         final String EXPECTED_RESULT = "this";
diff --git a/tests/src/com/android/inputmethod/latin/LatinIMEForTests.java b/tests/src/com/android/inputmethod/latin/LatinIMEForTests.java
index e47c557..035c8d7 100644
--- a/tests/src/com/android/inputmethod/latin/LatinIMEForTests.java
+++ b/tests/src/com/android/inputmethod/latin/LatinIMEForTests.java
@@ -21,4 +21,16 @@
     public boolean isInputViewShown() {
         return true;
     }
+
+    private boolean deallocateMemoryWasPerformed = false;
+
+    @Override
+    protected void deallocateMemory() {
+        super.deallocateMemory();
+        deallocateMemoryWasPerformed = true;
+    }
+
+    public boolean getDeallocateMemoryWasPerformed() {
+        return deallocateMemoryWasPerformed;
+    }
 }
diff --git a/tests/src/com/android/inputmethod/latin/LatinImeTests.java b/tests/src/com/android/inputmethod/latin/LatinImeTests.java
new file mode 100644
index 0000000..c6f6313
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/LatinImeTests.java
@@ -0,0 +1,40 @@
+/*
+ * 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;
+
+import android.test.suitebuilder.annotation.LargeTest;
+
+@LargeTest
+public class LatinImeTests extends InputTestsBase {
+    public void testDeferredDeallocation_doesntHappenBeforeTimeout() {
+        mLatinIME.mHandler.onFinishInputView(true);
+        runMessages();
+        sleep(1000); // 1s
+        runMessages();
+        assertFalse("memory deallocation performed before timeout passed",
+                ((LatinIMEForTests)mLatinIME).getDeallocateMemoryWasPerformed());
+    }
+
+    public void testDeferredDeallocation_doesHappenAfterTimeout() {
+        mLatinIME.mHandler.onFinishInputView(true);
+        runMessages();
+        sleep(11000); // 11s (timeout is at 10s)
+        runMessages();
+        assertTrue("memory deallocation not performed although timeout passed",
+                ((LatinIMEForTests)mLatinIME).getDeallocateMemoryWasPerformed());
+    }
+}
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
index 406046a..637ea4e 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
@@ -304,6 +304,40 @@
                 "unigram with various code points"));
     }
 
+    public void testCharacterTableIsPresent() throws IOException, UnsupportedFormatException {
+        final String[] wordSource = {"words", "used", "for", "testing", "a", "code point", "table"};
+        final List<String> words = Arrays.asList(wordSource);
+        final String correctCodePointTable = "toesdrniawuplgfcb ";
+        final String dictName = "codePointTableTest";
+        final String dictVersion = Long.toString(System.currentTimeMillis());
+        final String codePointTableAttribute = DictionaryHeader.CODE_POINT_TABLE_KEY;
+        final File file = BinaryDictUtils.getDictFile(dictName, dictVersion,
+                BinaryDictUtils.VERSION201_OPTIONS, getContext().getCacheDir());
+
+        // Write a test dictionary
+        final DictEncoder dictEncoder = new Ver2DictEncoder(file,
+                Ver2DictEncoder.CODE_POINT_TABLE_ON);
+        final FormatSpec.FormatOptions formatOptions =
+                new FormatSpec.FormatOptions(
+                        FormatSpec.MINIMUM_SUPPORTED_VERSION_OF_CODE_POINT_TABLE);
+        final FusionDictionary sourcedict = new FusionDictionary(new PtNodeArray(),
+                BinaryDictUtils.makeDictionaryOptions(dictName, dictVersion, formatOptions));
+        addUnigrams(words.size(), sourcedict, words, null /* shortcutMap */);
+        dictEncoder.writeDictionary(sourcedict, formatOptions);
+
+        // Read the dictionary
+        final DictDecoder dictDecoder = BinaryDictIOUtils.getDictDecoder(file, 0, file.length(),
+                DictDecoder.USE_BYTEARRAY);
+        final DictionaryHeader fileHeader = dictDecoder.readHeader();
+        // Check if codePointTable is present
+        assertTrue("codePointTable is not present",
+                fileHeader.mDictionaryOptions.mAttributes.containsKey(codePointTableAttribute));
+        final String codePointTable =
+                fileHeader.mDictionaryOptions.mAttributes.get(codePointTableAttribute);
+        // Check if codePointTable is correct
+        assertEquals("codePointTable is incorrect", codePointTable, correctCodePointTable);
+    }
+
     // Unit test for CharEncoding.readString and CharEncoding.writeString.
     public void testCharEncoding() {
         // the max length of a word in sWords is less than 50.
@@ -312,7 +346,7 @@
         final DictBuffer dictBuffer = new ByteArrayDictBuffer(buffer);
         for (final String word : sWords) {
             Arrays.fill(buffer, (byte) 0);
-            CharEncoding.writeString(buffer, 0, word);
+            CharEncoding.writeString(buffer, 0, word, null);
             dictBuffer.position(0);
             final String str = CharEncoding.readString(dictBuffer);
             assertEquals(word, str);
@@ -682,11 +716,13 @@
                 }
                 assertTrue(shortcutList.isEmpty());
             }
-            for (int j = 0; j < wordProperty.mBigrams.size(); j++) {
-                final String word1 = wordProperty.mBigrams.get(j).mWord;
-                final Pair<String, String> bigram = new Pair<>(word0, word1);
-                assertTrue(bigramSet.contains(bigram));
-                bigramSet.remove(bigram);
+            if (wordProperty.mHasNgrams) {
+                for (final WeightedString bigramTarget : wordProperty.getBigrams()) {
+                    final String word1 = bigramTarget.mWord;
+                    final Pair<String, String> bigram = new Pair<>(word0, word1);
+                    assertTrue(bigramSet.contains(bigram));
+                    bigramSet.remove(bigram);
+                }
             }
             token = result.mNextToken;
         } while (token != 0);
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java
index 96604a1..1f3ee19 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java
@@ -17,11 +17,11 @@
 package com.android.inputmethod.latin.makedict;
 
 import com.android.inputmethod.annotations.UsedForTesting;
-
 import java.io.File;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.nio.ByteBuffer;
+import java.util.HashMap;
 
 /**
  * Decodes binary files for a FusionDictionary.
@@ -109,15 +109,19 @@
      * A class grouping utility function for our specific character encoding.
      */
     static final class CharEncoding {
-        private static final int MINIMAL_ONE_BYTE_CHARACTER_VALUE = 0x20;
-        private static final int MAXIMAL_ONE_BYTE_CHARACTER_VALUE = 0xFF;
 
         /**
          * Helper method to find out whether this code fits on one byte
          */
-        private static boolean fitsOnOneByte(final int character) {
-            return character >= MINIMAL_ONE_BYTE_CHARACTER_VALUE
-                    && character <= MAXIMAL_ONE_BYTE_CHARACTER_VALUE;
+        private static boolean fitsOnOneByte(int character,
+                final HashMap<Integer, Integer> codePointToOneByteCodeMap) {
+            if (codePointToOneByteCodeMap != null) {
+                if (codePointToOneByteCodeMap.containsKey(character)) {
+                    character = codePointToOneByteCodeMap.get(character);
+                }
+            }
+            return character >= FormatSpec.MINIMAL_ONE_BYTE_CHARACTER_VALUE
+                    && character <= FormatSpec.MAXIMAL_ONE_BYTE_CHARACTER_VALUE;
         }
 
         /**
@@ -137,9 +141,10 @@
          * @param character the character code.
          * @return the size in binary encoded-form, either 1 or 3 bytes.
          */
-        static int getCharSize(final int character) {
+        static int getCharSize(final int character,
+                final HashMap<Integer, Integer> codePointToOneByteCodeMap) {
             // See char encoding in FusionDictionary.java
-            if (fitsOnOneByte(character)) return 1;
+            if (fitsOnOneByte(character, codePointToOneByteCodeMap)) return 1;
             if (FormatSpec.INVALID_CHARACTER == character) return 1;
             return 3;
         }
@@ -147,9 +152,10 @@
         /**
          * Compute the byte size of a character array.
          */
-        static int getCharArraySize(final int[] chars) {
+        static int getCharArraySize(final int[] chars,
+                final HashMap<Integer, Integer> codePointToOneByteCodeMap) {
             int size = 0;
-            for (int character : chars) size += getCharSize(character);
+            for (int character : chars) size += getCharSize(character, codePointToOneByteCodeMap);
             return size;
         }
 
@@ -159,11 +165,19 @@
          * @param codePoints the code point array to write.
          * @param buffer the byte buffer to write to.
          * @param index the index in buffer to write the character array to.
+         * @param codePointToOneByteCodeMap the map to convert the code point.
          * @return the index after the last character.
          */
-        static int writeCharArray(final int[] codePoints, final byte[] buffer, int index) {
+        static int writeCharArray(final int[] codePoints, final byte[] buffer, int index,
+                final HashMap<Integer, Integer> codePointToOneByteCodeMap) {
             for (int codePoint : codePoints) {
-                if (1 == getCharSize(codePoint)) {
+                if (codePointToOneByteCodeMap != null) {
+                    if (codePointToOneByteCodeMap.containsKey(codePoint)) {
+                        // Convert code points
+                        codePoint = codePointToOneByteCodeMap.get(codePoint);
+                    }
+                }
+                if (1 == getCharSize(codePoint, codePointToOneByteCodeMap)) {
                     buffer[index++] = (byte)codePoint;
                 } else {
                     buffer[index++] = (byte)(0xFF & (codePoint >> 16));
@@ -184,12 +198,19 @@
          * @param word the string to write.
          * @return the size written, in bytes.
          */
-        static int writeString(final byte[] buffer, final int origin, final String word) {
+        static int writeString(final byte[] buffer, final int origin, final String word,
+                final HashMap<Integer, Integer> codePointToOneByteCodeMap) {
             final int length = word.length();
             int index = origin;
             for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) {
-                final int codePoint = word.codePointAt(i);
-                if (1 == getCharSize(codePoint)) {
+                int codePoint = word.codePointAt(i);
+                if (codePointToOneByteCodeMap != null) {
+                    if (codePointToOneByteCodeMap.containsKey(codePoint)) {
+                        // Convert code points
+                        codePoint = codePointToOneByteCodeMap.get(codePoint);
+                    }
+                }
+                if (1 == getCharSize(codePoint, codePointToOneByteCodeMap)) {
                     buffer[index++] = (byte)codePoint;
                 } else {
                     buffer[index++] = (byte)(0xFF & (codePoint >> 16));
@@ -210,12 +231,13 @@
          * @param word the string to write.
          * @return the size written, in bytes.
          */
-        static int writeString(final OutputStream stream, final String word) throws IOException {
+        static int writeString(final OutputStream stream, final String word,
+                final HashMap<Integer, Integer> codePointToOneByteCodeMap) throws IOException {
             final int length = word.length();
             int written = 0;
             for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) {
                 final int codePoint = word.codePointAt(i);
-                final int charSize = getCharSize(codePoint);
+                final int charSize = getCharSize(codePoint, codePointToOneByteCodeMap);
                 if (1 == charSize) {
                     stream.write((byte) codePoint);
                 } else {
@@ -253,7 +275,7 @@
          */
         static int readChar(final DictBuffer dictBuffer) {
             int character = dictBuffer.readUnsignedByte();
-            if (!fitsOnOneByte(character)) {
+            if (!fitsOnOneByte(character, null)) {
                 if (FormatSpec.PTNODE_CHARACTERS_TERMINATOR == character) {
                     return FormatSpec.INVALID_CHARACTER;
                 }
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java
index 12290e6..2d536d8 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java
@@ -61,8 +61,9 @@
      * @param characters the character array
      * @return the size of the char array, including the terminator if any
      */
-    static int getPtNodeCharactersSize(final int[] characters) {
-        int size = CharEncoding.getCharArraySize(characters);
+    static int getPtNodeCharactersSize(final int[] characters,
+            final HashMap<Integer, Integer> codePointToOneByteCodeMap) {
+        int size = CharEncoding.getCharArraySize(characters, codePointToOneByteCodeMap);
         if (characters.length > 1) size += FormatSpec.PTNODE_TERMINATOR_SIZE;
         return size;
     }
@@ -76,8 +77,9 @@
      * @param ptNode the PtNode
      * @return the size of the char array, including the terminator if any
      */
-    private static int getPtNodeCharactersSize(final PtNode ptNode) {
-        return getPtNodeCharactersSize(ptNode.mChars);
+    private static int getPtNodeCharactersSize(final PtNode ptNode,
+            final HashMap<Integer, Integer> codePointToOneByteCodeMap) {
+        return getPtNodeCharactersSize(ptNode.mChars, codePointToOneByteCodeMap);
     }
 
     /**
@@ -92,13 +94,14 @@
     /**
      * Compute the size of a shortcut in bytes.
      */
-    private static int getShortcutSize(final WeightedString shortcut) {
+    private static int getShortcutSize(final WeightedString shortcut,
+            final HashMap<Integer, Integer> codePointToOneByteCodeMap) {
         int size = FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE;
         final String word = shortcut.mWord;
         final int length = word.length();
         for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) {
             final int codePoint = word.codePointAt(i);
-            size += CharEncoding.getCharSize(codePoint);
+            size += CharEncoding.getCharSize(codePoint, codePointToOneByteCodeMap);
         }
         size += FormatSpec.PTNODE_TERMINATOR_SIZE;
         return size;
@@ -110,11 +113,12 @@
      * This is known in advance and does not change according to position in the file
      * like address lists do.
      */
-    static int getShortcutListSize(final ArrayList<WeightedString> shortcutList) {
+    static int getShortcutListSize(final ArrayList<WeightedString> shortcutList,
+            final HashMap<Integer, Integer> codePointToOneByteCodeMap) {
         if (null == shortcutList || shortcutList.isEmpty()) return 0;
         int size = FormatSpec.PTNODE_SHORTCUT_LIST_SIZE_SIZE;
         for (final WeightedString shortcut : shortcutList) {
-            size += getShortcutSize(shortcut);
+            size += getShortcutSize(shortcut, codePointToOneByteCodeMap);
         }
         return size;
     }
@@ -125,14 +129,16 @@
      * @param ptNode the PtNode to compute the size of.
      * @return the maximum size of the PtNode.
      */
-    private static int getPtNodeMaximumSize(final PtNode ptNode) {
-        int size = getNodeHeaderSize(ptNode);
+    private static int getPtNodeMaximumSize(final PtNode ptNode,
+            final HashMap<Integer, Integer> codePointToOneByteCodeMap) {
+        int size = getNodeHeaderSize(ptNode, codePointToOneByteCodeMap);
         if (ptNode.isTerminal()) {
             // If terminal, one byte for the frequency.
             size += FormatSpec.PTNODE_FREQUENCY_SIZE;
         }
         size += FormatSpec.PTNODE_MAX_ADDRESS_SIZE; // For children address
-        size += getShortcutListSize(ptNode.mShortcutTargets);
+        // TODO: Use codePointToOneByteCodeMap for shortcuts.
+        size += getShortcutListSize(ptNode.mShortcutTargets, null /* codePointToOneByteCodeMap */);
         if (null != ptNode.mBigrams) {
             size += (FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE
                     + FormatSpec.PTNODE_ATTRIBUTE_MAX_ADDRESS_SIZE)
@@ -148,10 +154,11 @@
      *
      * @param ptNodeArray the node array to compute the maximum size of.
      */
-    private static void calculatePtNodeArrayMaximumSize(final PtNodeArray ptNodeArray) {
+    private static void calculatePtNodeArrayMaximumSize(final PtNodeArray ptNodeArray,
+            final HashMap<Integer, Integer> codePointToOneByteCodeMap) {
         int size = getPtNodeCountSize(ptNodeArray);
         for (PtNode node : ptNodeArray.mData) {
-            final int nodeSize = getPtNodeMaximumSize(node);
+            final int nodeSize = getPtNodeMaximumSize(node, codePointToOneByteCodeMap);
             node.mCachedSize = nodeSize;
             size += nodeSize;
         }
@@ -163,8 +170,10 @@
      *
      * @param ptNode the PtNode of which to compute the size of the header
      */
-    private static int getNodeHeaderSize(final PtNode ptNode) {
-        return FormatSpec.PTNODE_FLAGS_SIZE + getPtNodeCharactersSize(ptNode);
+    private static int getNodeHeaderSize(final PtNode ptNode,
+            final HashMap<Integer, Integer> codePointToOneByteCodeMap) {
+        return FormatSpec.PTNODE_FLAGS_SIZE + getPtNodeCharactersSize(ptNode,
+                codePointToOneByteCodeMap);
     }
 
     /**
@@ -367,7 +376,8 @@
      * @return false if none of the cached addresses inside the node array changed, true otherwise.
      */
     private static boolean computeActualPtNodeArraySize(final PtNodeArray ptNodeArray,
-            final FusionDictionary dict) {
+            final FusionDictionary dict,
+            final HashMap<Integer, Integer> codePointToOneByteCodeMap) {
         boolean changed = false;
         int size = getPtNodeCountSize(ptNodeArray);
         for (PtNode ptNode : ptNodeArray.mData) {
@@ -375,7 +385,7 @@
             if (ptNode.mCachedAddressAfterUpdate != ptNode.mCachedAddressBeforeUpdate) {
                 changed = true;
             }
-            int nodeSize = getNodeHeaderSize(ptNode);
+            int nodeSize = getNodeHeaderSize(ptNode, codePointToOneByteCodeMap);
             if (ptNode.isTerminal()) {
                 nodeSize += FormatSpec.PTNODE_FREQUENCY_SIZE;
             }
@@ -383,7 +393,9 @@
                 nodeSize += getByteSize(getOffsetToTargetNodeArrayDuringUpdate(ptNodeArray,
                         nodeSize + size, ptNode.mChildren));
             }
-            nodeSize += getShortcutListSize(ptNode.mShortcutTargets);
+            // TODO: Use codePointToOneByteCodeMap for shortcuts.
+            nodeSize += getShortcutListSize(ptNode.mShortcutTargets,
+                    null /* codePointToOneByteCodeMap */);
             if (null != ptNode.mBigrams) {
                 for (WeightedString bigram : ptNode.mBigrams) {
                     final int offset = getOffsetToTargetPtNodeDuringUpdate(ptNodeArray,
@@ -454,10 +466,11 @@
      * @return the same array it was passed. The nodes have been updated for address and size.
      */
     /* package */ static ArrayList<PtNodeArray> computeAddresses(final FusionDictionary dict,
-            final ArrayList<PtNodeArray> flatNodes) {
+            final ArrayList<PtNodeArray> flatNodes,
+            final HashMap<Integer, Integer> codePointToOneByteCodeMap) {
         // First get the worst possible sizes and offsets
         for (final PtNodeArray n : flatNodes) {
-            calculatePtNodeArrayMaximumSize(n);
+            calculatePtNodeArrayMaximumSize(n, codePointToOneByteCodeMap);
         }
         final int offset = initializePtNodeArraysCachedAddresses(flatNodes);
 
@@ -472,7 +485,8 @@
             for (final PtNodeArray ptNodeArray : flatNodes) {
                 ptNodeArray.mCachedAddressAfterUpdate = ptNodeArrayStartOffset;
                 final int oldNodeArraySize = ptNodeArray.mCachedSize;
-                final boolean changed = computeActualPtNodeArraySize(ptNodeArray, dict);
+                final boolean changed = computeActualPtNodeArraySize(ptNodeArray, dict,
+                        codePointToOneByteCodeMap);
                 final int newNodeArraySize = ptNodeArray.mCachedSize;
                 if (oldNodeArraySize < newNodeArraySize) {
                     throw new RuntimeException("Increased size ?!");
@@ -686,9 +700,10 @@
                 + (frequency & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY);
     }
 
-    /* package */ static final int getChildrenPosition(final PtNode ptNode) {
+    /* package */ static final int getChildrenPosition(final PtNode ptNode,
+            final HashMap<Integer, Integer> codePointToOneByteCodeMap) {
         int positionOfChildrenPosField = ptNode.mCachedAddressAfterUpdate
-                + getNodeHeaderSize(ptNode);
+                + getNodeHeaderSize(ptNode, codePointToOneByteCodeMap);
         if (ptNode.isTerminal()) {
             // A terminal node has the frequency.
             // If positionOfChildrenPosField is incorrect, we may crash when jumping to the children
@@ -705,10 +720,12 @@
      * @param dict the dictionary the node array is a part of (for relative offsets).
      * @param dictEncoder the dictionary encoder.
      * @param ptNodeArray the node array to write.
+     * @param codePointToOneByteCodeMap the map to convert the code points.
      */
     @SuppressWarnings("unused")
     /* package */ static void writePlacedPtNodeArray(final FusionDictionary dict,
-            final DictEncoder dictEncoder, final PtNodeArray ptNodeArray) {
+            final DictEncoder dictEncoder, final PtNodeArray ptNodeArray,
+            final HashMap<Integer, Integer> codePointToOneByteCodeMap) {
         // TODO: Make the code in common with BinaryDictIOUtils#writePtNode
         dictEncoder.setPosition(ptNodeArray.mCachedAddressAfterUpdate);
 
@@ -727,7 +744,7 @@
                         + FormatSpec.MAX_TERMINAL_FREQUENCY
                         + " : " + ptNode.mProbabilityInfo.toString());
             }
-            dictEncoder.writePtNode(ptNode, dict);
+            dictEncoder.writePtNode(ptNode, dict, codePointToOneByteCodeMap);
         }
         if (dictEncoder.getPosition() != ptNodeArray.mCachedAddressAfterUpdate
                 + ptNodeArray.mCachedSize) {
@@ -834,12 +851,16 @@
         // Write out the options.
         for (final String key : dict.mOptions.mAttributes.keySet()) {
             final String value = dict.mOptions.mAttributes.get(key);
-            CharEncoding.writeString(headerBuffer, key);
-            CharEncoding.writeString(headerBuffer, value);
+            CharEncoding.writeString(headerBuffer, key, null);
+            CharEncoding.writeString(headerBuffer, value, null);
         }
-
-        // TODO: Write out the code point table.
-
+        // Write out the codePointTable if there is codePointOccurrenceArray.
+        if (codePointOccurrenceArray != null) {
+            final String codePointTableString =
+                    encodeCodePointTable(codePointOccurrenceArray);
+            CharEncoding.writeString(headerBuffer, DictionaryHeader.CODE_POINT_TABLE_KEY, null);
+            CharEncoding.writeString(headerBuffer, codePointTableString, null);
+        }
         final int size = headerBuffer.size();
         final byte[] bytes = headerBuffer.toByteArray();
         // Write out the header size.
@@ -857,10 +878,30 @@
         final HashMap<Integer, Integer> mCodePointToOneByteCodeMap;
         final ArrayList<Entry<Integer, Integer>> mCodePointOccurrenceArray;
 
+        // Let code point table empty for version 200 dictionary which used in test
+        CodePointTable() {
+            mCodePointToOneByteCodeMap = null;
+            mCodePointOccurrenceArray = null;
+        }
+
         CodePointTable(final HashMap<Integer, Integer> codePointToOneByteCodeMap,
                 final ArrayList<Entry<Integer, Integer>> codePointOccurrenceArray) {
             mCodePointToOneByteCodeMap = codePointToOneByteCodeMap;
             mCodePointOccurrenceArray = codePointOccurrenceArray;
         }
     }
+
+    private static String encodeCodePointTable(
+            final ArrayList<Entry<Integer, Integer>> codePointOccurrenceArray) {
+        final StringBuilder codePointTableString = new StringBuilder();
+        int currentCodePointTableIndex = FormatSpec.MINIMAL_ONE_BYTE_CHARACTER_VALUE;
+        for (final Entry<Integer, Integer> entry : codePointOccurrenceArray) {
+            // Native reads the table as a string
+            codePointTableString.appendCodePoint(entry.getKey());
+            if (FormatSpec.MAXIMAL_ONE_BYTE_CHARACTER_VALUE < ++currentCodePointTableIndex) {
+                break;
+            }
+        }
+        return codePointTableString.toString();
+    }
 }
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java
index 965479d..8eabf74 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java
@@ -30,6 +30,8 @@
 
     public static final FormatSpec.FormatOptions VERSION2_OPTIONS =
             new FormatSpec.FormatOptions(FormatSpec.VERSION2);
+    public static final FormatSpec.FormatOptions VERSION201_OPTIONS =
+            new FormatSpec.FormatOptions(FormatSpec.VERSION201);
     public static final FormatSpec.FormatOptions VERSION4_OPTIONS_WITHOUT_TIMESTAMP =
             new FormatSpec.FormatOptions(FormatSpec.VERSION4, false /* hasTimestamp */);
     public static final FormatSpec.FormatOptions VERSION4_OPTIONS_WITH_TIMESTAMP =
@@ -52,7 +54,8 @@
 
     public static File getDictFile(final String name, final String version,
             final FormatOptions formatOptions, final File directory) {
-        if (formatOptions.mVersion == FormatSpec.VERSION2) {
+        if (formatOptions.mVersion == FormatSpec.VERSION2
+                || formatOptions.mVersion == FormatSpec.VERSION201) {
             return new File(directory, name + "." + version + TEST_DICT_FILE_EXTENSION);
         } else if (formatOptions.mVersion == FormatSpec.VERSION4) {
             return new File(directory, name + "." + version);
diff --git a/tests/src/com/android/inputmethod/latin/makedict/DictEncoder.java b/tests/src/com/android/inputmethod/latin/makedict/DictEncoder.java
index 645fd5c..10dd003 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/DictEncoder.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/DictEncoder.java
@@ -21,6 +21,7 @@
 import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
 
 import java.io.IOException;
+import java.util.HashMap;
 
 /**
  * An interface of binary dictionary encoder.
@@ -33,5 +34,6 @@
     public void setPosition(final int position);
     public int getPosition();
     public void writePtNodeCount(final int ptNodeCount);
-    public void writePtNode(final PtNode ptNode, final FusionDictionary dict);
+    public void writePtNode(final PtNode ptNode, final FusionDictionary dict,
+            final HashMap<Integer, Integer> codePointToOneByteCodeMap);
 }
diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java
index 65b84d5..6227f13 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java
@@ -177,7 +177,8 @@
         if (header == null) {
             throw new IOException("Cannot read the dictionary header.");
         }
-        if (header.mFormatOptions.mVersion != FormatSpec.VERSION2) {
+        if (header.mFormatOptions.mVersion != FormatSpec.VERSION2 &&
+                header.mFormatOptions.mVersion != FormatSpec.VERSION201) {
             throw new UnsupportedFormatException("File header has a wrong version : "
                     + header.mFormatOptions.mVersion);
         }
@@ -200,19 +201,19 @@
         if (0 != (flags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS)) {
             int index = 0;
             int character = CharEncoding.readChar(mDictBuffer);
-            addressPointer += CharEncoding.getCharSize(character);
+            addressPointer += CharEncoding.getCharSize(character, null);
             while (FormatSpec.INVALID_CHARACTER != character) {
                 // FusionDictionary is making sure that the length of the word is smaller than
                 // MAX_WORD_LENGTH.
                 // So we'll never write past the end of mCharacterBuffer.
                 mCharacterBuffer[index++] = character;
                 character = CharEncoding.readChar(mDictBuffer);
-                addressPointer += CharEncoding.getCharSize(character);
+                addressPointer += CharEncoding.getCharSize(character, null);
             }
             characters = Arrays.copyOfRange(mCharacterBuffer, 0, index);
         } else {
             final int character = CharEncoding.readChar(mDictBuffer);
-            addressPointer += CharEncoding.getCharSize(character);
+            addressPointer += CharEncoding.getCharSize(character, null);
             characters = new int[] { character };
         }
         final ProbabilityInfo probabilityInfo;
@@ -292,11 +293,11 @@
         }
         // Insert bigrams into the fusion dictionary.
         for (final WordProperty wordProperty : wordProperties) {
-            if (wordProperty.mBigrams == null) {
+            if (!wordProperty.mHasNgrams) {
                 continue;
             }
             final String word0 = wordProperty.mWord;
-            for (final WeightedString bigram : wordProperty.mBigrams) {
+            for (final WeightedString bigram : wordProperty.getBigrams()) {
                 fusionDict.setBigram(word0, bigram.mWord, bigram.mProbabilityInfo);
             }
         }
diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java
index 012fd81..2c2152b 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java
@@ -100,7 +100,10 @@
         Collections.sort(codePointOccurrenceArray, new Comparator<Entry<Integer, Integer>>() {
             @Override
             public int compare(final Entry<Integer, Integer> a, final Entry<Integer, Integer> b) {
-                return b.getValue().compareTo(a.getValue());
+                if (a.getValue() != b.getValue()) {
+                    return b.getValue().compareTo(a.getValue());
+                }
+                return b.getKey().compareTo(a.getKey());
             }
         });
         int currentCodePointTableIndex = FormatSpec.MINIMAL_ONE_BYTE_CHARACTER_VALUE;
@@ -121,7 +124,7 @@
     @Override
     public void writeDictionary(final FusionDictionary dict, final FormatOptions formatOptions)
             throws IOException, UnsupportedFormatException {
-        if (formatOptions.mVersion > FormatSpec.VERSION2) {
+        if (formatOptions.mVersion > FormatSpec.VERSION201) {
             throw new UnsupportedFormatException(
                     "The given format options has wrong version number : "
                     + formatOptions.mVersion);
@@ -132,7 +135,14 @@
         }
 
         // Make code point conversion table ordered by occurrence of code points
-        final CodePointTable codePointTable = makeCodePointTable(dict);
+        // Version 201 or later have codePointTable
+        final CodePointTable codePointTable;
+        if (mCodePointTableMode == CODE_POINT_TABLE_OFF || formatOptions.mVersion
+                < FormatSpec.MINIMUM_SUPPORTED_VERSION_OF_CODE_POINT_TABLE) {
+            codePointTable = new CodePointTable();
+        } else {
+            codePointTable = makeCodePointTable(dict);
+        }
 
         BinaryDictEncoderUtils.writeDictionaryHeader(mOutStream, dict, formatOptions,
                 codePointTable.mCodePointOccurrenceArray);
@@ -149,7 +159,8 @@
         ArrayList<PtNodeArray> flatNodes = BinaryDictEncoderUtils.flattenTree(dict.mRootNodeArray);
 
         MakedictLog.i("Computing addresses...");
-        BinaryDictEncoderUtils.computeAddresses(dict, flatNodes);
+        BinaryDictEncoderUtils.computeAddresses(dict, flatNodes,
+                codePointTable.mCodePointToOneByteCodeMap);
         MakedictLog.i("Checking PtNode array...");
         if (MakedictLog.DBG) BinaryDictEncoderUtils.checkFlatPtNodeArrayList(flatNodes);
 
@@ -161,7 +172,8 @@
         MakedictLog.i("Writing file...");
 
         for (PtNodeArray nodeArray : flatNodes) {
-            BinaryDictEncoderUtils.writePlacedPtNodeArray(dict, this, nodeArray);
+            BinaryDictEncoderUtils.writePlacedPtNodeArray(dict, this, nodeArray,
+                    codePointTable.mCodePointToOneByteCodeMap);
         }
         if (MakedictLog.DBG) BinaryDictEncoderUtils.showStatistics(flatNodes);
         mOutStream.write(mBuffer, 0, mPosition);
@@ -193,15 +205,19 @@
                 countSize);
     }
 
-    private void writePtNodeFlags(final PtNode ptNode) {
-        final int childrenPos = BinaryDictEncoderUtils.getChildrenPosition(ptNode);
+    private void writePtNodeFlags(final PtNode ptNode,
+            final HashMap<Integer, Integer> codePointToOneByteCodeMap) {
+        final int childrenPos = BinaryDictEncoderUtils.getChildrenPosition(ptNode,
+                codePointToOneByteCodeMap);
         mPosition = BinaryDictEncoderUtils.writeUIntToBuffer(mBuffer, mPosition,
                 BinaryDictEncoderUtils.makePtNodeFlags(ptNode, childrenPos),
                 FormatSpec.PTNODE_FLAGS_SIZE);
     }
 
-    private void writeCharacters(final int[] codePoints, final boolean hasSeveralChars) {
-        mPosition = CharEncoding.writeCharArray(codePoints, mBuffer, mPosition);
+    private void writeCharacters(final int[] codePoints, final boolean hasSeveralChars,
+            final HashMap<Integer, Integer> codePointToOneByteCodeMap) {
+        mPosition = CharEncoding.writeCharArray(codePoints, mBuffer, mPosition,
+                codePointToOneByteCodeMap);
         if (hasSeveralChars) {
             mBuffer[mPosition++] = FormatSpec.PTNODE_CHARACTERS_TERMINATOR;
         }
@@ -214,8 +230,10 @@
         }
     }
 
-    private void writeChildrenPosition(final PtNode ptNode) {
-        final int childrenPos = BinaryDictEncoderUtils.getChildrenPosition(ptNode);
+    private void writeChildrenPosition(final PtNode ptNode,
+            final HashMap<Integer, Integer> codePointToOneByteCodeMap) {
+        final int childrenPos = BinaryDictEncoderUtils.getChildrenPosition(ptNode,
+                codePointToOneByteCodeMap);
         mPosition += BinaryDictEncoderUtils.writeChildrenPosition(mBuffer, mPosition,
                 childrenPos);
     }
@@ -225,7 +243,8 @@
      *
      * @param shortcuts the shortcut attributes list.
      */
-    private void writeShortcuts(final ArrayList<WeightedString> shortcuts) {
+    private void writeShortcuts(final ArrayList<WeightedString> shortcuts,
+            final HashMap<Integer, Integer> codePointToOneByteCodeMap) {
         if (null == shortcuts || shortcuts.isEmpty()) return;
 
         final int indexOfShortcutByteSize = mPosition;
@@ -238,7 +257,8 @@
                     target.getProbability());
             mPosition = BinaryDictEncoderUtils.writeUIntToBuffer(mBuffer, mPosition, shortcutFlags,
                     FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE);
-            final int shortcutShift = CharEncoding.writeString(mBuffer, mPosition, target.mWord);
+            final int shortcutShift = CharEncoding.writeString(mBuffer, mPosition, target.mWord,
+                codePointToOneByteCodeMap);
             mPosition += shortcutShift;
         }
         final int shortcutByteSize = mPosition - indexOfShortcutByteSize;
@@ -278,12 +298,14 @@
     }
 
     @Override
-    public void writePtNode(final PtNode ptNode, final FusionDictionary dict) {
-        writePtNodeFlags(ptNode);
-        writeCharacters(ptNode.mChars, ptNode.hasSeveralChars());
+    public void writePtNode(final PtNode ptNode, final FusionDictionary dict,
+            final HashMap<Integer, Integer> codePointToOneByteCodeMap) {
+        writePtNodeFlags(ptNode, codePointToOneByteCodeMap);
+        writeCharacters(ptNode.mChars, ptNode.hasSeveralChars(), codePointToOneByteCodeMap);
         writeFrequency(ptNode.getProbability());
-        writeChildrenPosition(ptNode);
-        writeShortcuts(ptNode.mShortcutTargets);
+        writeChildrenPosition(ptNode, codePointToOneByteCodeMap);
+        // TODO: Use codePointToOneByteCodeMap for shortcuts.
+        writeShortcuts(ptNode.mShortcutTargets, null /* codePointToOneByteCodeMap */);
         writeBigrams(ptNode.mBigrams, dict);
     }
 }
diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver2DictEncoderTests.java b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictEncoderTests.java
index 7c0b92d..9104c2f 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/Ver2DictEncoderTests.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictEncoderTests.java
@@ -42,8 +42,8 @@
         final List<String> words = Arrays.asList(wordSource);
         final String correctCodePointTable = "eotdsanirfg bclwup";
         final String correctCodePointOccurrenceArrayString =
-                "10141164111411531003110297210521142103111911171108198199132111211021";
-        final String correctCodePointExpectedMapString = "323433363538373940494147454644424348";
+                "11641114101411531003114211021052972119111711121108110311021991981321";
+        final String correctCodePointExpectedMapString = "343332363540383937464549484744414243";
         final String dictName = "codePointTableTest";
         final String dictVersion = Long.toString(System.currentTimeMillis());
 
@@ -60,8 +60,8 @@
             codePointOccurrenceArrayString.append(entry.getKey());
             codePointOccurrenceArrayString.append(entry.getValue());
         }
-        assertEquals(codePointOccurrenceArrayString.toString(),
-                correctCodePointOccurrenceArrayString);
+        assertEquals(correctCodePointOccurrenceArrayString,
+                codePointOccurrenceArrayString.toString());
 
         // Check if mCodePointToOneByteCodeMap is correct
         final StringBuilder codePointExpectedMapString = new StringBuilder();
@@ -69,7 +69,7 @@
             codePointExpectedMapString.append(codePointTable.mCodePointToOneByteCodeMap.get(
                     correctCodePointTable.codePointAt(i)));
         }
-        assertEquals(codePointExpectedMapString.toString(), correctCodePointExpectedMapString);
+        assertEquals(correctCodePointExpectedMapString, codePointExpectedMapString.toString());
     }
 
     /**
diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java
index 5e8417e..0da915a 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java
@@ -97,12 +97,13 @@
             }
         }
         // Insert bigrams into the fusion dictionary.
+        // TODO: Support ngrams.
         for (final WordProperty wordProperty : wordProperties) {
-            if (wordProperty.mBigrams == null) {
+            if (!wordProperty.mHasNgrams) {
                 continue;
             }
             final String word0 = wordProperty.mWord;
-            for (final WeightedString bigram : wordProperty.mBigrams) {
+            for (final WeightedString bigram : wordProperty.getBigrams()) {
                 fusionDict.setBigram(word0, bigram.mWord, bigram.mProbabilityInfo);
             }
         }
diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
index 74da937..3262a16 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
@@ -27,6 +27,7 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.util.HashMap;
 
 /**
  * An implementation of DictEncoder for version 4 binary dictionary.
@@ -102,8 +103,9 @@
             }
         }
         for (final WordProperty word0Property : dict) {
-            if (null == word0Property.mBigrams) continue;
-            for (final WeightedString word1 : word0Property.mBigrams) {
+            if (!word0Property.mHasNgrams) continue;
+            // TODO: Support ngram.
+            for (final WeightedString word1 : word0Property.getBigrams()) {
                 final NgramContext ngramContext =
                         new NgramContext(new NgramContext.WordInfo(word0Property.mWord));
                 if (!binaryDict.addNgramEntry(ngramContext, word1.mWord,
@@ -141,6 +143,7 @@
     }
 
     @Override
-    public void writePtNode(PtNode ptNode, FusionDictionary dict) {
+    public void writePtNode(PtNode ptNode, FusionDictionary dict,
+            HashMap<Integer, Integer> codePointToOneByteCodeMap) {
     }
 }
diff --git a/tests/src/com/android/inputmethod/latin/personalization/ContextualDictionaryTests.java b/tests/src/com/android/inputmethod/latin/personalization/ContextualDictionaryTests.java
index 565fadb..0113099 100644
--- a/tests/src/com/android/inputmethod/latin/personalization/ContextualDictionaryTests.java
+++ b/tests/src/com/android/inputmethod/latin/personalization/ContextualDictionaryTests.java
@@ -42,8 +42,9 @@
         final ArrayList<String> dictTypes = new ArrayList<>();
         dictTypes.add(Dictionary.TYPE_CONTEXTUAL);
         final DictionaryFacilitator dictionaryFacilitator = new DictionaryFacilitator();
-        dictionaryFacilitator.resetDictionariesForTesting(getContext(), LOCALE_EN_US, dictTypes,
-                new HashMap<String, File>(), new HashMap<String, Map<String, String>>());
+        dictionaryFacilitator.resetDictionariesForTesting(getContext(),
+                new Locale[] { LOCALE_EN_US }, dictTypes, new HashMap<String, File>(),
+                new HashMap<String, Map<String, String>>());
         return dictionaryFacilitator;
     }
 
diff --git a/tests/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryTests.java b/tests/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryTests.java
index 4e7e814..afabbbd 100644
--- a/tests/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryTests.java
+++ b/tests/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryTests.java
@@ -55,8 +55,9 @@
         dictTypes.add(Dictionary.TYPE_MAIN);
         dictTypes.add(Dictionary.TYPE_PERSONALIZATION);
         final DictionaryFacilitator dictionaryFacilitator = new DictionaryFacilitator(getContext());
-        dictionaryFacilitator.resetDictionariesForTesting(getContext(), LOCALE_EN_US, dictTypes,
-                new HashMap<String, File>(), new HashMap<String, Map<String, String>>());
+        dictionaryFacilitator.resetDictionariesForTesting(getContext(),
+                new Locale[] { LOCALE_EN_US }, dictTypes, new HashMap<String, File>(),
+                new HashMap<String, Map<String, String>>());
         // Set subtypes.
         RichInputMethodManager.init(getContext());
         final RichInputMethodManager richImm = RichInputMethodManager.getInstance();
diff --git a/tests/src/com/android/inputmethod/latin/touchinputconsumer/NullGestureConsumerTests.java b/tests/src/com/android/inputmethod/latin/touchinputconsumer/NullGestureConsumerTests.java
new file mode 100644
index 0000000..ca1039b
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/touchinputconsumer/NullGestureConsumerTests.java
@@ -0,0 +1,47 @@
+/*
+ * 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.touchinputconsumer;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+/**
+ * Tests for GestureConsumer.NULL_GESTURE_CONSUMER.
+ */
+@SmallTest
+public class NullGestureConsumerTests extends AndroidTestCase {
+    /**
+     * Tests that GestureConsumer.NULL_GESTURE_CONSUMER indicates that it won't consume gesture data
+     * and that its methods don't raise exceptions even for invalid data.
+     */
+    public void testNullGestureConsumer() {
+        assertFalse(GestureConsumer.NULL_GESTURE_CONSUMER.willConsume());
+        GestureConsumer.NULL_GESTURE_CONSUMER.onInit(null, null);
+        GestureConsumer.NULL_GESTURE_CONSUMER.onGestureStarted(null, null);
+        GestureConsumer.NULL_GESTURE_CONSUMER.onGestureCanceled();
+        GestureConsumer.NULL_GESTURE_CONSUMER.onGestureCompleted(null);
+        GestureConsumer.NULL_GESTURE_CONSUMER.onImeSuggestionsProcessed(null, -1, -1);
+    }
+
+    /**
+     * Tests that newInstance returns NULL_GESTURE_CONSUMER for invalid input.
+     */
+    public void testNewInstanceGivesNullGestureConsumerForInvalidInputs() {
+        assertSame(GestureConsumer.NULL_GESTURE_CONSUMER,
+                GestureConsumer.newInstance(null, null, null, null));
+    }
+}
diff --git a/tests/src/com/android/inputmethod/latin/utils/CollectionUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/CollectionUtilsTests.java
new file mode 100644
index 0000000..76e2828
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/utils/CollectionUtilsTests.java
@@ -0,0 +1,55 @@
+/*
+ * 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.SmallTest;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+
+/**
+ * Tests for {@link CollectionUtils}.
+ */
+@SmallTest
+public class CollectionUtilsTests extends AndroidTestCase {
+    /**
+     * Tests that {@link CollectionUtils#arrayAsList(E[],int,int)} gives the expected
+     * results for a few valid inputs.
+     */
+    public void testArrayAsList() {
+        final String[] array = { "0", "1", "2", "3", "4" };
+        final ArrayList<String> empty = new ArrayList<>();
+        assertEquals(empty, CollectionUtils.arrayAsList(array, 0, 0));
+        assertEquals(empty, CollectionUtils.arrayAsList(array, 1, 1));
+        final ArrayList<String> expected123 = new ArrayList<>(Arrays.asList("1", "2", "3"));
+        assertEquals(expected123, CollectionUtils.arrayAsList(array, 1, 4));
+    }
+
+    /**
+     * Tests that {@link CollectionUtils#isEmpty(java.util.Collection)} gives the expected
+     * results for a few cases.
+     */
+    public void testIsNullOrEmpty() {
+        assertTrue(CollectionUtils.isNullOrEmpty(null));
+        assertTrue(CollectionUtils.isNullOrEmpty(new ArrayList()));
+        assertTrue(CollectionUtils.isNullOrEmpty(Collections.EMPTY_SET));
+        assertFalse(CollectionUtils.isNullOrEmpty(Collections.singleton("Not empty")));
+    }
+
+}
diff --git a/tests/src/com/android/inputmethod/latin/utils/SpacebarLanguageUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/SpacebarLanguageUtilsTests.java
index b766ab2..e6131cf 100644
--- a/tests/src/com/android/inputmethod/latin/utils/SpacebarLanguageUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/SpacebarLanguageUtilsTests.java
@@ -45,7 +45,6 @@
     RichInputMethodSubtype FR_CH;
     RichInputMethodSubtype DE;
     RichInputMethodSubtype DE_CH;
-    RichInputMethodSubtype HI_ZZ;
     RichInputMethodSubtype ZZ;
     RichInputMethodSubtype DE_QWERTY;
     RichInputMethodSubtype FR_QWERTZ;
@@ -55,6 +54,9 @@
     RichInputMethodSubtype ZZ_AZERTY;
     RichInputMethodSubtype ZZ_PC;
 
+    // This is a preliminary subtype and may not exist.
+    RichInputMethodSubtype HI_LATN;
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
@@ -87,8 +89,6 @@
                 Locale.GERMAN.toString(), "qwertz"));
         DE_CH = new RichInputMethodSubtype(mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
                 "de_CH", "swiss"));
-        HI_ZZ = new RichInputMethodSubtype(mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
-                "hi_ZZ", "qwerty"));
         ZZ = new RichInputMethodSubtype(mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
                 SubtypeLocaleUtils.NO_LANGUAGE, "qwerty"));
         DE_QWERTY = new RichInputMethodSubtype(
@@ -112,6 +112,12 @@
         ZZ_PC = new RichInputMethodSubtype(
                 AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
                     SubtypeLocaleUtils.NO_LANGUAGE, "pcqwerty"));
+
+        final InputMethodSubtype hiLatn = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+                "hi_ZZ", "qwerty");
+        if (hiLatn != null) {
+            HI_LATN = new RichInputMethodSubtype(hiLatn);
+        }
     }
 
     public void testAllFullDisplayNameForSpacebar() {
@@ -119,12 +125,17 @@
             final String subtypeName = SubtypeLocaleUtils
                     .getSubtypeDisplayNameInSystemLocale(subtype.getRawSubtype());
             final String spacebarText = subtype.getFullDisplayName();
-            final String languageName = SubtypeLocaleUtils
-                    .getSubtypeLocaleDisplayName(subtype.getLocale());
-            if (subtype.isNoLanguage()) {
-                assertFalse(subtypeName, spacebarText.contains(languageName));
+            final Locale[] locales = subtype.getLocales();
+            if (1 == locales.length) {
+                final String languageName = SubtypeLocaleUtils
+                        .getSubtypeLocaleDisplayName(locales[0].toString());
+                if (subtype.isNoLanguage()) {
+                    assertFalse(subtypeName, spacebarText.contains(languageName));
+                } else {
+                    assertTrue(subtypeName, spacebarText.contains(languageName));
+                }
             } else {
-                assertTrue(subtypeName, spacebarText.contains(languageName));
+                // TODO: test multi-lingual subtype spacebar display
             }
         }
     }
@@ -133,8 +144,14 @@
         for (final RichInputMethodSubtype subtype : mSubtypesList) {
             final String subtypeName = SubtypeLocaleUtils
                     .getSubtypeDisplayNameInSystemLocale(subtype.getRawSubtype());
+            final Locale[] locales = subtype.getLocales();
+            if (locales.length > 1) {
+                // TODO: test multi-lingual subtype spacebar display
+                continue;
+            }
+            final Locale locale = locales[0];
             if (SubtypeLocaleUtils.sExceptionalLocaleDisplayedInRootLocale.contains(
-                    subtype.getLocale())) {
+                    locale.toString())) {
                 // Skip test because the language part of this locale string doesn't represent
                 // the locale to be displayed on the spacebar (for example hi_ZZ and Hinglish).
                 continue;
@@ -144,7 +161,6 @@
                 assertEquals(subtypeName, SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(
                         subtype.getRawSubtype()), spacebarText);
             } else {
-                final Locale locale = SubtypeLocaleUtils.getSubtypeLocale(subtype);
                 assertEquals(subtypeName,
                         SubtypeLocaleUtils.getSubtypeLocaleDisplayName(locale.getLanguage()),
                         spacebarText);
@@ -182,8 +198,11 @@
             assertEquals("fr_CH", "Français (Suisse)", FR_CH.getFullDisplayName());
             assertEquals("de", "Deutsch", DE.getFullDisplayName());
             assertEquals("de_CH", "Deutsch (Schweiz)", DE_CH.getFullDisplayName());
-            assertEquals("hi_ZZ", "Hinglish", HI_ZZ.getFullDisplayName());
             assertEquals("zz", "QWERTY", ZZ.getFullDisplayName());
+            // This is a preliminary subtype and may not exist.
+            if (HI_LATN != null) {
+                assertEquals("hi_ZZ", "Hinglish", HI_LATN.getFullDisplayName());
+            }
 
             assertEquals("en_US", "English", EN_US.getMiddleDisplayName());
             assertEquals("en_GB", "English", EN_GB.getMiddleDisplayName());
@@ -193,8 +212,11 @@
             assertEquals("fr_CH", "Français", FR_CH.getMiddleDisplayName());
             assertEquals("de", "Deutsch", DE.getMiddleDisplayName());
             assertEquals("de_CH", "Deutsch", DE_CH.getMiddleDisplayName());
-            assertEquals("hi_ZZ", "Hinglish", HI_ZZ.getMiddleDisplayName());
             assertEquals("zz", "QWERTY", ZZ.getMiddleDisplayName());
+            // This is a preliminary subtype and may not exist.
+            if (HI_LATN != null) {
+                assertEquals("hi_ZZ", "Hinglish", HI_LATN.getMiddleDisplayName());
+            }
             return null;
         }
     };
diff --git a/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java
index c095e68..dfc3fec 100644
--- a/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java
@@ -45,7 +45,6 @@
     InputMethodSubtype FR_CH;
     InputMethodSubtype DE;
     InputMethodSubtype DE_CH;
-    InputMethodSubtype HI_ZZ;
     InputMethodSubtype ZZ;
     InputMethodSubtype DE_QWERTY;
     InputMethodSubtype FR_QWERTZ;
@@ -55,6 +54,9 @@
     InputMethodSubtype ZZ_AZERTY;
     InputMethodSubtype ZZ_PC;
 
+    // This is a preliminary subtype and may not exist.
+    InputMethodSubtype HI_LATN;
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
@@ -87,8 +89,6 @@
                 Locale.GERMAN.toString(), "qwertz");
         DE_CH = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
                 "de_CH", "swiss");
-        HI_ZZ = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
-                "hi_ZZ", "qwerty");
         ZZ = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
                 SubtypeLocaleUtils.NO_LANGUAGE, "qwerty");
         DE_QWERTY = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
@@ -105,20 +105,27 @@
                 SubtypeLocaleUtils.NO_LANGUAGE, "azerty");
         ZZ_PC = AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
                 SubtypeLocaleUtils.NO_LANGUAGE, "pcqwerty");
+
+        HI_LATN = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet("hi_ZZ", "qwerty");
     }
 
     public void testAllFullDisplayName() {
         for (final RichInputMethodSubtype subtype : mSubtypesList) {
             final String subtypeName = SubtypeLocaleUtils
                     .getSubtypeDisplayNameInSystemLocale(subtype.getRawSubtype());
-            if (subtype.isNoLanguage()) {
-                final String layoutName = SubtypeLocaleUtils
-                        .getKeyboardLayoutSetDisplayName(subtype.getRawSubtype());
-                assertTrue(subtypeName, subtypeName.contains(layoutName));
+            final Locale[] locales = subtype.getLocales();
+            if (1 == locales.length) {
+                if (subtype.isNoLanguage()) {
+                    final String layoutName = SubtypeLocaleUtils
+                            .getKeyboardLayoutSetDisplayName(subtype.getRawSubtype());
+                    assertTrue(subtypeName, subtypeName.contains(layoutName));
+                } else {
+                    final String languageName = SubtypeLocaleUtils
+                            .getSubtypeLocaleDisplayNameInSystemLocale(locales[0].toString());
+                    assertTrue(subtypeName, subtypeName.contains(languageName));
+                }
             } else {
-                final String languageName = SubtypeLocaleUtils
-                        .getSubtypeLocaleDisplayNameInSystemLocale(subtype.getLocale());
-                assertTrue(subtypeName, subtypeName.contains(languageName));
+                // TODO: test multi-lingual subtype spacebar display
             }
         }
     }
@@ -132,8 +139,11 @@
         assertEquals("fr_CH", "swiss", SubtypeLocaleUtils.getKeyboardLayoutSetName(FR_CH));
         assertEquals("de", "qwertz", SubtypeLocaleUtils.getKeyboardLayoutSetName(DE));
         assertEquals("de_CH", "swiss", SubtypeLocaleUtils.getKeyboardLayoutSetName(DE_CH));
-        assertEquals("hi_ZZ", "qwerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(HI_ZZ));
         assertEquals("zz", "qwerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(ZZ));
+        // This is a preliminary subtype and may not exist.
+        if (HI_LATN != null) {
+            assertEquals("hi_ZZ", "qwerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(HI_LATN));
+        }
 
         assertEquals("de qwerty", "qwerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(DE_QWERTY));
         assertEquals("fr qwertz", "qwertz", SubtypeLocaleUtils.getKeyboardLayoutSetName(FR_QWERTZ));
@@ -188,10 +198,13 @@
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(DE));
                 assertEquals("de_CH", "German (Switzerland)",
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(DE_CH));
-                assertEquals("hi_ZZ", "Hinglish",
-                        SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(HI_ZZ));
                 assertEquals("zz", "Alphabet (QWERTY)",
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ZZ));
+                // This is a preliminary subtype and may not exist.
+                if (HI_LATN != null) {
+                    assertEquals("hi_ZZ", "Hinglish",
+                            SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(HI_LATN));
+                }
                 return null;
             }
         };
@@ -263,10 +276,13 @@
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(DE));
                 assertEquals("de_CH", "Allemand (Suisse)",
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(DE_CH));
-                assertEquals("hi_ZZ", "Hindi/Anglais",
-                        SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(HI_ZZ));
                 assertEquals("zz", "Alphabet latin (QWERTY)",
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ZZ));
+                // This is a preliminary subtype and may not exist.
+                if (HI_LATN != null) {
+                    assertEquals("hi_ZZ", "Hindi/Anglais",
+                            SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(HI_LATN));
+                }
                 return null;
             }
         };
@@ -315,9 +331,9 @@
                     .getSubtypeDisplayNameInSystemLocale(rawSubtype);
             if (rawSubtype.equals(ARABIC) || rawSubtype.equals(FARSI)
                     || rawSubtype.equals(HEBREW)) {
-                assertTrue(subtypeName, SubtypeLocaleUtils.isRtlLanguage(subtype));
+                assertTrue(subtypeName, subtype.isRtlSubtype());
             } else {
-                assertFalse(subtypeName, SubtypeLocaleUtils.isRtlLanguage(subtype));
+                assertFalse(subtypeName, subtype.isRtlSubtype());
             }
         }
     }
diff --git a/tools/dicttool/compat/android/util/Pair.java b/tools/dicttool/compat/android/util/Pair.java
index 5bf3484..ab6096e 100644
--- a/tools/dicttool/compat/android/util/Pair.java
+++ b/tools/dicttool/compat/android/util/Pair.java
@@ -17,6 +17,7 @@
 package android.util;
 
 import java.util.Arrays;
+import java.util.Objects;
 
 public class Pair<T1, T2> {
     public final T1 mFirst;
@@ -29,7 +30,8 @@
 
     @Override
     public int hashCode() {
-        return Arrays.hashCode(new Object[] { mFirst, mSecond });
+        return (mFirst == null ? 0 : mFirst.hashCode())
+                ^ (mSecond == null ? 0 : mSecond.hashCode());
     }
 
     @Override
@@ -37,7 +39,6 @@
         if (o == this) return true;
         if (!(o instanceof Pair)) return false;
         Pair<?, ?> p = (Pair<?, ?>)o;
-        return ((mFirst == null && p.mFirst == null) || mFirst.equals(p.mFirst))
-                && ((mSecond == null && p.mSecond == null) || mSecond.equals(p.mSecond));
+        return Objects.equals(mFirst, p.mFirst) && Objects.equals(mSecond, p.mSecond);
     }
 }
diff --git a/tools/dicttool/compat/com/android/inputmethod/keyboard/ProximityInfo.java b/tools/dicttool/compat/com/android/inputmethod/keyboard/ProximityInfo.java
index 561b663..3a068bd 100644
--- a/tools/dicttool/compat/com/android/inputmethod/keyboard/ProximityInfo.java
+++ b/tools/dicttool/compat/com/android/inputmethod/keyboard/ProximityInfo.java
@@ -18,11 +18,10 @@
 
 public class ProximityInfo {
     public long getNativeProximityInfo() { return 0l; }
-    private static native long setProximityInfoNative(String locale,
-            int displayWidth, int displayHeight, int gridWidth, int gridHeight,
-            int mostCommonKeyWidth, int mostCommonKeyHeight, int[] proximityCharsArray,
-            int keyCount, int[] keyXCoordinates, int[] keyYCoordinates, int[] keyWidths,
-            int[] keyHeights, int[] keyCharCodes, float[] sweetSpotCenterXs,
+    private static native long setProximityInfoNative(int displayWidth, int displayHeight,
+            int gridWidth, int gridHeight, int mostCommonKeyWidth, int mostCommonKeyHeight,
+            int[] proximityCharsArray, int keyCount, int[] keyXCoordinates, int[] keyYCoordinates,
+            int[] keyWidths, int[] keyHeights, int[] keyCharCodes, float[] sweetSpotCenterXs,
             float[] sweetSpotCenterYs, float[] sweetSpotRadii);
     private static native void releaseProximityInfoNative(long nativeProximityInfo);
 }
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 44f9695..5dfb7bf 100644
--- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/DictionaryMaker.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/DictionaryMaker.java
@@ -158,7 +158,7 @@
             String outputBinary = null;
             String outputXml = null;
             String outputCombined = null;
-            int outputBinaryFormatVersion = 2; // the default version is 2.
+            int outputBinaryFormatVersion = FormatSpec.VERSION201; // the default version is 201.
             // Don't use code point table by default.
             int codePointTableMode = Ver2DictEncoder.CODE_POINT_TABLE_OFF;
 
diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Diff.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Diff.java
index 94d1ae8..c6818ce 100644
--- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Diff.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Diff.java
@@ -135,7 +135,7 @@
                     hasDifferences = true;
                 }
                 hasDifferences |= hasAttributesDifferencesAndPrintThemIfAny(word0Property.mWord,
-                        "Bigram", word0Property.mBigrams, word1PtNode.getBigrams());
+                        "Bigram", word0Property.getBigrams(), word1PtNode.getBigrams());
                 hasDifferences |= hasAttributesDifferencesAndPrintThemIfAny(word0Property.mWord,
                         "Shortcut", word0Property.mShortcutTargets,
                         word1PtNode.getShortcutTargets());
diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Info.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Info.java
index 9b2567f..2850e1f 100644
--- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Info.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Info.java
@@ -45,8 +45,8 @@
         int whitelistCount = 0;
         for (final WordProperty wordProperty : dict) {
             ++wordCount;
-            if (null != wordProperty.mBigrams) {
-                bigramCount += wordProperty.mBigrams.size();
+            if (wordProperty.mHasNgrams) {
+                bigramCount += wordProperty.mNgrams.size();
             }
             if (null != wordProperty.mShortcutTargets) {
                 shortcutCount += wordProperty.mShortcutTargets.size();
diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/XmlDictInputOutput.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/XmlDictInputOutput.java
index bdec447..cd3ce70 100644
--- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/XmlDictInputOutput.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/XmlDictInputOutput.java
@@ -353,7 +353,7 @@
                     + "\" " + PROBABILITY_ATTR + "=\"" + wordProperty.getProbability()
                     + (wordProperty.mIsNotAWord ? "\" " + NOT_A_WORD_ATTR + "=\"true" : "")
                     + "\">");
-            if (null != wordProperty.mShortcutTargets) {
+            if (wordProperty.mHasShortcuts) {
                 destination.write("\n");
                 for (WeightedString target : wordProperty.mShortcutTargets) {
                     destination.write("    <" + SHORTCUT_TAG + " " + PROBABILITY_ATTR + "=\""
@@ -362,9 +362,9 @@
                 }
                 destination.write("  ");
             }
-            if (null != wordProperty.mBigrams) {
+            if (wordProperty.mHasNgrams) {
                 destination.write("\n");
-                for (WeightedString bigram : wordProperty.mBigrams) {
+                for (WeightedString bigram : wordProperty.getBigrams()) {
                     destination.write("    <" + BIGRAM_TAG + " " + PROBABILITY_ATTR + "=\""
                             + bigram.getProbability() + "\">" + bigram.mWord
                             + "</" + BIGRAM_TAG + ">\n");
diff --git a/tools/make-keyboard-text/res/values-az-rAZ/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-az-rAZ/donottranslate-more-keys.xml
index a68de67..52fe565 100644
--- a/tools/make-keyboard-text/res/values-az-rAZ/donottranslate-more-keys.xml
+++ b/tools/make-keyboard-text/res/values-az-rAZ/donottranslate-more-keys.xml
@@ -18,6 +18,7 @@
 */
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- This is the same as Turkish -->
     <!-- U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
          U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
          U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE -->
diff --git a/tools/make-keyboard-text/res/values-bn-rBD/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-bn-rBD/donottranslate-more-keys.xml
new file mode 100644
index 0000000..4955cd4
--- /dev/null
+++ b/tools/make-keyboard-text/res/values-bn-rBD/donottranslate-more-keys.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Label for "switch to alphabetic" key.
+         U+0995: "क" BENGALI LETTER KA
+         U+0996: "ख" BENGALI LETTER KHA
+         U+0997: "ग" BENGALI LETTER GA -->
+    <string name="keylabel_to_alpha">&#x0995;&#x0996;&#x0997;</string>
+    <!-- U+09F3: "৳" BENGALI RUPEE SIGN -->
+    <string name="keyspec_currency">&#x09F3;</string>
+</resources>
diff --git a/tools/make-keyboard-text/res/values-hi/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-hi/donottranslate-more-keys.xml
index 55723cd..2a37d8b 100644
--- a/tools/make-keyboard-text/res/values-hi/donottranslate-more-keys.xml
+++ b/tools/make-keyboard-text/res/values-hi/donottranslate-more-keys.xml
@@ -57,4 +57,9 @@
     <string name="additional_morekeys_symbols_0">0</string>
     <!-- U+20B9: "₹" INDIAN RUPEE SIGN -->
     <string name="keyspec_currency">&#x20B9;</string>
+    <!-- U+0964: "।" DEVANAGARI DANDA -->
+    <string name="keyspec_period">&#x0964;</string>
+    <string name="keyspec_tablet_period">&#x0964;</string>
+    <string name="morekeys_period">"!autoColumnOrder!9,\\,,.,?,!,#,),(,/,;,',@,:,-,\",+,\\%,&amp;"</string>
+    <string name="morekeys_tablet_period">"!autoColumnOrder!8,\\,,.,',#,),(,/,;,@,:,-,\",+,\\%,&amp;"</string>
 </resources>
diff --git a/tools/make-keyboard-text/res/values-ne-rNP/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-ne-rNP/donottranslate-more-keys.xml
index 97c50d1..56b594f 100644
--- a/tools/make-keyboard-text/res/values-ne-rNP/donottranslate-more-keys.xml
+++ b/tools/make-keyboard-text/res/values-ne-rNP/donottranslate-more-keys.xml
@@ -57,4 +57,9 @@
     <string name="additional_morekeys_symbols_0">0</string>
     <!-- U+0930/U+0941/U+002E "रु." NEPALESE RUPEE SIGN -->
     <string name="keyspec_currency">&#x0930;&#x0941;&#x002E;</string>
+    <!-- U+0964: "।" DEVANAGARI DANDA -->
+    <string name="keyspec_period">&#x0964;</string>
+    <string name="keyspec_tablet_period">&#x0964;</string>
+    <string name="morekeys_period">"!autoColumnOrder!9,.,\\,,?,!,#,),(,/,;,',@,:,-,\",+,\\%,&amp;"</string>
+    <string name="morekeys_tablet_period">"!autoColumnOrder!8,.,\\,,',#,),(,/,;,@,:,-,\",+,\\%,&amp;"</string>
 </resources>
diff --git a/tools/make-keyboard-text/res/values-uz-rUZ/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-uz-rUZ/donottranslate-more-keys.xml
new file mode 100644
index 0000000..24dd091
--- /dev/null
+++ b/tools/make-keyboard-text/res/values-uz-rUZ/donottranslate-more-keys.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- This is the same as Turkish -->
+    <!-- U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+         U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+         U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE -->
+    <string name="morekeys_a">&#x00E2;,&#x00E4;,&#x00E1;</string>
+    <!-- U+0259: "ə" LATIN SMALL LETTER SCHWA
+         U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE -->
+    <string name="morekeys_e">&#x0259;,&#x00E9;</string>
+    <!-- U+0131: "ı" LATIN SMALL LETTER DOTLESS I
+         U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+         U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+         U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+         U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+         U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
+         U+012B: "ī" LATIN SMALL LETTER I WITH MACRON -->
+    <string name="morekeys_i">&#x0131;,&#x00EE;,&#x00EF;,&#x00EC;,&#x00ED;,&#x012F;,&#x012B;</string>
+    <!-- U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+         U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+         U+0153: "œ" LATIN SMALL LIGATURE OE
+         U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+         U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+         U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+         U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+         U+014D: "ō" LATIN SMALL LETTER O WITH MACRON -->
+    <string name="morekeys_o">&#x00F6;,&#x00F4;,&#x0153;,&#x00F2;,&#x00F3;,&#x00F5;,&#x00F8;,&#x014D;</string>
+    <!-- U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+         U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
+         U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+         U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+         U+016B: "ū" LATIN SMALL LETTER U WITH MACRON -->
+    <string name="morekeys_u">&#x00FC;,&#x00FB;,&#x00F9;,&#x00FA;,&#x016B;</string>
+    <!-- U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
+         U+00DF: "ß" LATIN SMALL LETTER SHARP S
+         U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
+         U+0161: "š" LATIN SMALL LETTER S WITH CARON -->
+    <string name="morekeys_s">&#x015F;,&#x00DF;,&#x015B;,&#x0161;</string>
+    <!-- U+011F: "ğ" LATIN SMALL LETTER G WITH BREVE -->
+    <string name="morekeys_g">&#x011F;</string>
+    <!-- U+0148: "ň" LATIN SMALL LETTER N WITH CARON
+         U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE -->
+    <string name="morekeys_n">&#x0148;,&#x00F1;</string>
+    <!-- U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
+         U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
+         U+010D: "č" LATIN SMALL LETTER C WITH CARON -->
+    <string name="morekeys_c">&#x00E7;,&#x0107;,&#x010D;</string>
+    <!-- U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE -->
+    <string name="morekeys_y">&#x00FD;</string>
+    <!-- U+017E: "ž" LATIN SMALL LETTER Z WITH CARON -->
+    <string name="morekeys_z">&#x017E;</string>
+</resources>