How to Translate an Android App with Kotlin: Complete Guide

Learn how to localize your Android app using Kotlin and Android Studio. This comprehensive guide covers string resources, pluralization, date formatting, and best practices for Android app t

  • date icon

    Thursday, Nov 13, 2025

How to Translate an Android App with Kotlin: Complete Guide

Translating your Android app is crucial for reaching a global audience. Android provides excellent built-in support for localization through resource files, making it straightforward to support multiple languages. This guide will walk you through the process of translating an Android app using Kotlin.

Understanding Android Localization

Android localization uses string resources stored in XML files within res/values directories. Each language gets its own values-xx folder (e.g., values-es for Spanish, values-fr for French). Android automatically loads the correct strings based on the user’s device language settings.

Step 1: Create String Resources

Start by creating string resources in your res/values/strings.xml file:

res/values/strings.xml (English - default):

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">My App</string>
    <string name="welcome_message">Welcome to our app!</string>
    <string name="button_submit">Submit</string>
    <string name="error_network">Network error. Please try again.</string>
    <string name="items_count">%1$d items</string>
</resources>

Step 2: Add Translations for Other Languages

Create language-specific folders and string files:

res/values-es/strings.xml (Spanish):

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">Mi Aplicación</string>
    <string name="welcome_message">¡Bienvenido a nuestra aplicación!</string>
    <string name="button_submit">Enviar</string>
    <string name="error_network">Error de red. Por favor, inténtalo de nuevo.</string>
    <string name="items_count">%1$d elementos</string>
</resources>

res/values-fr/strings.xml (French):

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">Mon Application</string>
    <string name="welcome_message">Bienvenue dans notre application!</string>
    <string name="button_submit">Soumettre</string>
    <string name="error_network">Erreur réseau. Veuillez réessayer.</string>
    <string name="items_count">%1$d éléments</string>
</resources>

Step 3: Use String Resources in Kotlin

Replace hardcoded strings with resource references:

Before:

val welcomeTextView = findViewById<TextView>(R.id.welcomeText)
welcomeTextView.text = "Welcome to our app!"

After:

val welcomeTextView = findViewById<TextView>(R.id.welcomeText)
welcomeTextView.text = getString(R.string.welcome_message)

Or in XML layouts:

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/welcome_message" />

Step 4: Handle String Formatting with Variables

Use getString() with format arguments for strings with variables:

strings.xml:

<string name="items_count">%1$d items</string>
<string name="user_greeting">Hello, %1$s! You have %2$d messages.</string>

Kotlin:

val itemCount = 5
val message = getString(R.string.items_count, itemCount)
// English: "5 items"
// Spanish: "5 elementos"

val userName = "John"
val messageCount = 3
val greeting = getString(R.string.user_greeting, userName, messageCount)
// English: "Hello, John! You have 3 messages."

Step 5: Pluralization

Android supports pluralization using plurals resources:

res/values/strings.xml:

<plurals name="items_count">
    <item quantity="zero">No items</item>
    <item quantity="one">%d item</item>
    <item quantity="other">%d items</item>
</plurals>

res/values-es/strings.xml:

<plurals name="items_count">
    <item quantity="zero">No hay elementos</item>
    <item quantity="one">%d elemento</item>
    <item quantity="other">%d elementos</item>
</plurals>

Kotlin usage:

val count = 1
val message = resources.getQuantityString(R.plurals.items_count, count, count)
// English: "1 item" (singular)
// English: "5 items" (plural)
// Spanish: "1 elemento" (singular)
// Spanish: "5 elementos" (plural)

Step 6: Localize Images and Drawables

You can provide different images for different languages:

  1. Create language-specific drawable folders:

    • res/drawable/ (default)
    • res/drawable-es/ (Spanish)
    • res/drawable-fr/ (French)
  2. Place localized images in each folder with the same name

  3. Reference them normally:

imageView.setImageResource(R.drawable.flag_icon)
// Android automatically selects the correct image based on language

Step 7: Format Dates and Numbers

Use Locale and formatting classes for localized formatting:

Dates:

import java.text.SimpleDateFormat
import java.util.*

val date = Date()
val formatter = SimpleDateFormat("EEEE, MMMM dd, yyyy", Locale.getDefault())
val localizedDate = formatter.format(date)
// English: "Wednesday, March 20, 2025"
// Spanish: "miércoles, 20 de marzo de 2025"

Numbers:

import java.text.NumberFormat
import java.util.*

val number = 1234.56
val formatter = NumberFormat.getNumberInstance(Locale.getDefault())
val localizedNumber = formatter.format(number)
// English: "1,234.56"
// Spanish: "1.234,56"

Currency:

val amount = 1234.56
val formatter = NumberFormat.getCurrencyInstance(Locale.getDefault())
val localizedCurrency = formatter.format(amount)
// English (US): "$1,234.56"
// Spanish (ES): "1.234,56 €"

Step 8: Handle Right-to-Left (RTL) Languages

Android automatically handles RTL layouts when you use:

  • start and end instead of left and right in XML
  • paddingStart/paddingEnd instead of paddingLeft/paddingRight
  • marginStart/marginEnd instead of marginLeft/marginRight

XML:

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:paddingStart="16dp"
    android:paddingEnd="16dp"
    android:textAlignment="viewStart" />

Kotlin:

textView.paddingStart = 16.dp
textView.paddingEnd = 16.dp

Step 9: Test Your Localization

Using Android Studio:

  1. Go to Run > Edit Configurations
  2. Under General, set Language to the language you want to test
  3. Run your app

Using Device Settings:

  1. Go to Settings > System > Languages & input
  2. Add or change the device language
  3. Launch your app

Programmatically (for testing):

import android.content.res.Configuration
import android.content.res.Resources
import java.util.*

fun setAppLocale(context: Context, language: String) {
    val locale = Locale(language)
    Locale.setDefault(locale)
    val config = Configuration()
    config.setLocale(locale)
    context.resources.updateConfiguration(config, context.resources.displayMetrics)
}

Best Practices

1. Use Descriptive String Names

Bad:

<string name="s1">Submit</string>

Good:

<string name="button_submit">Submit</string>

2. Avoid Hardcoding Strings

Bad:

Toast.makeText(this, "Error occurred", Toast.LENGTH_SHORT).show()

Good:

Toast.makeText(this, getString(R.string.error_occurred), Toast.LENGTH_SHORT).show()

3. Use String Arrays for Lists

res/values/strings.xml:

<string-array name="countries">
    <item>United States</item>
    <item>Canada</item>
    <item>Mexico</item>
</string-array>

Kotlin:

val countries = resources.getStringArray(R.array.countries)

4. Handle HTML in Strings

If you need HTML formatting:

strings.xml:

<string name="formatted_text"><![CDATA[This is <b>bold</b> and <i>italic</i> text.]]></string>

Kotlin:

val htmlText = getString(R.string.formatted_text)
textView.text = Html.fromHtml(htmlText, Html.FROM_HTML_MODE_LEGACY)

5. Test String Lengths

Some languages are longer than others. Design your UI to accommodate:

  • German and Finnish: 30-50% longer than English
  • Asian languages: May need more vertical space
  • Arabic and Hebrew: RTL layout considerations

6. Use Context-Specific Strings

If the same word has different meanings in different contexts:

strings.xml:

<string name="button_cancel">Cancel</string>
<string name="action_cancel_order">Cancel Order</string>

Common Pitfalls

1. Forgetting to Localize All Strings

Make sure to localize:

  • Button labels
  • Error messages
  • Toast messages
  • Dialog titles and messages
  • Menu items
  • Notification text

2. Hardcoding Format Strings

Bad:

val price = String.format("$%.2f", amount)

Good:

val formatter = NumberFormat.getCurrencyInstance(Locale.getDefault())
val price = formatter.format(amount)

3. Not Handling Missing Translations

If a translation is missing, Android falls back to the default language (usually English). Always provide fallbacks:

val text = try {
    getString(R.string.some_string)
} catch (e: Resources.NotFoundException) {
    getString(R.string.default_string)
}

4. Ignoring RTL Support

Always use start/end instead of left/right to support RTL languages like Arabic and Hebrew.

Advanced: Dynamic Language Switching

To allow users to change language within the app:

import android.content.Context
import android.content.res.Configuration
import java.util.*

class LocaleHelper {
    companion object {
        fun setLocale(context: Context, language: String): Context {
            val locale = Locale(language)
            Locale.setDefault(locale)
            
            val config = Configuration(context.resources.configuration)
            config.setLocale(locale)
            
            return context.createConfigurationContext(config)
        }
        
        fun getPersistedLanguage(context: Context): String {
            val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
            return prefs.getString("language", "en") ?: "en"
        }
        
        fun persistLanguage(context: Context, language: String) {
            val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
            prefs.edit().putString("language", language).apply()
        }
    }
}

Usage in Activity:

class MainActivity : AppCompatActivity() {
    override fun attachBaseContext(newBase: Context) {
        val language = LocaleHelper.getPersistedLanguage(newBase)
        super.attachBaseContext(LocaleHelper.setLocale(newBase, language))
    }
    
    fun changeLanguage(language: String) {
        LocaleHelper.persistLanguage(this, language)
        recreate() // Restart activity to apply new language
    }
}

Using Jetpack Compose

If you’re using Jetpack Compose for UI:

import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext

@Composable
fun WelcomeScreen() {
    val context = LocalContext.current
    Text(text = context.getString(R.string.welcome_message))
}

// With formatting
@Composable
fun ItemCount(count: Int) {
    val context = LocalContext.current
    Text(text = context.getString(R.string.items_count, count))
}

Conclusion

Localizing your Android app with Kotlin is straightforward when you follow these steps:

  1. Create strings.xml files for each language in res/values-xx/ folders
  2. Use getString() instead of hardcoded strings
  3. Handle pluralization with plurals resources
  4. Format dates and numbers using Locale
  5. Support RTL languages with start/end attributes
  6. Test thoroughly in all supported languages

By following these practices, you’ll create an app that provides a native experience for users worldwide, significantly expanding your potential user base.

Streamline Your Android Localization Workflow

Managing translations for multiple languages can become complex as your app grows. Consider using a translation management platform to:

  • Collaborate with translators
  • Keep translations in sync with your codebase
  • Automate the translation workflow
  • Maintain consistency across all languages
  • Integrate with your CI/CD pipeline

Ready to take your Android app global? Explore AZbox’s localization platform and streamline your translation workflow:

View AZbox Plans and Pricing

Blog

Latest Posts

Discover our latest articles and updates.

Why Translations Have Always Been a Problem in Software Development
date icon

Sunday, Dec 21, 2025

Why Translations Have Always Been a Problem in Software Development

For decades, software developers have struggled with translations and localization. What should be a straightforward pro

Read More
Cómo Traducir una App Flutter con Azbox: Guía Completa
date icon

Saturday, Dec 20, 2025

Cómo Traducir una App Flutter con Azbox: Guía Completa

Traducir tu app Flutter es esencial para llegar a una audiencia global. Azbox proporciona un potente SDK de Flutter que

Read More
How to Translate a Flutter App with Azbox: Complete Guide
date icon

Saturday, Dec 20, 2025

How to Translate a Flutter App with Azbox: Complete Guide

Translating your Flutter app is essential for reaching a global audience. Azbox provides a powerful Flutter SDK that sim

Read More
cta-image

Start Global Growth Today

Join hundreds of successful companies already using AZbox to reach customers worldwide. Start with a free trial, no credit card required.

Get Started - It's Free