App Localization in Android with Jetpack ...

App Localization in Android with Jetpack Compose

Sep 15, 2025

image

Localization is basically the process of making your app work for different languages and cultures. In Android development, this means creating resources that the system can automatically pick based on the device’s language settings. Getting localization right makes your app accessible to way more people around the world, which is pretty crucial if you want global success.

How String Localization Works

In Android, text localization happens through string resources stored in XML files. The main file is strings.xml, located in your project's res/values/ folder.

For each additional language, you create a separate directory with a locale suffix, like values-ru/ for Russian, values-fr/ for French, and so on.

Here’s what your folder structure looks like when you add Russian and Spanish localization:

res/
├── values/
│   └── strings.xml (default strings, usually English)
├── values-ru/
│   └── strings.xml (Russian strings)
└── values-es/
    └── strings.xml (Spanish strings)

Setting Up String Resources

Let’s look at a simple localization example. Each strings.xml file contains key-value pairs. The keys (identifiers) must be the same across all languages, while the values are the translated strings.

For example, your res/values/strings.xml might look like this:

<resources>
    <string name="app_name">Hello App</string>
    <string name="welcome_message">Welcome to our app!</string>
    <string name="button_text">Click</string>
</resources>

Now, let’s say you want to add Russian localization. In Android Studio, right-click on the res/values folder and select New -> Values Resource File:

In the dialog box, set the filename to strings-ru.xml and the folder to values-ru. Even if the values-ru folder doesn't exist yet, Android Studio will create it automatically.

After this, you’ll see the added file with a “ru” suffix in Android Studio’s project structure (indicating it’s actually in the values-ru folder):

Now modify your res/values-ru/strings-ru.xml file like this:

<resources>
    <string name="app_name">Привет</string>
    <string name="welcome_message">Добро пожаловать в наше приложение!</string>
    <string name="button_text">Нажать</string>
</resources>

The values-ru/strings-ru.xml file should have the same content as values/strings.xml, but instead of the default language text (usually English), you provide the Russian translation.

Using Localized Strings in Jetpack Compose

Now you need to apply this translation in your Kotlin code. In Jetpack Compose, you can directly access string resources using the stringResource() function, which reads the string resource from strings.xml and automatically updates the content when the language changes - perfect for Compose's reactive nature.

For example, here’s what your MainActivity.kt might look like:

package com.alenibric.hiapp
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.spclass MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            // Getting string from resources
            val welcomeText = stringResource(id = R.string.welcome_message)
            
            Column {
                Text(text = welcomeText, fontSize = 22.sp)
                Button({}) {
                    Text(text = stringResource(id = R.string.button_text), fontSize = 18.sp)
                }
            }
        }
    }
}

When you run the app, it will select the localization that matches the device’s system language. For example, if my device is set to Russian, it will use the localization from the strings-ru.xml file.

When the user changes the language on their device, the system automatically loads the appropriate strings.xml file, and the app displays text in the corresponding language.

Using Placeholders in Strings

Sometimes you need to use variables in strings, like displaying a username or item count. For this, you use placeholders in strings.xml. For example:

<resources>
    <string name="app_name">Hello App</string>
    <string name="greeting_user">Hello, %1$s!</string>
    <string name="item_count">You have %1$d items.</string>
</resources>

Here, the number “1” in “%1” indicates the argument number passed to the stringResource function, and the letter "s" in "$s" indicates the argument type - string, while "d" in "$d" indicates the argument is a number.

So %1$s is a placeholder for a string, and %1$d is for a decimal integer.

In localized versions, placeholders should be preserved. For example:

<resources>
    <string name="app_name">Привет</string>
    <string name="greeting_user">Привет, %1$s!</string>
    <string name="item_count">Выбрано товаров: %1$d.</string>
</resources>

Using with stringResource:

@Composable
fun UserGreeting(userName: String, itemCount: Int) {
    val greeting = stringResource(id = R.string.greeting_user, userName)
    val countMessage = stringResource(id = R.string.item_count, itemCount)
    
    Column {
        Text(text = greeting)
        Text(text = countMessage)
    }
}

stringResource() automatically substitutes the passed arguments into the corresponding placeholders.

Beyond Text Localization

Localization isn’t limited to just text. You can also localize:

Images: Translated images go in folders named with the pattern “drawable-language_code”, such as res/drawable-ru/. For example, flag.png for English in drawable/ and flag.png with a Russian flag image in drawable-ru/.

Dimensions (dimens.xml): Useful when layout sizes should be different for different languages (for example, for languages with long words).

Icons (mipmap): For app icons, works similarly to images.

Testing Localization

To test localization, you can change the language on your device or emulator:

  1. Go to Settings -> System -> Languages & input -> Languages

  2. Add the needed language and move it to the first position

You can also use Android Studio Preview functionality to view layouts with different locales. Just add the locale parameter to the @Preview annotation:

import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
@Preview(locale = "ru")
@Composable
fun UserGreetingPreviewRu() {
    UserGreeting("Tom", 41)
}

This way, you can easily see how your app looks in different languages without constantly changing your device settings!

Enjoy this post?

Buy DelphiFan Forum a coffee

More from DelphiFan Forum