diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java
index bf29b5ffe90ce3dd5462a2780f2275b6d47fa287..45ce6a85f1d44da672a0cca074e0711b926ea83a 100644
--- a/java/src/com/android/inputmethod/keyboard/Key.java
+++ b/java/src/com/android/inputmethod/keyboard/Key.java
@@ -278,8 +278,8 @@ public class Key implements Comparable<Key> {
 
         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 @@ public class Key implements Comparable<Key> {
             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 @@ public class Key implements Comparable<Key> {
             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 @@ public class Key implements Comparable<Key> {
                 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 @@ public class Key implements Comparable<Key> {
         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/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
index f9cf3535e010dec2acce7f01bfa10290a4f69a9f..ab0d63306a338912d2472dadae423c1adb701259 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
@@ -63,7 +63,6 @@ public final class KeyboardId {
     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 final class KeyboardId {
 
     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 @@ public final class KeyboardId {
         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 final class KeyboardId {
     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 7f2957fffa16d679d5988aedb30bca672a558187..93123d1ec89ace6f4825a36c10a53f99f87f9983 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -120,7 +120,9 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
         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 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
                 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()
diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index cded2cb8c66f462bfdfa102102b081e17046149a..ad30b746eeff2ec1869e38840f66c96f4caf0e89 100644
--- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -875,6 +875,11 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
     // 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/internal/KeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
index 50385555cd3e96969d854f1b238ea33f29f9992e..f4e010c4dc0efd4b94dcadf8043338ab089cebe0 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 org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
 import java.util.Arrays;
+import java.util.Locale;
 
 /**
  * Keyboard Building helper.
@@ -281,7 +282,8 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
 
             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 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
                     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 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
         }
     }
 
+    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/LanguageOnSpacebarHelper.java b/java/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelper.java
index 72ad2bd97d6b7047324c5b51b9f895b204074734..21eaed9500fe737d5d2ba952e6e398fa0e26da33 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 com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
 
 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 final class LanguageOnSpacebarHelper {
     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 @@ public final class LanguageOnSpacebarHelper {
         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/DictionaryFacilitator.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
index 1f0317288fded915447b80c7ecae4bfe708471da..4a218d550a7d8ccf5d07c6fc414a187618f9157b 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
@@ -416,33 +416,38 @@ public class DictionaryFacilitator {
     }
 
     @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));
-                }
-                if (dict == null) {
-                    throw new RuntimeException("Unknown dictionary type: " + 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);
                 }
-                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/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index be2efb2dc81241450a70bd3b05a9c54c50235fbd..a6be75943527a792c5e296b0577d5c59b306d31f 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -102,7 +102,7 @@ import com.android.inputmethod.latin.utils.ViewLayoutUtils;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.Collections;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Locale;
 import java.util.concurrent.TimeUnit;
@@ -575,18 +575,19 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
     // 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()) {
-            resetDictionaryFacilitatorForLocale(locale);
+            resetDictionaryFacilitatorForLocale(locales);
         }
         mDictionaryFacilitator.updateEnabledSubtypes(mRichImm.getMyEnabledInputMethodSubtypeList(
                 true /* allowsImplicitlySelectedSubtypes */));
@@ -628,35 +629,34 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
     }
 
     private void resetDictionaryFacilitatorIfNecessary() {
-        final Locale switcherSubtypeLocale = mSubtypeSwitcher.getCurrentSubtypeLocale();
-        if (mDictionaryFacilitator.isForLocales(new Locale[] { switcherSubtypeLocale })) {
+        final Locale[] subtypeSwitcherLocales = mSubtypeSwitcher.getCurrentSubtypeLocales();
+        if (mDictionaryFacilitator.isForLocales(subtypeSwitcherLocales)) {
             return;
         }
-        final String switcherLocaleStr = switcherSubtypeLocale.toString();
-        final Locale subtypeLocale;
-        if (TextUtils.isEmpty(switcherLocaleStr)) {
+        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;
         }
-        resetDictionaryFacilitatorForLocale(subtypeLocale);
+        resetDictionaryFacilitatorForLocale(subtypeLocales);
     }
 
     /**
-     * Reset the facilitator 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
      */
-    // TODO: make sure the current settings always have the right locale, and read from them
-    private void resetDictionaryFacilitatorForLocale(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);
@@ -881,7 +881,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
         // Update to a gesture consumer with the current editor and IME state.
         mGestureConsumer = GestureConsumer.newInstance(editorInfo,
                 mInputLogic.getPrivateCommandPerformer(),
-                Collections.singletonList(mSubtypeSwitcher.getCurrentSubtypeLocale()),
+                Arrays.asList(mSubtypeSwitcher.getCurrentSubtypeLocales()),
                 switcher.getKeyboard());
 
         // Forward this event to the accessibility utilities, if enabled.
@@ -1434,7 +1434,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
     public void onStartBatchInput() {
         mInputLogic.onStartBatchInput(mSettings.getCurrent(), mKeyboardSwitcher, mHandler);
         mGestureConsumer.onGestureStarted(
-                Collections.singletonList(mSubtypeSwitcher.getCurrentSubtypeLocale()),
+                Arrays.asList(mSubtypeSwitcher.getCurrentSubtypeLocales()),
                 mKeyboardSwitcher.getKeyboard());
     }
 
@@ -1558,7 +1558,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
                 // We should clear the contextual strip if there is no suggestion from dictionaries.
                 || noSuggestionsFromDictionaries) {
             mSuggestionStripView.setSuggestions(suggestedWords,
-                    SubtypeLocaleUtils.isRtlLanguage(mSubtypeSwitcher.getCurrentSubtype()));
+                    mSubtypeSwitcher.getCurrentSubtype().isRtlSubtype());
         }
     }
 
diff --git a/java/src/com/android/inputmethod/latin/RichInputMethodSubtype.java b/java/src/com/android/inputmethod/latin/RichInputMethodSubtype.java
index 0b08c48e5564e4cc91d37d9e933214ea236430b6..8d055531cf1845f98c01777e7d102a086b1c977e 100644
--- a/java/src/com/android/inputmethod/latin/RichInputMethodSubtype.java
+++ b/java/src/com/android/inputmethod/latin/RichInputMethodSubtype.java
@@ -114,10 +114,13 @@ public final class RichInputMethodSubtype {
         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 45d67ff887e2a3f43f66b54200bee34625b413b2..c339e96fb5dc1b534940945b1c7b114b9432241d 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -170,15 +170,21 @@ public final class SubtypeSwitcher {
             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 @@ public final class SubtypeSwitcher {
         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/utils/SubtypeLocaleUtils.java b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java
index 5a7f7662c8cbdf43c7814c1fd5f34bc4345a3c1f..61661cd520d894cbeae2d92d4ee647c5e3ffed4a 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 @@ public final class SubtypeLocaleUtils {
         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 @@ public final class SubtypeLocaleUtils {
         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/tests/src/com/android/inputmethod/latin/personalization/ContextualDictionaryTests.java b/tests/src/com/android/inputmethod/latin/personalization/ContextualDictionaryTests.java
index 565fadb2aca262b81f3aab1f0e379478f669d78a..011309942e73fb5bef5f9c6bff726952d568c42a 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 @@ public class ContextualDictionaryTests extends AndroidTestCase {
         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 4e7e8140a5ca325dbe66400a3aab73fe8acaa210..afabbbd38baf162c218cbc2dae2c68a40c16c325 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 @@ public class PersonalizationDictionaryTests extends AndroidTestCase {
         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/utils/SpacebarLanguageUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/SpacebarLanguageUtilsTests.java
index b766ab2e7f4d6eeb5c9051057edb1509137e6f30..8810eaf371a6d3069d8b812a19117541514555ec 100644
--- a/tests/src/com/android/inputmethod/latin/utils/SpacebarLanguageUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/SpacebarLanguageUtilsTests.java
@@ -119,12 +119,17 @@ public class SpacebarLanguageUtilsTests extends AndroidTestCase {
             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 +138,14 @@ public class SpacebarLanguageUtilsTests extends AndroidTestCase {
         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 +155,6 @@ public class SpacebarLanguageUtilsTests extends AndroidTestCase {
                 assertEquals(subtypeName, SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(
                         subtype.getRawSubtype()), spacebarText);
             } else {
-                final Locale locale = SubtypeLocaleUtils.getSubtypeLocale(subtype);
                 assertEquals(subtypeName,
                         SubtypeLocaleUtils.getSubtypeLocaleDisplayName(locale.getLanguage()),
                         spacebarText);
diff --git a/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java
index c095e6831eb1fd6102d4b1edbfc622acc3a563e7..02be5138f5f2f6a5ec677d2f6843003c15ba43d8 100644
--- a/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java
@@ -111,14 +111,19 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase {
         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
             }
         }
     }
@@ -315,9 +320,9 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase {
                     .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());
             }
         }
     }