Per-App Language Preferences in Jetpack Compose

Lucian Ghimpu
5 min readMar 27, 2024

đź“• A step-by-step guide to supporting both in-app language picker and per-app language setting in Jetpack Compose.

On the left is the Android per-app setting. On the right is the in-app language picker.

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.0or 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:

  1. Your Activity inherits from AppCompatActivity()
  2. Your xml theme needs to extend a appcompat 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.

On the left, are my resources, and on the right is the per-app language setting.

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:

  1. As part of the onboarding s
  2. 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:

  1. 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.
  2. We are setting the locale on click using the setApplicationLocales() function from the same library.
  3. I do have a ViewModel to handle some extra logic — this is of course not required.
  4. I’m using my custom Theme for styling.
  5. ⚠️ setApplicationLocales will recreate your Activity.

The above code should result in this:

Onboarding Screen

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.

--

--