Android Localization: Using Resource Qualifiers for Locales (values-es, values-fr) in Kotlin

In Android development, supporting multiple languages is crucial for reaching a global audience. Resource qualifiers allow you to provide localized resources (strings, layouts, drawables, etc.) based on the device’s locale. By using resource qualifiers like values-es for Spanish and values-fr for French, you can ensure that your app displays content in the user’s preferred language. This article explores how to effectively use resource qualifiers for locales in Kotlin XML development for Android.

Understanding Resource Qualifiers for Locales

Resource qualifiers are suffixes added to resource directory names (e.g., values, layout, drawable) that indicate the configuration for which the resources in that directory should be used. Locale qualifiers specify the language and optionally the region (country) of the user. For instance, values-es is for Spanish, and values-fr-CA is for French as spoken in Canada.

Why Use Locale-Specific Resource Qualifiers?

  • Enhanced User Experience: Users feel more comfortable using apps in their native language.
  • Global Reach: Makes your app accessible to a broader audience.
  • Compliance: Meets the requirements of international markets.
  • Improved App Ratings: Satisfied users are more likely to leave positive reviews.

How to Implement Resource Qualifiers for Locales in Android Kotlin Projects

To implement locale-specific resources, follow these steps:

Step 1: Create Resource Directories

Create directories under the res directory with the appropriate locale qualifier:

  • values-es: For Spanish strings
  • values-fr: For French strings
  • values-de: For German strings
  • layout-es: For Spanish-specific layouts
  • drawable-fr: For French-specific drawables

Example directory structure:


  app/
  └── src/
      └── main/
          └── res/
              ├── values/       # Default (English)
              │   └── strings.xml
              ├── values-es/    # Spanish
              │   └── strings.xml
              ├── values-fr/    # French
              │   └── strings.xml
              └── layout/       # Default layout
                  └── activity_main.xml

Step 2: Define Localized Resources

In each values directory, create a strings.xml file (or other resource file) with the localized values:

res/values/strings.xml (Default – English)

<resources>
    <string name="app_name">My App</string>
    <string name="greeting">Hello!</string>
    <string name="welcome_message">Welcome to My App</string>
</resources>
res/values-es/strings.xml (Spanish)

<resources>
    <string name="app_name">Mi Aplicación</string>
    <string name="greeting">¡Hola!</string>
    <string name="welcome_message">Bienvenido a Mi Aplicación</string>
</resources>
res/values-fr/strings.xml (French)

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

Step 3: Use Resources in Layouts and Kotlin Code

Reference the localized resources in your layouts and Kotlin code using the @string resource ID:

res/layout/activity_main.xml

<TextView
    android:id="@+id/greetingTextView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/greeting" />

<TextView
    android:id="@+id/welcomeTextView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/welcome_message" />
Kotlin Code Example

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextView

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

        val greetingTextView: TextView = findViewById(R.id.greetingTextView)
        val welcomeTextView: TextView = findViewById(R.id.welcomeTextView)

        greetingTextView.text = getString(R.string.greeting)
        welcomeTextView.text = getString(R.string.welcome_message)
    }
}

Step 4: Handle Locale Changes Programmatically (Optional)

If you need to change the locale programmatically (e.g., through a language settings option in your app), use the following approach:


import android.content.Context
import android.content.res.Configuration
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import java.util.Locale

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

        val spanishButton: Button = findViewById(R.id.spanishButton)
        val frenchButton: Button = findViewById(R.id.frenchButton)

        spanishButton.setOnClickListener {
            setLocale("es")
        }

        frenchButton.setOnClickListener {
            setLocale("fr")
        }
    }

    private fun setLocale(languageCode: String) {
        val locale = Locale(languageCode)
        Locale.setDefault(locale)

        val resources = resources
        val configuration = resources.configuration
        configuration.setLocale(locale)

        baseContext.createConfigurationContext(configuration)
        resources.updateConfiguration(configuration, resources.displayMetrics)

        recreate() // Recreate the activity to apply the new locale
    }
}
Layout for Locale Change Buttons

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="16dp">

    <TextView
        android:id="@+id/greetingTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/greeting" />

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

    <Button
        android:id="@+id/spanishButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Spanish" />

    <Button
        android:id="@+id/frenchButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="French" />
</LinearLayout>

Ensure that the necessary IDs are present in your layout file (e.g., spanishButton, frenchButton).

Advanced Usage and Considerations

  • Region-Specific Translations:
    For specific regions like French in Canada (values-fr-CA), provide tailored translations.
  • Plurals:
    Use <plurals> for handling different plural forms in different languages.
  • Date and Number Formatting:
    Format dates and numbers according to the device’s locale settings using java.text.DateFormat and java.text.NumberFormat.

Testing Localization

  • Android Studio: Use the Android Studio locale emulator to test different locales.
  • Device Settings: Change the language settings on your test device to verify the localization.
  • Crowdsourced Translation: Consider using crowdsourced translation platforms to ensure high-quality translations.

Conclusion

Leveraging resource qualifiers for locales in Kotlin XML development is essential for creating apps that cater to a global audience. By providing localized resources, you enhance the user experience, improve your app’s reach, and ensure compliance with international standards. Properly implementing locale-specific resources can lead to increased user satisfaction and higher app ratings. Make sure to test your localization thoroughly to deliver a polished and culturally sensitive product.