Keywords: Android | Locale | Programmatic Setting | Multi-language Support | Compatibility Handling
Abstract: This article provides an in-depth analysis of programmatically setting the locale in Android applications, covering the evolution from deprecated APIs to modern methods like AppCompatDelegate.setApplicationLocales(). Based on the best answer, it extracts core knowledge points, offers step-by-step code examples, and best practices for dynamic multi-language switching across different Android versions. The content emphasizes avoiding common pitfalls, such as proper text escaping and compatibility handling, to ensure stable app performance on diverse devices.
Introduction
In Android development, supporting multiple languages is essential for enhancing global user experience. Users often need to dynamically switch app languages, for instance, an Italian user might prefer Spanish over English. Traditional methods like directly modifying Configuration.locale have been gradually deprecated. This article systematically introduces modern approaches to programmatically set the locale, based on the latest best practices.
Core Concepts of Locale Setting
Locale in Android defines language and region-specific settings, affecting interface text, date formats, etc. Programmatic setting allows runtime changes without reinstalling the app. The key challenge lies in adapting to different Android versions to avoid compatibility issues.
Implementation Methods for Different Android Versions
For Android 13 and Above
Starting from Android 13, it is recommended to use the AppCompatDelegate.setApplicationLocales() method, which offers backward compatibility. This method simplifies locale setting and automatically handles Activity restart, eliminating the need for manual recreate() calls. Example:
val appLocale: LocaleListCompat = LocaleListCompat.forLanguageTags("es-ES")
AppCompatDelegate.setApplicationLocales(appLocale)
For API 33 and Above
For API 33 and above, the process is similar to the above method. Note that it should be called on the main thread as it may trigger Activity restart. The code example applies to both Kotlin and Java.
Handling Lower APIs
For APIs below 33, additional steps are required for compatibility. First, add a service to AndroidManifest.xml:
<service android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
android:enabled="false"
android:exported="false">
<meta-data
android:name="autoStoreLocales"
android:value="true" />
</service>
This service is available from AndroidX library version 1.6.0-alpha01 onward, assisting in storing locale settings.
Complete Code Example and Rewrite
Based on the best answer, here is a rewritten Kotlin code example for a BaseActivity that handles locale updates:
import android.content.Context
import android.content.res.Configuration
import android.content.res.Resources
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import java.util.Locale
open class BaseActivity : AppCompatActivity() {
override fun attachBaseContext(base: Context) {
super.attachBaseContext(updateBaseContextLocale(base))
}
private fun updateBaseContextLocale(context: Context): Context {
val language = SharedPrefUtils.getSavedLanguage() // Assume retrieving saved language from SharedPreferences
val locale = Locale(language)
Locale.setDefault(locale)
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
updateResourcesLocale(context, locale)
} else {
updateResourcesLocaleLegacy(context, locale)
}
}
@TargetApi(Build.VERSION_CODES.N)
private fun updateResourcesLocale(context: Context, locale: Locale): Context {
val configuration = Configuration(context.resources.configuration)
configuration.setLocale(locale)
return context.createConfigurationContext(configuration)
}
@SuppressWarnings("deprecation")
private fun updateResourcesLocaleLegacy(context: Context, locale: Locale): Context {
val resources = context.resources
val configuration = resources.configuration
configuration.locale = locale
resources.updateConfiguration(configuration, resources.displayMetrics)
return context
}
// Optional: Handle compatibility issues for Android 6 and 7
override fun applyOverrideConfiguration(overrideConfiguration: Configuration) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) {
// Update locale settings in overrideConfiguration here
setLocaleForOverride(overrideConfiguration) // Custom implementation needed
}
super.applyOverrideConfiguration(overrideConfiguration)
}
}
Best Practices and Considerations
First, use SharedPreferences to persist the user-selected locale, preventing reset on each app launch. When setting the locale, save the language code, e.g., SharedPrefUtils.saveLocale(locale). Second, for lower APIs, note that the updateConfiguration method is partially deprecated; prefer createConfigurationContext. Additionally, when handling Activity restart, consider user experience by showing progress indicators if needed.
In code, text content such as HTML tags must be properly escaped. For example, when describing XML configurations, use <service> instead of direct <service> to prevent parsing errors. This adheres to the principle of "retain normal tags, escape text content."
Conclusion
Programmatically setting the locale is a core feature for multilingual Android applications. By adopting modern APIs like AppCompatDelegate.setApplicationLocales() and combining with BaseActivity for compatibility, developers can efficiently implement dynamic language switching. Always test across different Android versions to ensure code stability and consistent user experience. As Android evolves, stay updated with official documentation to adapt to new changes.