Android localization: How to localize Android apps with examples

With Android’s global reach, getting Android localization right is an important part of app development.

Whether you’re a new developer working on your first app or an experienced programmer adding another one to your collection, the key question is: Who are you building this app for? In other words, who’s your target audience?

If you want your mobile apps to grow beyond your local area, possibly reaching users across different countries, here’s something to consider: most of the world doesn’t speak English. Eventually, you’ll need to support multiple languages.

Incorporating software internationalization into your development process from the beginning will help streamline localization efforts, making it easier to adapt your app for different languages and cultural contexts later on.

That’s why localizing your Android app is necessary. In this article, we’ll show you how to get started with Android localization using clear, step-by-step examples.

Check out how our platform can help you localize your Android apps faster.

The source code is available on GitHub.

    Preparing for Android localization

    Step 1: Create a new Android project

    To start your Android localization journey, first create a new Android project using the “Empty Activity” template in Android Studio. Android Studio not only simplifies project setup but also provides a built-in emulator for testing your mobile application translation on various devices and configurations, which is essential when verifying localization across different locales.

    For this example, we’ll be building a Kotlin-based project. Kotlin is a modern, concise language that integrates seamlessly with Android development, making it easier to manage localization and other app features.

    Here’s the project configuration we’ll use:

    • Name: LokaliseI18n
    • Package name: com.example.lokalisei18n
    • Language: Kotlin
    • Minimum SDK: API 31

    Having entered this information, hit Create and proceed to the next step.

    Step 2: Add some elements

    Next, let’s add some content to the empty MainActivity. Open the activity_main.xml file located in the app/src/main/res/layout directory. Add a simple TextView displaying the string “Hello world!” (note the ID attribute):

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="10dp"
        android:gravity="center_horizontal"
        tools:context=".MainActivity">
    
        <TextView
            android:id="@+id/test_text_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello world!"
            android:textSize="26sp" />
    </LinearLayout>
    

    However, there’s an issue here: the “Hello world!” string is hardcoded directly into the TextView. This is generally bad practice, as it makes the app harder to maintain and adapt, especially when you want to support multiple languages.

    For proper localization, you’ll need to handle text dynamically so the app can easily change languages based on user preferences.

    How to add Android language resources

    Step 1: Add string resources

    Let’s start by adding language resources to our Android app. These resources will hold the localization-related values, acting as static assets for multiple languages.

    Android provides a specific structure to organize app resources. Inside the res/values directory, you’ll find a strings.xml file that holds the text strings for the app.

    Let’s add a new string resource to strings.xml for the “Hello world!” text:

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <string name="app_name">LokaliseI18n</string>
        <string name="hello_world">Hello world!</string>
    </resources>

    Here, we’ve added the hello_world key to store the text “Hello world!”. While it’s fine for simple contexts like this, it’s a good habit to create self-explanatory names for your keys. For best practices, check out our blog post on working with translation keys.

    Step 2: Add more resources

    Next, let’s add localization for another language, such as Latvian. Android Studio has built-in support for localization (l10n), which makes this easier.

    Create a resource file

    1. Right-click on the res folder and choose New > Android resource file.
    2. In the resource file wizard, choose Values resource type from the dropdown menu and select Locale from the list of available qualifiers.
    3. Choose lv: Latvian from the language list. Keep the region setting at “Any region”.
    4. Name the file strings.xml. Android Studio will automatically create the folder res/values-lv/.
    5. Click OK. A new strings.xml file will open in the res/values-lv/ directory.

    Add new resource in Android Studio

    Add the Latvian resources to the newly created file:

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <string name="app_name">LokaliseI18n</string>
        <string name="hello_world">Sveika pasaule!</string>
    </resources>

    Now our mobile app has two supported languages: American English and Latvian.

    Step 3: Refer to the resources

    Once your language resources are set up, let’s reference them in the TextView. Open activity_main.xml and update the android:text attribute to use the resource key hello_world:

    <TextView android:id="@+id/test_text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world"
        android:textSize="26sp" />

    This key acts as a placeholder that will be replaced with the actual translation.

    Step 4: Refer to resources dynamically

    If you need to change the TextView text dynamically, do it programmatically in MainActivity.kt. Use the following code:

    package com.example.lokalisei18n
    
    import android.os.Bundle
    import androidx.appcompat.app.AppCompatActivity
    import android.widget.TextView
    import androidx.appcompat.app.ActionBar
    
    class MainActivity : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            val actionBar: ActionBar? = supportActionBar
            actionBar?.setTitle(R.string.app_name) // Set ActionBar title
            val textView = findViewById<TextView>(R.id.test_text_view)
            textView.setText(R.string.hello_world) // Set TextView text
        }
    }

    Main things to note:

    • The ActionBar title is set to the app_name string resource.
    • The test_text_view text is set to the hello_world string resource.

    Now your app is localized for two languages: English and Latvian.

    Step 5: Test Android localization

    You might be thinking, “It looks the same as before!” But in reality, the “Hello world!” text is no longer hardcoded. Instead, it’s pulled from the localized resources based on the device’s system language. To test this, run the app on an Android device with the system language set to Latvian.

    However, how do we switch our app to another language without touching the device settings? We’ll explore this in the next section.

    How to change the application locale programmatically

    By default, Android loads resources based on the system language set on the user’s device. For example, if Agnese, a Latvian user with her phone set to Latvian, opened our app, she’d see the content in Latvian. But what if another user wants to use Latvian while their phone’s default language is set to English?

    In this case, we need to override the default system locale programmatically. Let’s walk through how to achieve this by creating a new ContextUtils.kt file.

    Step 1: Create a utility class to update the locale

    Create a new utils/ContextUtils.kt file with the following code to add a method for updating the locale:

    package com.example.lokalisei18n.utils
    
    import android.content.Context
    import android.content.ContextWrapper
    import android.os.LocaleList
    import java.util.Locale
    
    class ContextUtils(base: Context) : ContextWrapper(base) {
        companion object {
            fun updateLocale(context: Context, localeToSwitchTo: Locale): ContextUtils {
                val resources = context.resources
                val configuration = resources.configuration // 1
    
                val localeList = LocaleList(localeToSwitchTo) // 2
                LocaleList.setDefault(localeList) // 3
                configuration.setLocales(localeList) // 4
    
                val updatedContext = context.createConfigurationContext(configuration) // 5
    
                return ContextUtils(updatedContext)
            }
        }
    }

    Key points:

    1. Create a new configuration object using the current resource configuration.
    2. Create a LocaleList with the locale we want to switch to.
    3. Set the default locale for the app to the first locale in the list (in this case, the one we created).
    4. Update the configuration by setting the new locale list.
    5. Create a new context with the updated configuration, then return it.

    Step 2: Use the utility class in MainActivity

    Now, let’s use the ContextUtils class in our app to change the locale. Update the MainActivity.kt file with the following code:

    package com.example.lokalisei18n
    
    import android.os.Bundle
    import androidx.appcompat.app.AppCompatActivity
    import android.widget.TextView
    import androidx.appcompat.app.ActionBar
    
    import java.util.Locale
    import android.content.Context
    import com.example.lokalisei18n.utils.ContextUtils
    
    class MainActivity : AppCompatActivity() {
        // ...
    
        override fun attachBaseContext(newBase: Context) {
            val localeToSwitch = Locale("lv")
            val localeUpdatedContext = ContextUtils.updateLocale(newBase, localeToSwitch)
            super.attachBaseContext(localeUpdatedContext)
        }
    }

    How it works:

    • We create a new ContextWrapper with the locale updated to Latvian ("lv").
    • This updated context is then passed to the attachBaseContext method of MainActivity.
    • When the activity starts, it runs in a context with the locale set to Latvian, even if the device’s system language is set to something else.

    Step 3: Test the locale change

    Now, run the app in the emulator. You should see the text localized to Latvian without needing to change the system language manually.

    And that’s it! You’ve successfully overridden the system locale programmatically.

    In the next section, we’ll cover other challenges you might encounter during your Android app localization journey.

    How to pluralize nouns

    When dealing with nouns, it’s important to pluralize them correctly according to the language’s rules. For example, you don’t want your app to say “I have 3 cat” instead of “3 cats.” This is where Android’s built-in support for quantity strings (plurals) is useful. Let’s walk through how to implement it.

    Step 1: Add TextViews for pluralization

    First, add three TextViews to activity_main.xml, which will display the text for one, few, and many quantities:

    <TextView android:id="@+id/plural_view_one"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="26sp" />
    
    <TextView android:id="@+id/plural_view_few"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="26sp" /> 
    
    <TextView android:id="@+id/plural_view_many"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="26sp" />

    Step 2: Understand pluralization rules

    Pluralization rules differ across languages. For example:

    • English has two main forms:
      • I have 0 cats
      • I have 1 cat
      • I have 3 cats
    • Latvian has three variations:
      • Man ir 0 kaķu
      • Man ir 1 kaķis
      • Man ir 3 kaķi

    To handle this, you’ll need to create separate plural elements for each language.

    Step 3: Define plural strings

    English pluralization

    In res/values/strings.xml, add the plural forms for American English:

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <string name="app_name">LokaliseI18n</string>
        <string name="hello_world">Hello world!</string>
        <plurals name="my_cats">
            <item quantity="one">I have %s cat</item>
            <item quantity="other">I have %s cats</item>
        </plurals>
    </resources>

    Next, take care of the res/values-lv/strings.xml file:

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <string name="app_name">LokaliseI18n</string>
        <string name="hello_world">Sveika pasaule!</string>
        <plurals name="my_cats">
            <item quantity="zero">Man ir %s kaķu</item>
            <item quantity="one">Man ir %s kaķis</item>
            <item quantity="other">Man ir %s kaķi</item>
        </plurals>
    </resources>

    This defines how the app will handle quantities in both languages.

    In case you’re interested, the syntax for plurals elements is documented in the syntax section of the Android documentation for quantity strings.

    Step 4: Set pluralized text in TextViews

    In the MainActivity.kt file, set the pluralized text in the TextViews using Resources.getQuantityString. Here’s how to do it:

    override fun onCreate(savedInstanceState: Bundle?) {
        // ...
    
        with (resources) {
            val pluralViewOne = findViewById<TextView>(R.id.plural_view_one)
            val quantityStringFor1 = getQuantityString(R.plurals.my_cats, 0, 0)
            pluralViewOne.text = quantityStringFor1
    
            val pluralViewFew = findViewById<TextView>(R.id.plural_view_few)
            val quantityStringFor2 = getQuantityString(R.plurals.my_cats, 1, 1)
            pluralViewFew.text = quantityStringFor2
            
            val pluralViewMany = findViewById<TextView>(R.id.plural_view_many)
            val quantityStringFor5 = getQuantityString(R.plurals.my_cats, 3, 3)
            pluralViewMany.text = quantityStringFor5
        }
    }

    getQuantityString uses the plural resource and the count to retrieve the correct string.The second parameter is the quantity, and the third is the value to replace %s in the string.

    Step 5: Test the pluralization

    Run the app and check if the text is properly pluralized for both English and Latvian.

    How to localize date and time

    To localize date and time in your Android app, you can use the getDateTimeInstance method from the DateFormat class, which automatically formats date and time according to the current locale.

    Step 1: Add a TextView for displaying the localized date and time

    First, add a TextView in activity_main.xml that will display the localized date and time:

    <TextView android:id="@+id/localized_date_time_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="26sp" />

    Step 2: Set the localized date and time in MainActivity

    Now, let’s update the MainActivity class to show the localized date and time in the TextView when the app launches. Add the following code in the onCreate method:

    // other imports...
    
    import java.text.DateFormat
    import java.util.Date
    
    class MainActivity : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
                // ...
    
                val currentDateTime = Date() // 1
                val localizedDateTimeView = findViewById<TextView>(R.id.localized_date_time_view)
                localizedDateTimeView.text = DateFormat.getDateTimeInstance().format(currentDateTime) // 2
            }
        }
        
        // ...
    }

    Explanation:

    • Create a Date object: val currentDateTime = Date() creates a Date object that represents the exact date and time at the moment of its creation.
    • Format the date and time: DateFormat.getDateTimeInstance().format(currentDateTime) automatically formats the date and time according to the device’s locale, using the DateFormat class.

    Step 3: Test the app

    Now, run the app to ensure the date and time are displayed in the appropriate format based on the device’s locale.

    Use Lokalise for Android localization

    If you’ve made it this far, you’re clearly serious about internationalization for your Android app. But let’s be real: do you or your team have the time to dive into all these new classes, methods, and deprecated variables just to handle localization?

    Probably not.

    As your app grows, so will the number of strings and languages you need to manage. That’s where a translation management system like Lokalise comes in handy. Let’s walk through how it works.

    Step 1: Set up a Lokalise project

    To get started, create a free account or log in to Lokalise. In the dashboard, click the New project button and create a Web and mobile project. Enter the following information:

    Create a new project on Lokalise for Android localization

    Once the project is created, click Upload files:

    Upload translation files to Lokalise

    Upload your language resource files, such as the strings.xml files we created in the tutorial. If Lokalise detects the wrong language, select the correct one from the dropdown menu.

    Adjust the files to upload

    Click the Import files button to complete the resource uploading process. In the Editor tab, you’ll see all your project’s resources neatly organized.

    Manage translations on Lokalise

    Step 2: Manage resources with Lokalise

    Once your language resources are uploaded, Lokalise allows you to:

    • Easily edit translations in the UI.
    • Request professional translation services.
    • Use AI to perform translation.
    • Generate translation files for multiple locales.
    • Integrate with numerous third-party services.
    • And many more!

    For example, if you’re a dog person and want to change all instances of “cat” to “dog,” simply click on the translation and edit it.

    Edit translations as needed

    Then, head to the Download page and select the Android Resources (.xml) format from the dropdown menu:

    Choose Android XML file format to download

    Click Build and download to get a ZIP file with your updated translations. Replace the old resource files in your project with these new ones.

    Step 3: Over-the-air (OTA) updates

    Lokalise also offers an over-the-air (OTA) feature, allowing you to deliver translation updates without needing to rebuild or republish your app to Google Play or other stores. This is useful for minor adjustments like fixing typos.

    To use OTA, go to the Download page and select Android SDK under the Mobile SDK section from the dropdown menu:

    Use Android SDK for OTA

    Scroll down and click Build only to generate an OTA bundle. You can manage the bundle by going to More > Settings > OTA bundles. In the OTA bundles section, switch to the Android SDK tab and view your bundle. Optionally, rename it.

    Manage Android OTA bundles

    You can learn more about OTA in Lokalise’s Developer Hub.

    Step 4: Integrate the Lokalise SDK

    To integrate the Lokalise SDK into your app, follow these steps:

    • Add Maven to your settings.gradle file:
    dependencyResolutionManagement {
        repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
        repositories {
            google()
            mavenCentral()
            maven { url 'https://maven.lokalise.com' }
        }
    }
    • Add the Lokalise SDK dependency to build.gradle. It supports both Kotlin-based and Java mobile apps:
    dependencies {
    
        // other dependencies...
    
        implementation('com.lokalise.android:sdk:2.1.1') {
            transitive = true
        }
    }
    • Initialize the Lokalise SDK in your app. Create a new MyApplication.kt file:
    package com.example.lokalisei18n
    import android.app.Application
    import com.lokalise.sdk.Lokalise
    
    class MyApplication : Application() {
        override fun onCreate() {
            super.onCreate()
            Lokalise.init(this, "YOUR_TOKEN", "YOUR_PROJECT_ID")  // 1
            Lokalise.updateTranslations()  // 2
        }
    }

    Make sure to replace "YOUR_TOKEN" and "YOUR_PROJECT_ID" with the values from your Lokalise project.

    To get these values, head over to your Lokalise project’s dashboard and click More > Settings. Here, take note of the Project ID, click Generate new token, and note down the resulting token value as well:

    Grab project ID and OTA token

    • Update your AndroidManifest.xml to use MyApplication (note the android:name=".MyApplication" part):
    <application
        android:allowBackup="true"
        android:name=".MyApplication"
        ... >
        <activity android:name=".MainActivity" ... >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
    • In MainActivity.kt, modify the attachBaseContext method to wrap the context with Lokalise:
    override fun attachBaseContext(newBase: Context) {
        super.attachBaseContext(LokaliseContextWrapper.wrap(newBase))
    }

    Step 5: Test the OTA updates

    Now, run your app in the emulator and check that the text reflects the updated translations. You can easily make more changes in the Lokalise editor and generate new OTA bundles. When ready, set the bundle as production and save the changes.

    Test Android localization and OTA

    Of course, you can perform additional changes in the Lokalise editor and generate a new Android SDK bundle. However, in this case make sure to set this new bundle as production and click Save changes:

    Generate more OTA bundles as needed

    You can learn more about Android SDK and OTA bundles in our Developer Hub. I would also recommend checking the best practices that will help you optimize OTA bundle sizes.

    Conclusion

    In this tutorial, we covered how to localize an Android app for multiple locales. We tackled some of the challenges posed by API deprecations that make Android localization trickier, and we learned how to switch between languages—whether by changing the device’s language or programmatically within the app using a modified Context.

    We also explored how Lokalise streamlines the process by delivering localizations over the air (OTA) without the need to republish mobile apps into Google Play. Along the way, we learned how to pluralize nouns with Android’s quantity strings and localize date and time formats.

    With these tools and techniques, you’re well-equipped to handle Android app localization. Until next time—happy coding!

    Further reading

    Related articles
    Stop wasting time with manual localization tasks. 

    Launch global products days from now.