SJ cartoon avatar

Mobile Changing Android's Locale Programmatically

Oh, the hacks we do to support user requirements…

In a kiosk app I wrote, I was asked to support setting up a tablet’s wifi and changing the tablet’s language in-app to support a few languages by administrators with lowered permissions (the kiosk app was locked down, the admins in question could not exit the app to go to the settings page).

Like a good, wholesome developer - I followed best practices as well as I could, and I started an intent to load the desired Settings pages:

@OnClick(R.id.button_wifi_setup)
void setupWifi() {
    startActivity(new Intent(Settings.ACTION_WIFI_SETTINGS));
}

@OnClick(R.id.button_language_setup)
void setupLanguage() {
    startActivity(new Intent(Settings.ACTION_LOCALE_SETTINGS));
}

When clicked, these buttons will load the Wifi or Language Settings, and when those activities are finished - we come back to the Kiosk app. Worked like a charm, no problems.

Features Are Creeps

The latest feature the client wanted was that ANYONE using the Kiosk app could change the app’s language very quickly (no new screen loaded - e.g. via a drop-down). The other downside of the Locale Settings page was that it shows every language the tablet supports - whether or not those languages are actually translated in the app.

In this case, the client only supported three languages, so showing a list of 30 languages to a user might imply that each of those languages was supported.

At first, I wasn’t sure how possible this feature was - but then I remembered that it was Android, and so everything is possible in some way, shape, or form.

Changing Your Locale

Kudos go out to Gunhan for that post as it’s super helpful.

I’ve re-packaged that work into a non-persisting setup (persistence code is commented out) with a few StringDefs and other cleanup code.

This method does the bulk of the work:

private static boolean updateResources(Context context, String language) {
    Locale locale = new Locale(language);
    Locale.setDefault(locale);

    Resources resources = context.getResources();

    Configuration configuration = resources.getConfiguration();
    configuration.locale = locale;

    resources.updateConfiguration(configuration, resources.getDisplayMetrics());

    return true;
}

It selects a new locale from the language string that was passed in, and then updates the app’s resource files.

An example of use (through a static public function) is this:

@OnClick(R.id.button_change_locale)
void changeLocale() {
    if (++mLanguageIndex >= LocaleUtils.LocaleDef.SUPPORTED_LOCALES.length) {
        mLanguageIndex = 0;
    }

    LocaleUtils.setLocale(this, mLanguageIndex);
}

Of note, this new language will ONLY apply to new Activities, not the one you’re currently on.

Instantaneous Locale Update

To get around this little issue, there are a few methods. An easy one is to just re-load the current activity, or have your language changing code in a different activity. That way, when you come back to the original page, the language settings would have taken effect.

I don’t like the user experience in that case.

Another method (untested), is what you would do with a regular settings change, which is to setup a locale change receiver and act upon that. Should work, but feels a bit heavy for me.

My extremely simple, and non-elegant, solution is to have a method on the page where I’m changing languages that just re-loads the strings that are on the page. Usually, there are so few strings that this solution takes seconds to implement.

private void setupUi() {
    mChangeLocaleButton.setText(R.string.current_language);
    mLoadActivityButton.setText(R.string.open);
}

Check Out the Code

I’ve created a Github repo here: https://github.com/sureshjoshi/android-locale-example where you can check out the code to see exactly what the LocaleUtils helper looks like.

Feature Photo credit: Rob Lee / Foter / CC BY-ND