Creating Multi-language Apps with XML Layouts

In today’s globalized world, creating applications that cater to a diverse user base is more critical than ever. One of the key aspects of achieving this is through internationalization (i18n) and localization (l10n). When building Android apps with XML layouts, handling multiple languages efficiently is crucial. This blog post explores the best practices for creating multi-language applications using XML layouts in Android.

Understanding Internationalization (i18n) and Localization (l10n)

  • Internationalization (i18n): Designing and developing an application in a way that it can be adapted to various languages and regions without engineering changes. It focuses on making the app adaptable.
  • Localization (l10n): Adapting an internationalized application for a specific language or region by adding locale-specific components and translating text. It focuses on tailoring the app for a particular market.

Why Support Multiple Languages?

  • Wider Audience: Reach a larger audience by supporting different languages.
  • Improved User Experience: Users prefer applications in their native language.
  • Increased Engagement: Localization can lead to higher user engagement and satisfaction.
  • Competitive Advantage: Distinguish your app in the global market.

How to Implement Multi-Language Support in Android Using XML Layouts

Implementing multi-language support in Android using XML layouts involves the following steps:

Step 1: Organizing Resources for Localization

Android uses resource folders to manage different languages. The resources are organized under the res directory. To add support for a new language, you need to create a new values directory for each language. The directory name follows the format values-xx, where xx is the ISO 639-1 two-letter language code. For example, values-es for Spanish, values-fr for French, and so on.


res/
    values/
        strings.xml
    values-es/
        strings.xml
    values-fr/
        strings.xml

Step 2: Creating String Resource Files

In each values-xx directory, create a strings.xml file that contains the translations for that specific language. Make sure that the string names in each file are identical.

Default strings.xml (English):


<resources>
    <string name="app_name">My Application</string>
    <string name="hello_world">Hello, World!</string>
    <string name="welcome_message">Welcome to My Application</string>
</resources>

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


<resources>
    <string name="app_name">Mi Aplicación</string>
    <string name="hello_world">¡Hola Mundo!</string>
    <string name="welcome_message">Bienvenido a Mi Aplicación</string>
</resources>

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


<resources>
    <string name="app_name">Mon Application</string>
    <string name="hello_world">Bonjour le monde!</string>
    <string name="welcome_message">Bienvenue dans Mon Application</string>
</resources>

Step 3: Referencing Strings in XML Layouts

In your XML layouts, reference the string resources using the @string/string_name syntax. Android automatically loads the appropriate string resource based on the device’s current locale.


<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/hello_world" />
<Button
    android:id="@+id/myButton"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/welcome_message" />

Step 4: Updating Strings in Code

If you need to update strings programmatically, you can use the getString() method from the Resources class. This method retrieves the localized string resource based on the current locale.


val welcomeMessage = resources.getString(R.string.welcome_message)
myTextView.text = welcomeMessage

Step 5: Handling Locale Changes at Runtime

Android allows users to change the device’s locale at runtime. To handle locale changes gracefully, you can override the onConfigurationChanged() method in your activities. This method is called whenever the device configuration changes, including locale changes.


import android.content.res.Configuration
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    override fun onConfigurationChanged(newConfig: Configuration) {
        super.onConfigurationChanged(newConfig)
        // Update UI components with new locale-specific strings
        updateUI()
    }

    private fun updateUI() {
        // Example: Updating a TextView with the new locale's string
        val helloTextView = findViewById<TextView>(R.id.helloTextView)
        helloTextView.text = resources.getString(R.string.hello_world)
    }
}

Add this to your activity in AndroidManifest.xml to listen for config changes


<activity
    android:name=".MainActivity"
    android:configChanges="locale|layoutDirection">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

Step 6: Using the LocaleListCompat for API 24+

For API 24+, use LocaleListCompat for managing locales.


import android.content.Context
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import androidx.core.os.LocaleListCompat
import java.util.Locale

class BaseActivity : AppCompatActivity() {

    override fun attachBaseContext(newBase: Context) {
        val localeList = LocaleListCompat.getDefault()
        val context: Context = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            val configuration = newBase.resources.configuration
            configuration.setLocales(localeList)
            newBase.createConfigurationContext(configuration)
        } else {
            // Before API 24, directly update the Locale
            val locale = localeList.get(0) ?: Locale.getDefault()
            Locale.setDefault(locale)
            val configuration = newBase.resources.configuration
            configuration.locale = locale
            newBase.createConfigurationContext(configuration)
        }
        super.attachBaseContext(context)
    }
}

Advanced Localization Techniques

Beyond simple string translation, consider these advanced techniques for richer localization:

  • Plurals: Handle grammatical number agreement for different quantities (e.g., “1 item,” “2 items”).
  • Date/Time Formatting: Use SimpleDateFormat or java.time (API 26+) to format dates and times based on the locale.
  • Currency Formatting: Display monetary values correctly using NumberFormat.getCurrencyInstance().
  • Right-to-Left (RTL) Layouts: Support languages like Arabic and Hebrew by enabling RTL layout mirroring.
  • Image Localization: Provide locale-specific images if visuals vary across cultures.

<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <plurals name="numberOfItems">
        <item quantity="one">You have <xliff:g id="count">%d</xliff:g> item.</item>
        <item quantity="other">You have <xliff:g id="count">%d</xliff:g> items.</item>
    </plurals>
</resources>

val itemCount = 5
val formattedString = resources.getQuantityString(R.plurals.numberOfItems, itemCount, itemCount)
myTextView.text = formattedString

Best Practices for Creating Multi-Language Apps

  • Externalize All Strings: Avoid hardcoding strings directly in your code or XML layouts.
  • Use Consistent String Names: Keep the string names consistent across all strings.xml files.
  • Provide a Default Language: Always include a values directory with a strings.xml file containing the default language (usually English).
  • Test Thoroughly: Test your application with different locales to ensure that all text is displayed correctly.
  • Use Tools: Leverage localization management tools and services for more complex projects.

Conclusion

Creating multi-language applications is essential for reaching a global audience and enhancing the user experience. By properly organizing resources, externalizing strings, and handling locale changes, you can create an Android application that supports multiple languages effectively. Remember to follow best practices and thoroughly test your application to ensure a seamless experience for all users, regardless of their language preference.