Localization in Compose Multiplatform
In this article, we will explore how to implement localization in a Compose Multiplatform app. We’ll cover the necessary setup, resource sharing, and platform-specific implementations to ensure your app supports multiple languages effectively. Let’s walk through the steps to get your app ready for localization.
Before we start, make sure you have generated your Kotlin Multiplatform project using the KMP wizard at kmp.jetbrains.com. This step ensures you have a structured project set up for your Compose Multiplatform application.
Project Structure
We’ll utilize a shared structure for localization resources in the commonMain/composeresources
folder. This allows you to share strings, fonts, images, videos, and files across both Android and iOS platforms.
Step 1: Update Info.plist
for iOS
In your iOS app, you need to specify the supported localizations in the Info.plist
file. Add the following XML snippet to include English and Arabic
<array>
<key>CFBundleLocalizations</key>
<array>
<string>en</string>
<string>ar</string>
</array>
</array>
Step 2: Configure Android Resources
In your composeapp/build.gradle.kts
file, ensure the following configuration is included within the android
block to enable resource generation for localization
androidResources {
generateLocaleConfig = true
}
Next, create a resources.properties
file in the androidMain/res
directory and add the following line:
unqualifiedResLocale=en-US
This setting tells Android to use the specified locale as the default. It helps the system understand which resources to load when no specific language is defined.
Step 3: Create Localized Strings in Common Main
Now, let’s create our shared string resources. In the commonMain
folder, create a file named values/strings.xml
and add the following content:
<resources>
<string name="hello">Hello, I love Kotlin</string>
<string name="en">English</string>
<string name="ar">العربية</string>
<string name="app_language">App Language: %1$s</string>
<string name="change_lang">Change Language</string>
</resources>
Additionally, create a folder named values-ar
for Arabic localization. Inside, add the strings.xml
file with the following content:
<resources>
<string name="hello">اهلا انا احب كوتلن</string>
<string name="en">English</string>
<string name="ar">العربية</string>
<string name="app_language">لغة التطبيق: %1$s</string>
<string name="change_lang">تغيير اللغة</string>
</resources>
in android stuido hit Build/Rebuild Project in order to genreate Res resources
now in commonMain/kotlin/ create a new .kt file AppLang.kt
import lokalization.composeapp.generated.resources.Res
import lokalization.composeapp.generated.resources.ar
import lokalization.composeapp.generated.resources.en
import org.jetbrains.compose.resources.StringResource
enum class AppLang(
val code: String,
val stringRes: StringResource
) {
English("en", Res.string.en),
Arabic("ar", Res.string.ar)
}
Step 4: Set Up the Android and iOS Locale Managers
Create a common interface for managing application locale in commonMain/AppLocaleManager.kt
:
interface AppLocaleManager {
fun getLocale(): String
}
@Composable
expect fun rememberAppLocale(): AppLang
Android Implementation
In the androidMain
directory, implement the AppLocaleManager
interface:
class AndroidAppLocaleManager(
private val context: Context,
) : AppLocaleManager {
private val localManager = context.getSystemService<LocaleManager>()
override fun getLocale(): String {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val locales = localManager?.applicationLocales ?: return "en"
if (locales.isEmpty) "en" else
locales[0]?.toLanguageTag()?.split("-")?.firstOrNull() ?: "en"
} else {
AppCompatDelegate.getApplicationLocales()
.toLanguageTags().split("-")
.firstOrNull() ?: "en"
}
}
}
@Composable
actual fun rememberAppLocale(): AppLang {
val context = LocalContext.current
val locale = AndroidAppLocaleManager(context).getLocale()
return remember(locale) {
locale.toApLang()
}
}
iOS Implementation
In the iosMain
directory, implement the locale manager:
class IosAppLocaleManager : AppLocaleManager {
override fun getLocale(): String {
val nsLocale = NSLocale.currentLocale.languageCode
return nsLocale
}
}
@Composable
actual fun rememberAppLocale(): AppLang {
val nsLocale = IosAppLocaleManager().getLocale()
return remember(nsLocale) {
when (nsLocale) {
"ar" -> AppLang.Arabic
else -> AppLang.English
}
}
}
Step 5: Create the Main Application Composable
Finally, in commonMain
, create the main composable function for your app:
val LocalAppLocalization = compositionLocalOf {
AppLang.English
}
@Composable
fun App() {
val currentLanguage = rememberAppLocale()
val urlLauncher = rememberUrlLauncher()
CompositionLocalProvider(LocalAppLocalization provides currentLanguage) {
MaterialTheme {
Column(
modifier = Modifier.fillMaxSize().padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
Text(stringResource(Res.string.app_language, stringResource(LocalAppLocalization.current.stringRes)))
Text(stringResource(Res.string.hello), style = TextStyle(fontSize = 20.sp, fontWeight = FontWeight.Bold))
Button(onClick = { urlLauncher.openAppSettings() }) {
Text(stringResource(Res.string.change_lang))
}
}
}
}
}
now lets run the app on ios and adnroid and test it
lets try to change the language now when Change Language clicked
Conclusion
In this article, we covered the steps to implement localization in a Kotlin Multiplatform application using Compose. By sharing resources in the commonMain/composeresources
directory and setting up platform-specific locale managers, we ensured that our app can support multiple languages seamlessly.
Github Repository