Android will run on many devices in many regions. To reach the most users, your application should handle text, audio files, numbers, currency, and graphics in ways appropriate to the locales where your application will be used. The resource framework automatically selects the resources that best match the device.

Such a behaviour is ok for most apps. But for some specialized app, it might be required to change the localization at runtime, e.g. through in-app Settings.

Make sure that you are already familiar with the following concepts: Resources, Configuration, and Locale.

The first step is to set up the project for localization. This is very simple and all you need is to add resources for different localizations.

The next step is to actually change the localization at runtime. This is pretty straightforward and can be done like this:

public class Language {
    private static String PREFS_LANGUAGE = "LANGUAGE";

    public static void setLanguage(Context context, String language) {
        // Save selected language
        persist()

        // Update language
        Locale locale = new Locale(language);
        Locale.setDefault(locale);
        Configuration config = new Configuration();
        config.locale = locale;
        context.getResources().updateConfiguration(config, context.getResources().getDisplayMetrics());
    }

    private static SharedPreferences getSharedPrefs(Context context) {
        return context.getSharedPreferences(context.getPackageName(), MODE_PRIVATE);
    }
}

There are a few other things that you need to keep in mind when changing the language at runtime. The most important is to store the user’s selection in order to retain the language with app restarts. SharedPreferences is the preferred method to handle this.

private static void persist(String language) {
    SharedPreferences.Editor editor = getSharedPrefs(context).edit();
    editor.putString(PREFS_LANGUAGE, language);
    editor.apply();
}

If you need to access the current language anywhere in the app, you can add some utility methods too:

public static String currentLanguageString(Context context) {
    return getString(context, currentLanguage(context));
}

public static String getString(Context context, String identifier) {
    int resourceId = context.getResources().getIdentifier(identifier, "string", context.getPackageName());
    return context.getResources().getString(resourceId);
}

public static String currentLanguage(Context context) {
    SharedPreferences prefs = getSharedPrefs(context);
    if (!prefs.contains(PREFS_LANGUAGE)) {
        String deviceLanguage = Locale.getDefault().getLanguage();
        if (Arrays.asList(Constants.languages).contains(deviceLanguage)) {
            setLanguage(context, deviceLanguage);
        } else {
            setLanguage(context, Constants.languages[0]);
        }
    }
    return prefs.getString(PREFS_LANGUAGE, Constants.languages[0]);
}

If you have tried the code upto now, you will notice that it works great when you change the language. But as soon as you navigate to another activity, the localization switches back to the default one. We need to update the localization before displaying all activities. This can be done by creating a BaseActivity that your other activities can inherit from.

public class BaseAppCompatActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        Language.setLanguage(this, Language.currentLanguage(this));
        super.onCreate(savedInstanceState);
    }
}