Friday, May 26, 2017

Limiting Android Localization to Supported Languages

When you're working with an Android app that will be used in a variety of markets, localization is an important feature to support the different countries, date formats, number and currency formats that are used around the world. Android has deep support for localization which you can read about in great detail.

In iOS, in order to add support for a language, you add languages to your project properties in an explicit way. In Android, adding support for a language is more implicit -- you simply start localizing resources. This is easy to use but it creates a problem where you might have partial support for a language in place but not be ready to take that support live. Android doesn't offer a means of explicitly listing which languages you wish customers to have access to. This can lead to situations where you have to keep localization resources on a branch or out of source-control or otherwise limit how those files are managed. That's viable but it isn't a great fit for every workflow.

On a recent client project, I did a little work with Gradle to make supported languages more explicit.  I added a list of supported languages in the build.gradle, used resource shrinking with resConfigs and exposed the languages to Java in BuildConfig using buildConfigField:

// Approved Languages
def approvedLanguages = [ "en", "ru" ]
resConfigs approvedLanguages
buildConfigField "String[]", "APPROVED_LANGUAGES", "{\"" + approvedLanguages.join("\",\"") + "\"}"

By exposing the approved languages to Java, it was possible to use it localization code, for instance to only localize dates to languages that are in the supported language list:
public class LocaleUtils {

     public static boolean isSupported(Locale locale) {
        for (String language : BuildConfig.APPROVED_LANGUAGES ) {
            if (locale.equals(locale.getLanguage()))
        for (String language : BuildConfig.APPROVED_LANGUAGES) {
            if (language.equals(locale.getLanguage()))
                 return true;
         }
         return false;
     }

     public static String localizeLocalDate(LocalDate localDate, String format) {
        return localDate.toString(
                DateTimeFormat.forPattern(format).withLocale(getDefaultSupportedLocale()));
        Locale locale = getDefaultSupportedLocale();
        DateTimeFormatter formatter = DateTimeFormat.forPattern(format).withLocale(locale);
        return localDate.toString(formatter);
     }
}

With this in place, we can start working on a new locale, integrating translated strings and graphics in the main line of development and know that they won't be exposed to customers until we're ready. With a little work, we could also use a different list of supported languages on a debug and production build.

No comments:

Post a Comment