18
Localization

Knowing CriminalIntent is going to be a wildly popular app, you have decided to make it accessible to a larger audience. Your first step is to localize all of the user-facing text so your app can be read in Spanish or English.

Localization is the process of providing the appropriate resources for your app based on the user’s language setting. In this chapter you will provide a Spanish version of strings.xml. When a device’s language is set to Spanish, Android will automatically find and use the Spanish strings at runtime (Figure 18.1).

Figure 18.1  IntentoCriminal

Screenshot shows the IntentoCriminal app in Android. The text in the app is shown in the Spanish language.

Localizing Resources

Language settings are part of the device’s configuration. (See the section called Device configurations and alternative resources in Chapter 3 for an overview of device configuration.) Android provides qualifiers for different languages, just as it does for screen orientation, screen size, and other configuration factors. This makes localization straightforward: You create resource subdirectories with the desired language configuration qualifier and put the alternative resources in them. The Android resource system does the rest.

In your CriminalIntent project, create a new values resource file (as you did in Chapter 17): In the project tool window, right-click res/values/ and select NewValues resource file. Enter strings for the File name. Leave the Source set option set to main. Make sure Directory name is set to values. Select Locale in the Available qualifiers list and click the >> button to move Locale to the Chosen qualifiers section. Select es: Spanish in the Language list. Any Region will be automatically selected in the Specific Region Only list – which is just what you want, so leave that selection be.

The resulting New Resource File window should look similar to Figure 18.2.

Figure 18.2  Adding a qualified strings resource file

Screenshot shows New Resource File window.

Note that Android Studio automatically changes the Directory name field to values-es. The language configuration qualifiers are taken from ISO 639-1 codes, and each consists of two characters. For Spanish, the qualifier is -es.

Click OK. The new strings.xml file will be listed under res/values, with (es) after its name. The strings files are grouped together in the project tool window’s Android view (Figure 18.3).

Figure 18.3  Viewing new strings.xml in Android view

Screenshot shows new strings.xml in Android view with strings.xml(es) selected in the menu.

However, if you explore the directory structure, you will see that your project now contains an additional values directory: res/values-es. The newly generated strings.xml is located inside of this new directory (Figure 18.4).

Figure 18.4  Viewing new strings.xml in Project view

Screenshot shows Project window displaying new strings.xml in Project view.

Now it is time to make the magic happen. Add Spanish versions of all your strings to res/values-es/strings.xml. (If you do not wish to type these strings in, copy the contents from the solution file. You can find the URL in the section called Adding an Icon in Chapter 2.)

Listing 18.1  Adding Spanish alternative of string resources (res/values-es/strings.xml)

<resources>
    <string name="app_name">IntentoCriminal</string>
    <string name="crime_title_hint">Introduzca un título para el crimen.</string>
    <string name="crime_title_label">Título</string>
    <string name="crime_details_label">Detalles</string>
    <string name="crime_solved_label">Solucionado</string>
    <string name="date_picker_title">Fecha del crimen:</string>
    <string name="new_crime">Crimen Nuevo</string>
    <string name="show_subtitle">Mostrar Subtítulos</string>
    <string name="hide_subtitle">Esconder Subtítulos</string>
    <string name="subtitle_format">%1$s crímenes</string>
    <string name="crime_suspect_text">Elegir Sospechoso</string>
    <string name="crime_report_text">Enviar el Informe del Crimen</string>
    <string name="crime_report">%1$s!
        El crimen fue descubierto el %2$s. %3$s, y %4$s
    </string>
    <string name="crime_report_solved">El caso está resuelto</string>
    <string name="crime_report_unsolved">El caso no está resuelto</string>
    <string name="crime_report_no_suspect">no hay sospechoso.</string>
    <string name="crime_report_suspect">el/la sospechoso/a es %s.</string>
    <string name="crime_report_subject">IntentoCriminal Informe del Crimen</string>
    <string name="send_report">Enviar el informe del crimen a través de</string>
</resources>

That is all you have to do to provide localized string resources for your app. To confirm, change your device’s settings to Spanish by opening Settings and finding the language settings. Depending on your version of Android, these settings will be labeled Language and input, Language and Keyboard, or something similar.

When you get to a list of language options, choose a setting for Español. The region (España or Estados Unidos) will not matter, because the qualification -es matches both. (Note that on newer versions of Android, users can select multiple languages and assign a priority order. If you are on a newer device, make sure Español appears first in your language settings list.)

Run CriminalIntent and bask in the glory of your newly localized app. When you are done basking, return your device’s language setting to English. Look for Ajustes or Configuración (Settings) in the launcher and find the setting that includes Idioma (Language).

Default resources

The configuration qualifier for English is -en. In a fit of localization, you might think to rename your existing values directory to values-en/. This is not a good idea, but pretend for a moment you did just that: Your hypothetical update means your app now has an English strings.xml in values-en and a Spanish strings.xml in values-es.

Your newly updated app will build just fine. It will also run just fine on devices with the language set to Spanish or English. But what happens if the user’s device language is set to Italian? Bad things. Very bad things. At runtime Android will not find string resources that match the current configuration. The severity of the resulting bad behavior depends on where the resource ID for a given string is being referenced.

If an unmatched string resource is referenced in your XML layout file, the app will display the resource ID of the resource (rather than a meaningful string you have defined). To see this behavior in action, comment out the crime_title_label entry from values/strings.xml. (You can easily comment out a single line by clicking on the line and then pressing Command+/ [Ctrl+/].)

Listing 18.2  Commenting out English crime_title_label (res/values/strings.xml)

<resources>
    <string name="app_name">IntentoCriminal</string>
    <string name="crime_title_hint">Introduzca un título para el crimen.</string>
    <!--<string name="crime_title_label">Title</string>-->
    ...

(Recall that crime_title_label is referenced in fragment_crime.xml:)

<TextView
    style="?android:listSeparatorTextViewStyle"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@string/crime_title_label" />

Run CriminalIntent on a device with the language set to English. The app displays the resource ID for crime_title_label instead of the TITLE text (Figure 18.5).

Figure 18.5  Referencing missing English version of title string from XML

Screenshot shows the CriminalIntent app in Android.

Even worse, if an unmatched string resource is referenced in your Java code, the app will crash. To see this in action, comment out crime_report_subject in the English values/strings.xml.

Listing 18.3  Commenting out English crime_report_subject (res/values/strings.xml)

<string name="crime_report_no_suspect">there is no suspect.</string>
<string name="crime_report_suspect">the suspect is %s.</string>
<!--<string name="crime_report_subject">CriminalIntent Crime Report</string>-->

(Recall that crime_report_subject is referenced by the crime report button’s click listener in CrimeFragment.java:)

mReportButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                Intent i = new Intent(Intent.ACTION_SEND);
                i.setType("text/plain");
                i.putExtra(Intent.EXTRA_TEXT, getCrimeReport());
                i.putExtra(Intent.EXTRA_SUBJECT,
                        getString(R.string.crime_report_subject));
                i = Intent.createChooser(i, getString(R.string.send_report));
                startActivity(i);
            }
        });

Run the app and press SEND CRIME REPORT to see the crash (Figure 18.6).

Figure 18.6  Missing English version of subject string

Screenshot shows crash report of the application on an Android phone. Text reads, CriminalIntent has stopped. Open app again Mute until device restarts.

The moral of the story is this: Provide a default resource for each of your resources. Resources in unqualified resource directories are your default resources. Default resources will be used if no match for the current device configuration is found. Your app will misbehave if Android looks for a resource and cannot find one that matches the device configuration.

(Leave crime_title_label and crime_report_subject commented out for now. You will be reminded to uncomment both in a bit.)

Screen density works differently

The exception to providing default resources is for screen density. A project’s drawable directories are typically qualified for screen density with -mdpi, -xxhdpi, etc., as you have seen. However, Android’s decision about which drawable resource to use is not a simple matter of matching the device’s screen density or defaulting to an unqualified directory if there is no match.

The choice is based on a combination of screen size and density, and Android may choose a drawable from a directory that is qualified with a lower or higher density than the device and then scale the drawable. There are more details in the docs at developer.android.com/​guide/​practices/​screens_support.xhtml, but the important point is that putting default drawable resources in res/drawable/ is not necessary.

Checking string coverage using Translations Editor

As the number of languages you support grows, making sure you provide a version of each string for each language becomes more difficult. Luckily, Android Studio provides a handy Translations Editor to see all of your translations in one place. To launch the Translations Editor, right-click on one of the strings.xml files in the project tool window and select Open Translations Editor. The Translations Editor displays all of the app’s strings and the translation status for each of the languages your app currently provides any qualified string values for. Since crime_title_label and crime_report_subject are commented out, you will see those field names in red (Figure 18.7).

Figure 18.7  Using the Translations Editor to check your string coverage

Screenshot shows Translations editor.

(Now it is time to uncomment both crime_title_label and crime_report_subject before moving on. You can uncomment out a single line by clicking on the line and then pressing Command+/ [Ctrl+/] again.)

Targeting a region

You can qualify a resource directory with a language-plus-region qualifier that targets resources even more specifically. For instance, the qualifier for Spanish spoken in Spain is -es-rES, where the r denotes a region qualifier and ES is the ISO 3166-1-alpha-2 code for Spain. Configuration qualifiers are not case sensitive, but it is good to follow Android’s convention here: Use a lowercase language code and an uppercase region code prefixed with a lowercase r.

Note that a language-region qualifier, such as -es-rES, may look like two distinct configuration qualifiers that have been combined, but it is just one. The region is not a valid qualifier on its own.

Figure 18.8 shows the locale resource resolution strategy and how it differs based on the device’s Android version.

Figure 18.8  Locale resolution (pre- and post-Nougat)

Figure shows Locale resolution (pre- and post-Nougat).

A resource qualified with both a locale and region has two opportunities for matching a user’s locale. An exact match occurs when both the language and region qualifiers match the user’s locale. If no exact match is found, the system will strip off the region qualifier and look for an exact match for the language only.

On devices running pre-Nougat versions of Android, if no language match is found, the default (unqualified resource) is used. Nougat has enhanced locale support, with more locales and the ability to select more than one locale in the device’s settings. The system also uses a more intelligent resource resolution strategy for locale with the aim of showing the correct language as often as possible, even if the app does not provide an exact region match or a nonqualified language match. If no exact match is found on a device running Nougat, and no language-only match is found, the system will look for a resource qualified with the same language but a different region and will use the best match of resources that meet those criteria.

Consider an example. Suppose you set the language on your device to Spanish and your region to Chile (Figure 18.9). An app on your device contains Spanish strings.xml files tailored for Spain and Mexico (in values-es-rES and values-es-rMX). The default values directory contains an English strings.xml. If your device is running pre-Nougat Android, you will see the English contents of the default values directory. But if your device is running Nougat, you will have a better experience: You will see the contents of values-es-rMX/strings.xml – which means you will see Spanish, though not tailored to Chile.

Figure 18.9  Locale resolution example (pre- and post-Nougat)

Figure shows Locale resolution (pre- and post-Nougat).

This example is a bit contrived. But it highlights an important point: Provide strings in as general a context as possible, using language-only qualified directories as much as possible and region-qualified directories only when necessary. Rather than maintaining all Spanish user-facing strings in three region-qualified directories, the example app above would be better off storing the Spanish strings in a language-only qualified values-es directory and providing region-qualified strings only for words and phrases that are different in the different regional dialects. This not only makes maintaining the strings files easier for the programmer, but it also helps the system resolve the resources on both pre-Nougat devices and Nougat devices by providing a language-only match.

Testing custom locales

Different devices and different versions of Android know about and support different locales. It is possible, then, that you may want to provide strings or other resources for a locale that is not available on your test device. If this is the case, not to worry. You can use the Custom Locale tool on the emulator to create and apply a locale that is not actually supported by the system image. The emulator will then simulate a runtime configuration that includes that language/region combination, allowing you to test how your app behaves in that configuration.

The emulator comes packaged with the Custom Locale tool. On your emulator, open the App Launcher screen. Click on the icon labeled Custom Locale. Once launched, the Custom Locale tool allows you to browse existing locales, add new custom locales, and apply a custom locale for testing (Figure 18.10).

Figure 18.10  Custom Locale tool

Screenshot shows CustomLocale screen in Android. A text field above shows the Current Locale. Below is a Locale List displaying the available locale languages.

Note that if you apply a custom locale that is not supported by the system image, the system UI will still display the default language. Your app, however, will resolve resources based on the custom locale you chose.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.159.150