diff --git a/java/src/com/android/inputmethod/latin/AssetFileAddress.java b/java/src/com/android/inputmethod/latin/AssetFileAddress.java
index 16296f0e2fac9a81ce96bbd5d8165daef455af23..47c750f54a7092d9f963856fa046f7e4aea57240 100644
--- a/java/src/com/android/inputmethod/latin/AssetFileAddress.java
+++ b/java/src/com/android/inputmethod/latin/AssetFileAddress.java
@@ -35,11 +35,14 @@ final class AssetFileAddress {
         mLength = length;
     }
 
+    public static AssetFileAddress makeFromFile(final File file) {
+        if (!file.isFile()) return null;
+        return new AssetFileAddress(file.getAbsolutePath(), 0L, file.length());
+    }
+
     public static AssetFileAddress makeFromFileName(final String filename) {
         if (null == filename) return null;
-        final File f = new File(filename);
-        if (!f.isFile()) return null;
-        return new AssetFileAddress(filename, 0l, f.length());
+        return makeFromFile(new File(filename));
     }
 
     public static AssetFileAddress makeFromFileNameAndOffset(final String filename,
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
index d0bd01f6b9afa4658b9baa9d4356cf6d37477dc7..443ffa2e9db6ac1a19e72f3487d2833f9226420c 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
@@ -149,7 +149,7 @@ public final class BinaryDictionaryFileDumper {
         final int MODE_MAX = NONE;
 
         final Uri.Builder wordListUriBuilder = getProviderUriBuilder(id);
-        final String finalFileName = BinaryDictionaryGetter.getCacheFileName(id, locale, context);
+        final String finalFileName = DictionaryInfoUtils.getCacheFileName(id, locale, context);
         String tempFileName;
         try {
             tempFileName = BinaryDictionaryGetter.getTempFileName(id, context);
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
index 22b5cd55cc8e5729713c4b53aa0807038ac4f633..5da0f1be884f8335b9f3010b667b6042fad68cf9 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
@@ -65,110 +65,12 @@ final class BinaryDictionaryGetter {
     // Prevents this from being instantiated
     private BinaryDictionaryGetter() {}
 
-    /**
-     * Returns whether we may want to use this character as part of a file name.
-     *
-     * This basically only accepts ascii letters and numbers, and rejects everything else.
-     */
-    private static boolean isFileNameCharacter(int codePoint) {
-        if (codePoint >= 0x30 && codePoint <= 0x39) return true; // Digit
-        if (codePoint >= 0x41 && codePoint <= 0x5A) return true; // Uppercase
-        if (codePoint >= 0x61 && codePoint <= 0x7A) return true; // Lowercase
-        return codePoint == '_'; // Underscore
-    }
-
-    /**
-     * Escapes a string for any characters that may be suspicious for a file or directory name.
-     *
-     * Concretely this does a sort of URL-encoding except it will encode everything that's not
-     * alphanumeric or underscore. (true URL-encoding leaves alone characters like '*', which
-     * we cannot allow here)
-     */
-    // TODO: create a unit test for this method
-    private static String replaceFileNameDangerousCharacters(final String name) {
-        // This assumes '%' is fully available as a non-separator, normal
-        // character in a file name. This is probably true for all file systems.
-        final StringBuilder sb = new StringBuilder();
-        final int nameLength = name.length();
-        for (int i = 0; i < nameLength; i = name.offsetByCodePoints(i, 1)) {
-            final int codePoint = name.codePointAt(i);
-            if (isFileNameCharacter(codePoint)) {
-                sb.appendCodePoint(codePoint);
-            } else {
-                // 6 digits - unicode is limited to 21 bits
-                sb.append(String.format((Locale)null, "%%%1$06x", codePoint));
-            }
-        }
-        return sb.toString();
-    }
-
-    /**
-     * Reverse escaping done by replaceFileNameDangerousCharacters.
-     */
-    private static String getWordListIdFromFileName(final String fname) {
-        final StringBuilder sb = new StringBuilder();
-        final int fnameLength = fname.length();
-        for (int i = 0; i < fnameLength; i = fname.offsetByCodePoints(i, 1)) {
-            final int codePoint = fname.codePointAt(i);
-            if ('%' != codePoint) {
-                sb.appendCodePoint(codePoint);
-            } else {
-                final int encodedCodePoint = Integer.parseInt(fname.substring(i + 1, i + 7), 16);
-                i += 6;
-                sb.appendCodePoint(encodedCodePoint);
-            }
-        }
-        return sb.toString();
-    }
-
-    /**
-     * Helper method to get the top level cache directory.
-     */
-    private static String getWordListCacheDirectory(final Context context) {
-        return context.getFilesDir() + File.separator + "dicts";
-    }
-
-    /**
-     * Find out the cache directory associated with a specific locale.
-     */
-    private static String getCacheDirectoryForLocale(final String locale, final Context context) {
-        final String relativeDirectoryName = replaceFileNameDangerousCharacters(locale);
-        final String absoluteDirectoryName = getWordListCacheDirectory(context) + File.separator
-                + relativeDirectoryName;
-        final File directory = new File(absoluteDirectoryName);
-        if (!directory.exists()) {
-            if (!directory.mkdirs()) {
-                Log.e(TAG, "Could not create the directory for locale" + locale);
-            }
-        }
-        return absoluteDirectoryName;
-    }
-
-    /**
-     * Generates a file name for the id and locale passed as an argument.
-     *
-     * In the current implementation the file name returned will always be unique for
-     * any id/locale pair, but please do not expect that the id can be the same for
-     * different dictionaries with different locales. An id should be unique for any
-     * dictionary.
-     * The file name is pretty much an URL-encoded version of the id inside a directory
-     * named like the locale, except it will also escape characters that look dangerous
-     * to some file systems.
-     * @param id the id of the dictionary for which to get a file name
-     * @param locale the locale for which to get the file name as a string
-     * @param context the context to use for getting the directory
-     * @return the name of the file to be created
-     */
-    public static String getCacheFileName(String id, String locale, Context context) {
-        final String fileName = replaceFileNameDangerousCharacters(id);
-        return getCacheDirectoryForLocale(locale, context) + File.separator + fileName;
-    }
-
     /**
      * Generates a unique temporary file name in the app cache directory.
      */
     public static String getTempFileName(String id, Context context) throws IOException {
-        return File.createTempFile(replaceFileNameDangerousCharacters(id), null).getAbsolutePath();
+        return File.createTempFile(DictionaryInfoUtils.replaceFileNameDangerousCharacters(id),
+                null).getAbsolutePath();
     }
 
     /**
@@ -221,27 +123,6 @@ final class BinaryDictionaryGetter {
         }
     }
 
-    /**
-     * Helper method to the list of cache directories, one for each distinct locale.
-     */
-    private static File[] getCachedDirectoryList(final Context context) {
-        return new File(getWordListCacheDirectory(context)).listFiles();
-    }
-
-    /**
-     * Returns the category for a given file name.
-     *
-     * This parses the file name, extracts the category, and returns it. See
-     * {@link #getMainDictId(Locale)} and {@link #isMainWordListId(String)}.
-     * @return The category as a string or null if it can't be found in the file name.
-     */
-    private static String getCategoryFromFileName(final String fileName) {
-        final String id = getWordListIdFromFileName(fileName);
-        final String[] idArray = id.split(ID_CATEGORY_SEPARATOR);
-        if (2 != idArray.length) return null;
-        return idArray[0];
-    }
-
     /**
      * Utility class for the {@link #getCachedWordLists} method
      */
@@ -268,20 +149,21 @@ final class BinaryDictionaryGetter {
      * @param context the context on which to open the files upon.
      * @return an array of binary dictionary files, which may be empty but may not be null.
      */
-    private static File[] getCachedWordLists(final String locale,
-            final Context context) {
-        final File[] directoryList = getCachedDirectoryList(context);
+    private static File[] getCachedWordLists(final String locale, final Context context) {
+        final File[] directoryList = DictionaryInfoUtils.getCachedDirectoryList(context);
         if (null == directoryList) return EMPTY_FILE_ARRAY;
         final HashMap<String, FileAndMatchLevel> cacheFiles = CollectionUtils.newHashMap();
         for (File directory : directoryList) {
             if (!directory.isDirectory()) continue;
-            final String dirLocale = getWordListIdFromFileName(directory.getName());
+            final String dirLocale =
+                    DictionaryInfoUtils.getWordListIdFromFileName(directory.getName());
             final int matchLevel = LocaleUtils.getMatchLevel(dirLocale, locale);
             if (LocaleUtils.isMatch(matchLevel)) {
                 final File[] wordLists = directory.listFiles();
                 if (null != wordLists) {
                     for (File wordList : wordLists) {
-                        final String category = getCategoryFromFileName(wordList.getName());
+                        final String category =
+                                DictionaryInfoUtils.getCategoryFromFileName(wordList.getName());
                         final FileAndMatchLevel currentBestMatch = cacheFiles.get(category);
                         if (null == currentBestMatch || currentBestMatch.mMatchLevel < matchLevel) {
                             cacheFiles.put(category, new FileAndMatchLevel(wordList, matchLevel));
@@ -310,7 +192,7 @@ final class BinaryDictionaryGetter {
             final File fileToKeep) {
         try {
             final File canonicalFileToKeep = fileToKeep.getCanonicalFile();
-            final File[] directoryList = getCachedDirectoryList(context);
+            final File[] directoryList = DictionaryInfoUtils.getCachedDirectoryList(context);
             if (null == directoryList) return;
             for (File directory : directoryList) {
                 // There is one directory per locale. See #getCachedDirectoryList
@@ -318,7 +200,8 @@ final class BinaryDictionaryGetter {
                 final File[] wordLists = directory.listFiles();
                 if (null == wordLists) continue;
                 for (File wordList : wordLists) {
-                    final String fileId = getWordListIdFromFileName(wordList.getName());
+                    final String fileId =
+                            DictionaryInfoUtils.getWordListIdFromFileName(wordList.getName());
                     if (fileId.equals(id)) {
                         if (!canonicalFileToKeep.equals(wordList.getCanonicalFile())) {
                             wordList.delete();
@@ -331,28 +214,6 @@ final class BinaryDictionaryGetter {
         }
     }
 
-
-    /**
-     * Returns the id associated with the main word list for a specified locale.
-     *
-     * Word lists stored in Android Keyboard's resources are referred to as the "main"
-     * word lists. Since they can be updated like any other list, we need to assign a
-     * unique ID to them. This ID is just the name of the language (locale-wise) they
-     * are for, and this method returns this ID.
-     */
-    private static String getMainDictId(final Locale locale) {
-        // This works because we don't include by default different dictionaries for
-        // different countries. This actually needs to return the id that we would
-        // like to use for word lists included in resources, and the following is okay.
-        return MAIN_DICTIONARY_CATEGORY + ID_CATEGORY_SEPARATOR + locale.getLanguage().toString();
-    }
-
-    private static boolean isMainWordListId(final String id) {
-        final String[] idArray = id.split(ID_CATEGORY_SEPARATOR);
-        if (2 != idArray.length) return false;
-        return MAIN_DICTIONARY_CATEGORY.equals(idArray[0]);
-    }
-
     // ## HACK ## we prevent usage of a dictionary before version 18 for English only. The reason
     // for this is, since those do not include whitelist entries, the new code with an old version
     // of the dictionary would lose whitelist functionality.
@@ -429,16 +290,16 @@ final class BinaryDictionaryGetter {
                     hasDefaultWordList);
         }
         final File[] cachedWordLists = getCachedWordLists(locale.toString(), context);
-        final String mainDictId = getMainDictId(locale);
+        final String mainDictId = DictionaryInfoUtils.getMainDictId(locale);
         final DictPackSettings dictPackSettings = new DictPackSettings(context);
 
         boolean foundMainDict = false;
         final ArrayList<AssetFileAddress> fileList = CollectionUtils.newArrayList();
         // cachedWordLists may not be null, see doc for getCachedDictionaryList
         for (final File f : cachedWordLists) {
-            final String wordListId = getWordListIdFromFileName(f.getName());
+            final String wordListId = DictionaryInfoUtils.getWordListIdFromFileName(f.getName());
             final boolean canUse = f.canRead() && hackCanUseDictionaryFile(locale, f);
-            if (canUse && isMainWordListId(wordListId)) {
+            if (canUse && DictionaryInfoUtils.isMainWordListId(wordListId)) {
                 foundMainDict = true;
             }
             if (!dictPackSettings.isWordListActive(wordListId)) continue;
@@ -451,7 +312,7 @@ final class BinaryDictionaryGetter {
 
         if (!foundMainDict && dictPackSettings.isWordListActive(mainDictId)) {
             final int fallbackResId =
-                    DictionaryFactory.getMainDictionaryResourceId(context.getResources(), locale);
+                    DictionaryInfoUtils.getMainDictionaryResourceId(context.getResources(), locale);
             final AssetFileAddress fallbackAsset = loadFallbackResource(context, fallbackResId);
             if (null != fallbackAsset) {
                 fileList.add(fallbackAsset);
diff --git a/java/src/com/android/inputmethod/latin/DictionaryFactory.java b/java/src/com/android/inputmethod/latin/DictionaryFactory.java
index 388ad6c5903ccf56304b918a08f908c0e9c053c0..40e51672a583c1dfa14b921663d9f26592edbbb5 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryFactory.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryFactory.java
@@ -31,9 +31,6 @@ import java.util.Locale;
  */
 public final class DictionaryFactory {
     private static final String TAG = DictionaryFactory.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();
 
     /**
      * Initializes a main dictionary collection from a dictionary pack, with explicit flags.
@@ -96,8 +93,8 @@ public final class DictionaryFactory {
             final Locale locale) {
         AssetFileDescriptor afd = null;
         try {
-            final int resId =
-                    getMainDictionaryResourceIdIfAvailableForLocale(context.getResources(), locale);
+            final int resId = DictionaryInfoUtils.getMainDictionaryResourceIdIfAvailableForLocale(
+                    context.getResources(), locale);
             if (0 == resId) return null;
             afd = context.getResources().openRawResourceFd(resId);
             if (afd == null) {
@@ -154,47 +151,7 @@ public final class DictionaryFactory {
      */
     public static boolean isDictionaryAvailable(Context context, Locale locale) {
         final Resources res = context.getResources();
-        return 0 != getMainDictionaryResourceIdIfAvailableForLocale(res, locale);
-    }
-
-    private static final String DEFAULT_MAIN_DICT = "main";
-    private static final String MAIN_DICT_PREFIX = "main_";
-
-    /**
-     * Helper method to return a dictionary res id for a locale, or 0 if none.
-     * @param locale dictionary locale
-     * @return main dictionary resource id
-     */
-    private static int getMainDictionaryResourceIdIfAvailableForLocale(final Resources res,
-            final Locale locale) {
-        int resId;
-        // Try to find main_language_country dictionary.
-        if (!locale.getCountry().isEmpty()) {
-            final String dictLanguageCountry = MAIN_DICT_PREFIX + locale.toString().toLowerCase();
-            if ((resId = res.getIdentifier(
-                    dictLanguageCountry, "raw", RESOURCE_PACKAGE_NAME)) != 0) {
-                return resId;
-            }
-        }
-
-        // Try to find main_language dictionary.
-        final String dictLanguage = MAIN_DICT_PREFIX + locale.getLanguage();
-        if ((resId = res.getIdentifier(dictLanguage, "raw", RESOURCE_PACKAGE_NAME)) != 0) {
-            return resId;
-        }
-
-        // Not found, return 0
-        return 0;
-    }
-
-    /**
-     * Returns a main dictionary resource id
-     * @param locale dictionary locale
-     * @return main dictionary resource id
-     */
-    public static int getMainDictionaryResourceId(final Resources res, final Locale locale) {
-        int resourceId = getMainDictionaryResourceIdIfAvailableForLocale(res, locale);
-        if (0 != resourceId) return resourceId;
-        return res.getIdentifier(DEFAULT_MAIN_DICT, "raw", RESOURCE_PACKAGE_NAME);
+        return 0 != DictionaryInfoUtils.getMainDictionaryResourceIdIfAvailableForLocale(
+                res, locale);
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/DictionaryInfoUtils.java b/java/src/com/android/inputmethod/latin/DictionaryInfoUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..c676bf1b968560615237b46e1ea2d6c13f423804
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/DictionaryInfoUtils.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.Log;
+
+import com.android.inputmethod.latin.makedict.BinaryDictIOUtils;
+import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
+import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Locale;
+
+/**
+ * This class encapsulates the logic for the Latin-IME side of dictionary information management.
+ */
+public class DictionaryInfoUtils {
+    private static final String TAG = DictionaryInfoUtils.class.getSimpleName();
+    // This class must be located in the same package as LatinIME.java.
+    private static final String RESOURCE_PACKAGE_NAME =
+            DictionaryInfoUtils.class.getPackage().getName();
+    private static final String DEFAULT_MAIN_DICT = "main";
+    private static final String MAIN_DICT_PREFIX = "main_";
+    // 6 digits - unicode is limited to 21 bits
+    private static final int MAX_HEX_DIGITS_FOR_CODEPOINT = 6;
+
+    private DictionaryInfoUtils() {
+        // Private constructor to forbid instantation of this helper class.
+    }
+
+    /**
+     * Returns whether we may want to use this character as part of a file name.
+     *
+     * This basically only accepts ascii letters and numbers, and rejects everything else.
+     */
+    private static boolean isFileNameCharacter(int codePoint) {
+        if (codePoint >= 0x30 && codePoint <= 0x39) return true; // Digit
+        if (codePoint >= 0x41 && codePoint <= 0x5A) return true; // Uppercase
+        if (codePoint >= 0x61 && codePoint <= 0x7A) return true; // Lowercase
+        return codePoint == '_'; // Underscore
+    }
+
+    /**
+     * Escapes a string for any characters that may be suspicious for a file or directory name.
+     *
+     * Concretely this does a sort of URL-encoding except it will encode everything that's not
+     * alphanumeric or underscore. (true URL-encoding leaves alone characters like '*', which
+     * we cannot allow here)
+     */
+    // TODO: create a unit test for this method
+    public static String replaceFileNameDangerousCharacters(final String name) {
+        // This assumes '%' is fully available as a non-separator, normal
+        // character in a file name. This is probably true for all file systems.
+        final StringBuilder sb = new StringBuilder();
+        final int nameLength = name.length();
+        for (int i = 0; i < nameLength; i = name.offsetByCodePoints(i, 1)) {
+            final int codePoint = name.codePointAt(i);
+            if (DictionaryInfoUtils.isFileNameCharacter(codePoint)) {
+                sb.appendCodePoint(codePoint);
+            } else {
+                sb.append(String.format((Locale)null, "%%%1$0" + MAX_HEX_DIGITS_FOR_CODEPOINT + "x",
+                        codePoint));
+            }
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Helper method to get the top level cache directory.
+     */
+    private static String getWordListCacheDirectory(final Context context) {
+        return context.getFilesDir() + File.separator + "dicts";
+    }
+
+    /**
+     * Reverse escaping done by replaceFileNameDangerousCharacters.
+     */
+    public static String getWordListIdFromFileName(final String fname) {
+        final StringBuilder sb = new StringBuilder();
+        final int fnameLength = fname.length();
+        for (int i = 0; i < fnameLength; i = fname.offsetByCodePoints(i, 1)) {
+            final int codePoint = fname.codePointAt(i);
+            if ('%' != codePoint) {
+                sb.appendCodePoint(codePoint);
+            } else {
+                // + 1 to pass the % sign
+                final int encodedCodePoint = Integer.parseInt(
+                        fname.substring(i + 1, i + 1 + MAX_HEX_DIGITS_FOR_CODEPOINT), 16);
+                i += MAX_HEX_DIGITS_FOR_CODEPOINT;
+                sb.appendCodePoint(encodedCodePoint);
+            }
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Helper method to the list of cache directories, one for each distinct locale.
+     */
+    public static File[] getCachedDirectoryList(final Context context) {
+        return new File(DictionaryInfoUtils.getWordListCacheDirectory(context)).listFiles();
+    }
+
+    /**
+     * Returns the category for a given file name.
+     *
+     * This parses the file name, extracts the category, and returns it. See
+     * {@link #getMainDictId(Locale)} and {@link #isMainWordListId(String)}.
+     * @return The category as a string or null if it can't be found in the file name.
+     */
+    public static String getCategoryFromFileName(final String fileName) {
+        final String id = getWordListIdFromFileName(fileName);
+        final String[] idArray = id.split(BinaryDictionaryGetter.ID_CATEGORY_SEPARATOR);
+        // An id is supposed to be in format category:locale, so splitting on the separator
+        // should yield a 2-elements array
+        if (2 != idArray.length) return null;
+        return idArray[0];
+    }
+
+    /**
+     * Find out the cache directory associated with a specific locale.
+     */
+    private static String getCacheDirectoryForLocale(final String locale, final Context context) {
+        final String relativeDirectoryName = replaceFileNameDangerousCharacters(locale);
+        final String absoluteDirectoryName = getWordListCacheDirectory(context) + File.separator
+                + relativeDirectoryName;
+        final File directory = new File(absoluteDirectoryName);
+        if (!directory.exists()) {
+            if (!directory.mkdirs()) {
+                Log.e(TAG, "Could not create the directory for locale" + locale);
+            }
+        }
+        return absoluteDirectoryName;
+    }
+
+    /**
+     * Generates a file name for the id and locale passed as an argument.
+     *
+     * In the current implementation the file name returned will always be unique for
+     * any id/locale pair, but please do not expect that the id can be the same for
+     * different dictionaries with different locales. An id should be unique for any
+     * dictionary.
+     * The file name is pretty much an URL-encoded version of the id inside a directory
+     * named like the locale, except it will also escape characters that look dangerous
+     * to some file systems.
+     * @param id the id of the dictionary for which to get a file name
+     * @param locale the locale for which to get the file name as a string
+     * @param context the context to use for getting the directory
+     * @return the name of the file to be created
+     */
+    public static String getCacheFileName(String id, String locale, Context context) {
+        final String fileName = replaceFileNameDangerousCharacters(id);
+        return getCacheDirectoryForLocale(locale, context) + File.separator + fileName;
+    }
+
+    public static boolean isMainWordListId(final String id) {
+        final String[] idArray = id.split(BinaryDictionaryGetter.ID_CATEGORY_SEPARATOR);
+        // An id is supposed to be in format category:locale, so splitting on the separator
+        // should yield a 2-elements array
+        if (2 != idArray.length) return false;
+        return BinaryDictionaryGetter.MAIN_DICTIONARY_CATEGORY.equals(idArray[0]);
+    }
+
+    /**
+     * Helper method to return a dictionary res id for a locale, or 0 if none.
+     * @param locale dictionary locale
+     * @return main dictionary resource id
+     */
+    public static int getMainDictionaryResourceIdIfAvailableForLocale(final Resources res,
+            final Locale locale) {
+        int resId;
+        // Try to find main_language_country dictionary.
+        if (!locale.getCountry().isEmpty()) {
+            final String dictLanguageCountry =
+                    MAIN_DICT_PREFIX + locale.toString().toLowerCase(Locale.ROOT);
+            if ((resId = res.getIdentifier(
+                    dictLanguageCountry, "raw", RESOURCE_PACKAGE_NAME)) != 0) {
+                return resId;
+            }
+        }
+
+        // Try to find main_language dictionary.
+        final String dictLanguage = MAIN_DICT_PREFIX + locale.getLanguage();
+        if ((resId = res.getIdentifier(dictLanguage, "raw", RESOURCE_PACKAGE_NAME)) != 0) {
+            return resId;
+        }
+
+        // Not found, return 0
+        return 0;
+    }
+
+    /**
+     * Returns a main dictionary resource id
+     * @param locale dictionary locale
+     * @return main dictionary resource id
+     */
+    public static int getMainDictionaryResourceId(final Resources res, final Locale locale) {
+        int resourceId = getMainDictionaryResourceIdIfAvailableForLocale(res, locale);
+        if (0 != resourceId) return resourceId;
+        return res.getIdentifier(DEFAULT_MAIN_DICT, "raw", RESOURCE_PACKAGE_NAME);
+    }
+
+    /**
+     * Returns the id associated with the main word list for a specified locale.
+     *
+     * Word lists stored in Android Keyboard's resources are referred to as the "main"
+     * word lists. Since they can be updated like any other list, we need to assign a
+     * unique ID to them. This ID is just the name of the language (locale-wise) they
+     * are for, and this method returns this ID.
+     */
+    public static String getMainDictId(final Locale locale) {
+        // This works because we don't include by default different dictionaries for
+        // different countries. This actually needs to return the id that we would
+        // like to use for word lists included in resources, and the following is okay.
+        return BinaryDictionaryGetter.MAIN_DICTIONARY_CATEGORY +
+                BinaryDictionaryGetter.ID_CATEGORY_SEPARATOR + locale.getLanguage().toString();
+    }
+
+    public static FileHeader getDictionaryFileHeaderOrNull(final File file) {
+        try {
+            final FileHeader header = BinaryDictIOUtils.getDictionaryFileHeader(file);
+            return header;
+        } catch (UnsupportedFormatException e) {
+            return null;
+        } catch (IOException e) {
+            return null;
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/ExternalDictionaryGetterForDebug.java b/java/src/com/android/inputmethod/latin/ExternalDictionaryGetterForDebug.java
index 6e5a37c72cd8ccc25558773d4f53520f9bf389d1..d9e4bb63da943dd309bb3612d35fe66e5e19ceac 100644
--- a/java/src/com/android/inputmethod/latin/ExternalDictionaryGetterForDebug.java
+++ b/java/src/com/android/inputmethod/latin/ExternalDictionaryGetterForDebug.java
@@ -21,11 +21,8 @@ import android.content.Context;
 import android.content.DialogInterface;
 import android.content.DialogInterface.OnClickListener;
 import android.os.Environment;
-import android.util.Log;
 
-import com.android.inputmethod.latin.makedict.BinaryDictIOUtils;
 import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
-import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
 
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
@@ -44,22 +41,11 @@ public class ExternalDictionaryGetterForDebug {
             + "/Download";
     private static final String DICTIONARY_LOCALE_ATTRIBUTE = "locale";
 
-    private static FileHeader getDictionaryFileHeaderOrNull(final File file) {
-        try {
-            final FileHeader header = BinaryDictIOUtils.getDictionaryFileHeader(file);
-            return header;
-        } catch (UnsupportedFormatException e) {
-            return null;
-        } catch (IOException e) {
-            return null;
-        }
-    }
-
     private static String[] findDictionariesInTheDownloadedFolder() {
         final File[] files = new File(SOURCE_FOLDER).listFiles();
         final ArrayList<String> eligibleList = CollectionUtils.newArrayList();
         for (File f : files) {
-            final FileHeader header = getDictionaryFileHeaderOrNull(f);
+            final FileHeader header = DictionaryInfoUtils.getDictionaryFileHeaderOrNull(f);
             if (null == header) continue;
             eligibleList.add(f.getName());
         }
@@ -102,7 +88,7 @@ public class ExternalDictionaryGetterForDebug {
 
     private static void askInstallFile(final Context context, final String fileName) {
         final File file = new File(SOURCE_FOLDER, fileName.toString());
-        final FileHeader header = getDictionaryFileHeaderOrNull(file);
+        final FileHeader header = DictionaryInfoUtils.getDictionaryFileHeaderOrNull(file);
         final StringBuilder message = new StringBuilder();
         final String locale =
                 header.mDictionaryOptions.mAttributes.get(DICTIONARY_LOCALE_ATTRIBUTE);
@@ -143,7 +129,7 @@ public class ExternalDictionaryGetterForDebug {
             final String id = BinaryDictionaryGetter.MAIN_DICTIONARY_CATEGORY
                     + BinaryDictionaryGetter.ID_CATEGORY_SEPARATOR + locale;
             final String finalFileName =
-                    BinaryDictionaryGetter.getCacheFileName(id, locale, context);
+                    DictionaryInfoUtils.getCacheFileName(id, locale, context);
             final String tempFileName = BinaryDictionaryGetter.getTempFileName(id, context);
             tempFile = new File(tempFileName);
             tempFile.delete();