Per-App Language Preferences in Jetpack Compose
đź“• A step-by-step guide to supporting both in-app language picker and per-app language setting in Jetpack Compose.
Intro — some Context
I was working on a project and one of the requirements was to support an in-app language picker. If you are an Android Developer you know this subject always sparks debates.
Why?
- Well, first of all, anything related to Localization in one way or another involves some extra complexity.
- For Android specifically, there was no API to override the system language for a while; developers had to implement it themselves.
- Additionally, there’s a debate regarding the UI/UX aspect. Should we use the system language directly? Should we override it from the first launch? Should we show a language picker upon the first launch, or only inside the settings?
ℹ️ Note:
System Language — the global language of your phone.
App Language — the specific language used in your app (either forced or by user selection).
🪙 My two cents: I believe apps should allow the user to select an app-specific language. Anything that forces the user to use a specific language isn’t very user-friendly; there must be a way to allow language changes.
Fast forward to today, and you may already be aware that Android now supports per-app language settings. This means users have an additional setting within each app where they can select their preferred language.
⚠️ It’s important to note that the languages available for selection must be supported by the developer — it’s a standard practice among apps to enable per-app language settings.
This feature serves as a centralized setting that can complement existing in-app language pickers, which remain necessary for older Android versions.
Ok, but there’s pretty good documentation from Google regarding this subject that covers most use cases, why this blog post?
Well, while the documentation is extensive, it’s also somewhat scattered, providing guidelines for various aspects:
- Manual/automatic language support
- In-app switcher vs no in-app switcher
- Using AndroidX or Framework API
What I aim to provide is a step-by-step guide tailored to a common use case:
“As a developer, I want my app to support per-app language settings while still offering an in-app language picker. The implementation should be efficient, compatible with as many Android versions as possible, and utilize Jetpack Compose.”
Step by Step guide
From the above use-case, we want use:
- Automatic per-app language -> where supported languages are generated based on the resources available in our project. For example, if our project includes resources for Italian, French, and English locales, these languages will be automatically added to the per-app language setting.
- In-app language picker -> to cater to both older and newer versions of Android. This ensures users can adjust language preferences without leaving the app, enhancing user experience and convenience.
- Compose support -> I’m mentioning this because there’s a small thing we need to take care of for it to work
Step 0: Prerequisites and Compose support
Before proceeding with the guide, it’s important to follow certain prerequisites. They are not all mandatory as you can bypass them BUT it will add some extra work.
Here’s what you’ll need:
- Android Studio Giraffe and AGP 8.1 (needed for automatic per-app language support — If not possible refer to the manual approach)
compileSdkVersion
to 33 (needed for per-app setting)- Dependency to
androidx.appcompat:appcompat:1.6.0
or higher (if not possible, refer to using the framework API)
If you do choose to use appcompact
AND you are using Compose (as we will in this guide) you also need to make sure that:
- Your
Activity
inherits fromAppCompatActivity()
- Your
xml
theme needs to extend aappcompat
theme (e.g. Theme.AppCompat.NoActionBar)
I’m mentioning this because projects created in Compose usually have an activity that extends ComponentActivity
— this might lead to weird behaviors and crashes.
Step 1: Enable automatic per-app language support
You need to add in your module-level build.gradle.kts
android {
androidResources {
generateLocaleConfig = true
}
}
Then in your app module res
folder you need to create a resources.properties
to specify the default language.
unqualifiedResLocale=en-US
After this step and after a build, some files will be generated to support per-app language. That means that as long as you add new languages (via resource files), Gradle will handle the new language for you âś…
After this step, you should already see the per-app setting. Your app will be ready to handle language changes, the system will take care of keeping the preference for you.
Step 2: In-app language picker
As mentioned above, I still like to support an in-app language picker. Now when and where to show it is up to the developer.
In my case, I show it:
- As part of the onboarding s
- As a part of the settings screen
It’s up to you, I’ll showcase a simple onboarding screen in compose.
Ok, but how does it work? Do we keep a dedicated preference for the user language? Nope! We will set and use the per-app setting via a dedicated API.
Ok, let’s build the onboarding screen — first, a simple UI with a message and some input Boxes to handle the selection:
Some Notes:
- I’m hardcoding my languages — in my case, I only have two, and I’m sure they will not change. For a more dynamic and automatic approach, you can use
getApplicationLocales()
methods fromAppcompat 1.6.0.
ℹ️ I would highly recommend doing so if you have a growing app that needs to be more future-proof. - We are setting the locale on click using the
setApplicationLocales()
function from the same library. - I do have a
ViewModel
to handle some extra logic — this is of course not required. - I’m using my custom
Theme
for styling. - ⚠️
setApplicationLocales
will recreate your Activity.
The above code should result in this:
And that’s it, you can replicate the same logic anywhere, basically, the key is to make use of the setApplicationLocales
, getApplicationLocales
from AppCompact.
Step 3: Android 12 and lower
As you know, the per-app language setting is only available for Android 13 onwards. Before that, usually, developers kept their preference for the user’s language.
But luckily for us, in AndroidX this preference can be handled automatically by adding this to our manifest:
<application
...
<service
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
android:enabled="false"
android:exported="false">
<meta-data
android:name="autoStoreLocales"
android:value="true" />
</service>
...
</application>
Conclusion
That’s it! 🎉 We’ve successfully implemented both the per-app setting and our own language picker, utilizing the same source of truth for locale management.
Again, I highly recommend looking also at the official documentation if you have a different use case, we focused on a more generic one.