Posted by Neelansh Sahai Android Developer Relations Engineer (on Twitter and LinkedIn)What if you have a set of users who are quite fluent in English, Hindi, and Spanish, and they have a news app on their phones and prefer to read the news in Hindi? For their texting app, they prefer Spanish as they have some friends and family who they text with in Spanish. But for ease of access, they still prefer their device to be in English. Now there are many such use-cases where the users might want their app languages to be different from their system language. Interesting!
This blog focuses on how to integrate the Per-App Language Preferences API in your app to provide your users the flexibility to choose different languages for different apps.
1. Users can change the language settings from system settings by selecting:
Settings → System → Languages & Input → App Languages → [Select the desired App] → [Select the desired Language]
NOTE: Only those apps that have opted in for the feature by specifying the locale_config.xml file (more on this below), will appear in system settings.
There are 5 steps that need to be followed while working on the Per-App Language Preferences feature, listed here →
1. Create locale_config.xml file
NOTE: The locale tags must follow the BCP47 syntax, which is usually {language subtag}–{script subtag}–{country subtag}. Anything other than that will be filtered out by the system and won't be visible in the system settings.
<?xml version="1.0" encoding="utf-8"?><locale-config xmlns:android="http://schemas.android.com/apk/res/android"> ...
<!-- English --> <locale android:name="en"/>
<!-- Japanese (Japan) --> <locale android:name="ja-JP"/>
<!-- Chinese (Macao) in Simplified Script --> <locale android:name="zh-Hans-MO"/>
<!-- Chinese (Taiwan) in Traditional Script --> <locale android:name="zh-Hant-TW"/> ...</locale-config>
<manifest> ... <application ... android:localeConfig="@xml/locale_config"> </application></manifest>
def latestAppCompatVersion = “1.6.0-rc01”
dependencies { ... implementation "androidx.appcompat:appcompat:$latestAppCompatVersion" implementation "androidx.appcompat:appcompat-resources:$latestAppCompatVersion" ...}
val appLocale: LocaleListCompat = LocaleListCompat.forLanguageTags("xx-YY")// Call this on the main thread as it may require Activity.restart()AppCompatDelegate.setApplicationLocales(appLocale)
// Call this to get the selected locale and display it in your Appval selectedLocale = AppCompatDelegate.getApplicationLocales()[0]
NOTE: These APIs are also backward compatible, so even if the app is being used on Android 12 or lower, the APIs would still behave the same, and no additional checks for OS versions are required in your code.
<application ... <service android:name="androidx.appcompat.app.AppLocalesMetadataHolderService" android:enabled="false" android:exported="false"> <meta-data android:name="autoStoreLocales" android:value="true" /> </service> ...</application>
// Specify the constants to be used in the below code snippets
companion object {
// Constants for SharedPreference File const val PREFERENCE_NAME = "shared_preference" const val PREFERENCE_MODE = Context.MODE_PRIVATE // Constants for SharedPreference Keys const val FIRST_TIME_MIGRATION = "first_time_migration" const val SELECTED_LANGUAGE = "selected_language" // Constants for SharedPreference Values const val STATUS_DONE = "status_done"}
// Utility method to put a string in a SharedPreferenceprivate fun putString(key: String, value: String) { val editor = getSharedPreferences(PREFERENCE_NAME, PREFERENCE_MODE).edit() editor.putString(key, value) editor.apply()}// Utility method to get a string from a SharedPreferenceprivate fun getString(key: String): String? { val preference = getSharedPreferences(PREFERENCE_NAME, PREFERENCE_MODE) return preference.getString(key, null)}
// Check if the migration has already been done or notif (getString(FIRST_TIME_MIGRATION) != STATUS_DONE) {
// Fetch the selected language from wherever it was stored. In this case it’s SharedPref
// In this case let’s assume that it was stored in a key named SELECTED_LANGUAGE getString(SELECTED_LANGUAGE)?.let { it →
// Set this locale using the AndroidX library that will handle the storage itself val localeList = LocaleListCompat.forLanguageTags(it) AppCompatDelegate.setApplicationLocales(localeList)
// Set the migration flag to ensure that this is executed only once putString(FIRST_TIME_MIGRATION, STATUS_DONE) }}
Here are a few things that might prove to be beneficial for you users.
With that, most parts of the feature are covered. So let’s have a quick recap on what we discussed in today’s read.