diff --git a/java/res/values/donottranslate.xml b/java/res/values/donottranslate.xml
index b97d4d7d45d6cdc4df9a66f7030774840ef655fc..99b80d0b6a0470f2bd21adc23b43352d06f93b61 100644
--- a/java/res/values/donottranslate.xml
+++ b/java/res/values/donottranslate.xml
@@ -146,13 +146,26 @@
 
     <!-- Generic subtype label -->
     <string name="subtype_generic">%s</string>
-    <!-- Description for generic QWERTY keyboard subtype -->
+
+    <!-- Predefined keyboard layouts for additional subtype -->
+    <string-array name="predefined_layouts">
+        <item>qwerty</item>
+        <item>qwertz</item>
+        <item>azerty</item>
+        <item>dvorak</item>
+    </string-array>
+    <!-- Predefined keyboard layout display names -->
+    <string-array name="predefined_layout_display_names">
+        <item>QWERTY</item>
+        <item>QWERTZ</item>
+        <item>AZERTY</item>
+        <item>Dvorak</item>
+    </string-array>
+    <!-- Description for generic subtype that has predefined layout.
+         The string resource name must be "subtype_generic_<layout name>". -->
     <string name="subtype_generic_qwerty">%s (QWERTY)</string>
-    <!-- Description for generic QWERTZ keyboard subtype -->
     <string name="subtype_generic_qwertz">%s (QWERTZ)</string>
-    <!-- Description for generic AZERTY keyboard subtype -->
     <string name="subtype_generic_azerty">%s (AZERTY)</string>
-    <!-- Description for generic Dvorak keyboard subtype -->
     <string name="subtype_generic_dvorak">%s (Dvorak)</string>
 
     <!-- dictionary pack package name /settings activity (for shared prefs and settings) -->
diff --git a/java/src/com/android/inputmethod/latin/AdditionalSubtype.java b/java/src/com/android/inputmethod/latin/AdditionalSubtype.java
index 1e405f9a25070d40aef027f324cb5a18bbb160b8..458d9ee147f9e9339481ac44adf801b05e778ee3 100644
--- a/java/src/com/android/inputmethod/latin/AdditionalSubtype.java
+++ b/java/src/com/android/inputmethod/latin/AdditionalSubtype.java
@@ -22,30 +22,8 @@ import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.KEYBOAR
 
 import android.view.inputmethod.InputMethodSubtype;
 
-import java.util.HashMap;
 
 public class AdditionalSubtype {
-    public static final String QWERTY = "qwerty";
-    public static final String QWERTZ = "qwertz";
-    public static final String AZERTY = "azerty";
-    public static final String DVORAK = "dvorak";
-    public static final String[] PREDEFINED_KEYBOARD_LAYOUT_SET = {
-        QWERTY,
-        QWERTZ,
-        AZERTY,
-        DVORAK
-    };
-
-    // Keyboard layout to subtype name resource id map.
-    private static final HashMap<String, Integer> sKeyboardLayoutToNameIdsMap =
-            new HashMap<String, Integer>();
-
-    static {
-        sKeyboardLayoutToNameIdsMap.put(QWERTY, R.string.subtype_generic_qwerty);
-        sKeyboardLayoutToNameIdsMap.put(QWERTZ, R.string.subtype_generic_qwertz);
-        sKeyboardLayoutToNameIdsMap.put(AZERTY, R.string.subtype_generic_azerty);
-        sKeyboardLayoutToNameIdsMap.put(DVORAK, R.string.subtype_generic_dvorak);
-    }
 
     private AdditionalSubtype() {
         // This utility class is not publicly instantiable.
@@ -63,7 +41,8 @@ public class AdditionalSubtype {
         final String layoutExtraValue = KEYBOARD_LAYOUT_SET + "=" + keyboardLayoutSetName;
         final String filteredExtraValue = StringUtils.appendToCsvIfNotExists(
                 IS_ADDITIONAL_SUBTYPE, extraValue);
-        Integer nameId = sKeyboardLayoutToNameIdsMap.get(keyboardLayoutSetName);
+        Integer nameId = SubtypeLocale.getSubtypeNameIdFromKeyboardLayoutName(
+                keyboardLayoutSetName);
         if (nameId == null) nameId = R.string.subtype_generic;
         return new InputMethodSubtype(nameId, R.drawable.ic_subtype_keyboard,
                 localeString, KEYBOARD_MODE,
diff --git a/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java b/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java
index 7a22c974246a30ba0eed02bb0bdb18c7dbb31934..b67f327d795be07c248dade20fc0c291c15c974a 100644
--- a/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java
+++ b/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java
@@ -129,7 +129,7 @@ public class AdditionalSubtypeSettings extends PreferenceFragment {
             setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
 
             // TODO: Should filter out already existing combinations of locale and layout.
-            for (final String layout : AdditionalSubtype.PREDEFINED_KEYBOARD_LAYOUT_SET) {
+            for (final String layout : SubtypeLocale.getPredefinedKeyboardLayoutSet()) {
                 add(new KeyboardLayoutSetItem(layout));
             }
         }
diff --git a/java/src/com/android/inputmethod/latin/SubtypeLocale.java b/java/src/com/android/inputmethod/latin/SubtypeLocale.java
index d291d1a2ef423ea19d93b92ae0d5f09bf69d2df9..33ad23a6085b8d8ae9f41ceba6353b5eabf837ea 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeLocale.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeLocale.java
@@ -27,10 +27,23 @@ import java.util.Locale;
 
 public class SubtypeLocale {
     private static final String TAG = SubtypeLocale.class.getSimpleName();
+    // This class must be located in the same package as LatinIME.java.
+    private static final String RESOURCE_PACKAGE_NAME =
+            DictionaryFactory.class.getPackage().getName();
 
     // Special language code to represent "no language".
     public static final String NO_LANGUAGE = "zz";
 
+    public static final String QWERTY = "qwerty";
+
+    private static String[] sPredefinedKeyboardLayoutSet;
+    // Keyboard layout to its display name map.
+    private static final HashMap<String, String> sKeyboardKayoutToDisplayNameMap =
+            new HashMap<String, String>();
+    // Keyboard layout to subtype name resource id map.
+    private static final HashMap<String, Integer> sKeyboardLayoutToNameIdsMap =
+            new HashMap<String, Integer>();
+    private static final String SUBTYPE_RESOURCE_GENERIC_NAME_PREFIX = "string/subtype_generic_";
     // Exceptional locales to display name map.
     private static final HashMap<String, String> sExceptionalDisplayNamesMap =
             new HashMap<String, String>();
@@ -41,13 +54,36 @@ public class SubtypeLocale {
 
     public static void init(Context context) {
         final Resources res = context.getResources();
-        final String[] locales = res.getStringArray(R.array.subtype_locale_exception_keys);
-        final String[] displayNames = res.getStringArray(R.array.subtype_locale_exception_values);
-        for (int i = 0; i < locales.length; i++) {
-            sExceptionalDisplayNamesMap.put(locales[i], displayNames[i]);
+
+        final String[] predefinedLayoutSet = res.getStringArray(R.array.predefined_layouts);
+        sPredefinedKeyboardLayoutSet = predefinedLayoutSet;
+        final String[] layoutDisplayNames = res.getStringArray(
+                R.array.predefined_layout_display_names);
+        for (int i = 0; i < predefinedLayoutSet.length; i++) {
+            final String layoutName = predefinedLayoutSet[i];
+            sKeyboardKayoutToDisplayNameMap.put(layoutName, layoutDisplayNames[i]);
+            final String resourceName = SUBTYPE_RESOURCE_GENERIC_NAME_PREFIX + layoutName;
+            final int resId = res.getIdentifier(resourceName, null, RESOURCE_PACKAGE_NAME);
+            sKeyboardLayoutToNameIdsMap.put(layoutName, resId);
+        }
+
+        final String[] exceptionalLocales = res.getStringArray(
+                R.array.subtype_locale_exception_keys);
+        final String[] exceptionalDisplayNames = res.getStringArray(
+                R.array.subtype_locale_exception_values);
+        for (int i = 0; i < exceptionalLocales.length; i++) {
+            sExceptionalDisplayNamesMap.put(exceptionalLocales[i], exceptionalDisplayNames[i]);
         }
     }
 
+    public static String[] getPredefinedKeyboardLayoutSet() {
+        return sPredefinedKeyboardLayoutSet;
+    }
+
+    public static int getSubtypeNameIdFromKeyboardLayoutName(String keyboardLayoutName) {
+        return sKeyboardLayoutToNameIdsMap.get(keyboardLayoutName);
+    }
+
     // Get InputMethodSubtype's display name in its locale.
     //        isAdditionalSubtype (T=true, F=false)
     // locale layout | Short  Middle      Full
@@ -116,11 +152,7 @@ public class SubtypeLocale {
 
     public static String getKeyboardLayoutSetDisplayName(InputMethodSubtype subtype) {
         final String layoutName = getKeyboardLayoutSetName(subtype);
-        // TODO: This hack should be removed.
-        if (layoutName.equals(AdditionalSubtype.DVORAK)) {
-            return StringUtils.toTitleCase(layoutName, Locale.US);
-        }
-        return layoutName.toUpperCase();
+        return sKeyboardKayoutToDisplayNameMap.get(layoutName);
     }
 
     public static String getKeyboardLayoutSetName(InputMethodSubtype subtype) {
@@ -130,7 +162,7 @@ public class SubtypeLocale {
         if (keyboardLayoutSet == null) {
             android.util.Log.w(TAG, "KeyboardLayoutSet not found, use QWERTY: " +
                     "locale=" + subtype.getLocale() + " extraValue=" + subtype.getExtraValue());
-            return AdditionalSubtype.QWERTY;
+            return QWERTY;
         }
         return keyboardLayoutSet;
     }
diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
index 3b9a4069d0bf95646745f0e41adcf8bb611d05b0..80428730912c110a3f60ba0d1cb1954266592c0e 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -100,7 +100,7 @@ public class SubtypeSwitcher {
         mCurrentSystemLocale = mResources.getConfiguration().locale;
         mCurrentSubtype = mImm.getCurrentInputMethodSubtype();
         mNoLanguageSubtype = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
-                service, SubtypeLocale.NO_LANGUAGE, AdditionalSubtype.QWERTY);
+                service, SubtypeLocale.NO_LANGUAGE, SubtypeLocale.QWERTY);
 
         final NetworkInfo info = mConnectivityManager.getActiveNetworkInfo();
         mIsNetworkConnected = (info != null && info.isConnected());
diff --git a/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java b/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java
index b294770575da9a79938973c950bebe0b6f8184f8..16b544169f17a3e819a3d304bb262e634517bd3f 100644
--- a/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java
+++ b/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java
@@ -116,17 +116,17 @@ public class SubtypeLocaleTests extends AndroidTestCase {
     public void testSampleSubtypes() {
         final Context context = getContext();
         final InputMethodSubtype EN_US = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
-                context, Locale.US.toString(), AdditionalSubtype.QWERTY);
+                context, Locale.US.toString(), "qwerty");
         final InputMethodSubtype EN_GB = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
-                context, Locale.UK.toString(), AdditionalSubtype.QWERTY);
+                context, Locale.UK.toString(), "qwerty");
         final InputMethodSubtype FR = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
-                context, Locale.FRENCH.toString(), AdditionalSubtype.AZERTY);
+                context, Locale.FRENCH.toString(), "azerty");
         final InputMethodSubtype FR_CA = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
-                context, Locale.CANADA_FRENCH.toString(), AdditionalSubtype.QWERTY);
+                context, Locale.CANADA_FRENCH.toString(), "qwerty");
         final InputMethodSubtype DE = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
-                context, Locale.GERMAN.toString(), AdditionalSubtype.QWERTZ);
+                context, Locale.GERMAN.toString(), "qwertz");
         final InputMethodSubtype ZZ = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
-                context, SubtypeLocale.NO_LANGUAGE, AdditionalSubtype.QWERTY);
+                context, SubtypeLocale.NO_LANGUAGE, "qwerty");
 
         assertFalse(AdditionalSubtype.isAdditionalSubtype(EN_US));
         assertFalse(AdditionalSubtype.isAdditionalSubtype(EN_GB));
@@ -166,13 +166,13 @@ public class SubtypeLocaleTests extends AndroidTestCase {
 
     public void testAdditionalSubtype() {
         final InputMethodSubtype DE_QWERTY = AdditionalSubtype.createAdditionalSubtype(
-                Locale.GERMAN.toString(), AdditionalSubtype.QWERTY, null);
+                Locale.GERMAN.toString(), "qwerty", null);
         final InputMethodSubtype FR_QWERTZ = AdditionalSubtype.createAdditionalSubtype(
-                Locale.FRENCH.toString(), AdditionalSubtype.QWERTZ, null);
+                Locale.FRENCH.toString(), "qwertz", null);
         final InputMethodSubtype US_AZERTY = AdditionalSubtype.createAdditionalSubtype(
-                Locale.US.toString(), AdditionalSubtype.AZERTY, null);
+                Locale.US.toString(), "azerty", null);
         final InputMethodSubtype ZZ_AZERTY = AdditionalSubtype.createAdditionalSubtype(
-                SubtypeLocale.NO_LANGUAGE, AdditionalSubtype.AZERTY, null);
+                SubtypeLocale.NO_LANGUAGE, "azerty", null);
 
         assertTrue(AdditionalSubtype.isAdditionalSubtype(FR_QWERTZ));
         assertTrue(AdditionalSubtype.isAdditionalSubtype(DE_QWERTY));