Blog Infos
Author
Published
Topics
, , , ,
Published

One of the strengths of building with Compose Multiplatform is the ability to reuse business and UI logic across Android, iOS, Desktop, and Web (experimental). But when building production-ready apps, localization is essential for delivering a personalized experience to global users.

In this blog post, We’ll walk through a simple approach to implement language switching in a Compose Multiplatform app using a shared changeLanguage function—tailored to each platform.

📁 Step 1: Organizing Language Resources in Compose Multiplatform

Unlike traditional Android apps that use res/values-<lang> directories, Compose Multiplatform projects should place all localization resources under the composeResources folder in commonMain.

commonMain/
├── composeResources/
│ ├── values
│ ├────string.xml
│ ├── values-hi
│ ├────string.xml
│ ├── values-es
│ ├────string.xml

Each string.xml contains localized string resources using the same keys.
Example:

<!-- values/string.xml -->
<resources>
    <string name="email">Email</string>
    <string name="password">Password</string>
</resources>

 

<!-- values-hi/string.xml -->
<resources>
    <string name="email">ईमेल</string>
    <string name="password">पासवर्ड</string>
</resources>

 

Accessing Resources:

The org.jetbrains.compose.resources Gradle plugin processes these files and generates a type-safe Res object (you might need to run a Gradle build like ./gradlew generateComposeResClass for it to appear). You access these resources in your Composables using functions like stringResource

💡 Example: Login Screen Using Localized Strings

 

import org.jetbrains.compose.resources.stringResource
import your.project.package.generated.resources.Res // Import the generated Res object

Text(
    text = stringResource(Res.string.email),
    style = MaterialTheme.typography.titleLarge
)
OutlinedTextField(
    value = email,
    onValueChange = { email = it },
    label = { Text(stringResource(Res.string.email)) },
    modifier = Modifier.fillMaxWidth()
)
OutlinedTextField(
    value = password,
    onValueChange = { password = it },
    label = { Text(stringResource(Res.string.password)) },
    modifier = Modifier.fillMaxWidth()
)
Button(onClick = { /* login */ }) {
    Text(stringResource(Res.string.button_login))
}

 

✅ Avoid hardcoded strings like "Login" or "Enter email"—always use stringResource(Res.string.<id>) for localization support.

🧩 Step 2: Platform-Agnostic Expect Function

We begin by declaring a platform-agnostic changeLanguage function using Kotlin’s expect/actual mechanism.

// commonMain/kotlin/com/yourpackage/app/LocalizationWrapper.kt
expect fun changeLanguage(language: String)
🤖 Android Implementation

On Android, we update the default locale using Locale.setDefault().

// androidMain/kotlin/com/yourpackage/app/LocalizationWrapper.kt
actual fun changeLanguage(language: String) {
    val locale = Locale(language)
    Locale.setDefault(locale)
}
🍏 iOS Implementation

On iOS, we use NSUserDefaults to update the AppleLanguages array. This is how iOS handles preferred languages.

// iosMain/kotlin/com/yourpackage/app/LocalizationWrapper.kt
actual fun changeLanguage(language: String) {
    NSUserDefaults.standardUserDefaults
        .setObject(arrayListOf(language), "AppleLanguages")
}
🖥️ Desktop (JVM) Implementation

For desktop, it’s as simple as updating the default locale just like Android.

// desktopMain/kotlin/com/yourpackage/app/LocalizationWrapper.kt
actual fun changeLanguage(language: String) {
    val locale = Locale.of(language)
    Locale.setDefault(locale)
}
🔍 What this does:

This functions updates the default locale at runtime for your Compose Multiplatform app.

🧠 Step 3: CompositionLocal for Propagating Language

Use CompositionLocalProvider to manage the app’s current language across composables:

// commonMain/kotlin/com/yourpackage/app/App.kt
val LocalLocalization = staticCompositionLocalOf { "en" }
var languageCode by remember { mutableStateOf("en") }

 

CompositionLocalProvider(LocalLocalization provides languageCode) {
    Column {
        Button(onClick = {
            languageCode = "hi"
            changeLanguage("hi")
        }) {
            Text("Change Language")
        }
        App() // Your localized content
    }
}

 

This approach enables you to reactively rerender the UI when language changes. Combine this with string resources using a wrapper or localization library to provide multi-language support.

Job Offers

Job Offers

There are currently no vacancies.

OUR VIDEO RECOMMENDATION

No results found.

Jobs

🚀 Demo
📝 Final Thoughts

Localization is not just about translation — it’s about creating an inclusive and accessible user experience. As Compose Multiplatform continues to evolve, investing time in building a robust localization strategy will make your app stand out in international markets.

🔵 Reference:

This article was previously published on proandroiddev.com.

Menu