A Comprehensive Guide to Programmatically Setting Locale in Android

Dec 02, 2025 · Programming · 13 views · 7.8

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.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.