Chapter 20. All the World’s Not English: Strings and Internationalization

“All the world’s a stage,” wrote William Shakespeare. But not all the players on that great and turbulent stage speak the great Bard’s native tongue. To be usable on a global scale, your software needs to communicate in many different languages. The menu labels, button strings, dialog messages, title bar/action bar titles, and error messages must be settable to the user’s choice of language. This is the topic of internationalization and localization. (Because the words “internationalization” and “localization” take a long time to say and write, they’re often abbreviated using their first and last letters and the count of omitted letters: I18N and L10N.)

If you’ve got your strings in a separate XML file, as we advised in Chapter 1, you have already done part of the work of internationalizing your app. Aren’t you glad you followed our advice?

Android provides a Locale class to discover/control the internationalization settings. A default Locale is inherited from the user’s language settings when your app starts up. As a best practice, your app should never ask the users to choose a language, because they’ll already have chosen one when setting up the device.

Note that if you know internationalization from desktop Java, it’s pretty much the same here. We’ll explain as we go along, with examples, in this chapter.

Ian’s basic steps: Internationalization

Internationalization and localization consist of:

Sensitivity training (internationalization or I18N)

Making your software sensitive to the issues introduced in the first paragraph of this recipe.

Language lessons (localization or L10N)

Writing text mapping files for each language.

Culture lessons (optional)

Customizing the presentation of numbers, fractions, dates, and message formatting. Images and colors, for example, can mean different things in different cultures.

This chapter’s recipes provide examples of doing all three.

See also

Wikipedia has a good article on internalization and localization. See also Java Internationalization by Andy Deitsch and David Czarnecki (O’Reilly).

Microsoft’s The GUI Guide: International Terminology for the Windows Interface was, despite the title, less about UI design than about internationalization; it came with a 3.5-inch floppy disk holding suggested translations of common Microsoft Windows GUI element names into a dozen or so common languages. This book is rather dated today, but it might be a start for translating simple texts into some common languages. It can often be found on the usual used-book websites.

20.1 Internationalizing Application Text

Ian Darwin

Problem

You want the text of your buttons, labels, and so on to appear in the user’s chosen language.

Solution

Create or update the file strings.xml in the res/values subdirectory of your application. Translate the string values into the given language.

Discussion

Every Android project created with the SDK has a file called strings.xml in the res/values directory. This is where you are advised to place your application’s strings, from the application title through to the button text and even down to the contents of dialogs. You can refer to a string by name in the following two ways:

  • By a reference in a layout file, to apply the correct version of the string directly to a GUI component; for example, android:text="@string/hello"

  • If you need the value in Java code, by using a lookup such as getString(R.string.hello) to look up the string’s value from the file

To make all of these strings available in a different language, you need to know the correct ISO-639 language code; a few common ones are shown in Table 20-1.

Table 20-1. Common languages and codes
Language Code

Chinese (traditional)

cn-tw

Chinese (simplified)

cn-zh

English

en

French

fr

German

de

Italian

it

Japanese

jp

Spanish

es

With this information, you can create a new subdirectory, res/values-LL (where LL is replaced by the ISO language code). In this directory you create a copy of strings.xml, and in it you translate the individual string values (but not the names). For example, a simple application might have the following in strings.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">MyAndroid</string>
    <string name="hello">Hello Android</string>
</resources>

You might create res/values-es/strings.xml containing the following Spanish text (see Figure 20-1):

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">MiAndroid</string>
    <string name="hello">Hola Android</string>
</resources>

You might also create the file res/values-fr/strings.xml containing the following French text:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">Bonjour Android</string>
    <string name="app_name">MonAndroid</string>
</resources>

Note that the order of entries within this file does not matter.

Now when you look up the string "hello" using either of the methods described earlier, you will get the version based on the user’s language choice. If the user selects a language that you don’t have a L10N file for, the app will still work, but it will get the value from the default file—the one in the values directory with no language code.

This lookup is done per string, so if there is a string that’s not defined in a language-specific file, the app will find the version of it in the default strings.xml file.

Is it really that simple?

Yes. Just package your application and deploy it as usual. Go into the Settings app of your emulator or device, choose Language, select French or Spanish, and the program title and window contents should reflect the change (Figure 20-1).

ack2 2001
Figure 20-1. Hello app in Spanish

You just have to remember to keep all the versions of strings.xml in sync with the “master” copy.

Regional variants

OK, so it’s not quite that simple. There are also regional variations within a language. In English there are, for example, UK English (a.k.a. “the real thing” to some, or “the Queen’s/King’s English”), US English, Canadian, Australian, and so on. These, fortunately, have tended to use the same vocabulary for technical terms, so using the regional variations is not as important for English. On the other hand, French and Spanish, to name two that I am familiar with, are languages where there is significant variation in vocabulary from one region to another. Parisian French and Canadian French have used different vocabularies for many words coined since the 1500s, when the exodus to Canada began. The many Spanish colonies were likewise largely isolated from hearing and reading one another’s words for hundreds of years—from the time of their founding until the age of radio—and they have diverged even more than French. So you may want to create “variant” files for these languages, as for any other that has significant regional variation.

Android’s practice here diverges slightly from Java’s, in that Android uses a letter r to denote regional variations; for example, you’d create a directory named values-fr-rCA for Canadian French. Note that, as in Java, language codes are in lowercase and variations (which are usually the two-letter ISO 3166-1 alpha-2 country codes) are written in capital letters (except for the leading r). So, for example, we might wind up with the set of files listed in Table 20-2.

Table 20-2. L10N directory examples
Directory Meaning

values

English - default

values-es

Spanish - Castilian, generic

values-es-rCU

Spanish - Cuban

values-es-rCL

Spanish - Chilean

values-fr

French - generic

values-fr-rCA

French - Canadian

See Also

There is a bit more detail in the official Android localization documentation.

20.2 Finding and Translating Strings

Ian Darwin

Problem

You need to find all the strings in your application, internationalize them, and translate them.

Solution

Use one of the several good tools for finding string literals, as well as collaborative and commercial services that translate text files.

Discussion

Suppose you have a mix of old and new Java code in your app; the new code was written specifically for Android, while the older code may have been used in some other Java environment. You need to find every string literal, isolate them into a Strings.xml file, and translate it into any necessary languages.

Current versions of Android Studio and Eclipse plug-ins will warn about strings that are not internationalized, when you use them inside your app. Android Lint (see Recipe 3.13) will do a stronger job of this.

The Android Localizer from ArtfulBits is a free and open source tool that you can use to handle both finding and translating strings.

Imagine a slightly different scenario: suppose your organization has a “native” (Objective-C) application from iOS and you are building the “native” Java version for Android. Here, the properties files are in very different formats—on iOS there is a Java properties-like file, but with the default (probably English) strings on the left and the translations on the right. No names are used, just the actual strings, so you might find something like the following:

You-not us-are responsible=You-not us-are responsible

You cannot translate this directly into XML, since the “name” is used as an identifier in the generated R (Resources) class, and the hyphen (-) and straight quotes (") characters are not valid in Java identifiers. Doing it manually, you might come up with something like this:

<string name="you_not_us_are_responsible">You-not us-are responsible</string>

Stack Overflow user johnthuss has developed a version of a Java program that performs such translations from iOS to Android format, handling characters that are not valid identifiers.

Now you are ready to begin translating your master resource file into other languages. While it may be tempting to scrimp on this part of the work, it is generally worthwhile to engage the services of a professional translation service skilled in the particular language(s) you target. Google offers an outsourced translation service through the Google Play Developer Console. Alternatively, you may wish to investigate the commercial collaborative translation service Crowdin.

When using any third-party translation service, especially for languages with which you or your staff are not personally “first or second childhood language” familiar, you should get a second opinion. Embarrassing errors in software shipped with “bad” translations can be very expensive.

A quick web search will find many commercial services that will perform translations for you, as well as some that can help with the internationalization part of the work.

20.3 Handling the Nuances of strings.xml

Daniel Fowler

Problem

On most occasions, entering text in the strings.xml file is easy enough, but sometimes peculiar results crop up.

Solution

Understanding how some text strings and characters work in strings.xml will prevent strange results.

Discussion

When some text is required on a screen, it can be declared in a layout file, as shown in the following android:text attribute:

<TextView android:id="@+id/textview1"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="This is text"/>

The text can also be set in code:

TextView tview = (TextView) findViewById(R.id.textview1);
tview.setText("This is text");

However, hardcoding strings like this is not recommended, because it reduces maintainability. Changing text at a later date may mean hunting down declarations across several Java source files and layout files. Instead, text in a project can be centralized into a strings.xml file. The file is located in the project’s res/values folder. Centralizing text means that, if you need to change it, you only need to do so in one place. It also makes localization much easier (see Recipe 20.1}. Here is an example of a strings.xml file:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">Strings XML</string>
    <string name="text1">This is text</string>
    <string name="text2">And so is this</string>
</resources>

To access the declared string from another project’s XML file, use @string/<string_name>. Using the preceding example, the text for two TextViews is set with the following layout XML file:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent">
  <TextView android:id="@+id/textview1"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="@string/text1"
            android:textSize="16dp"/>
  <TextView android:id="@+id/textview2"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="@string/text2"
            android:textSize="16dp"/>
</LinearLayout>

When the strings.xml file is saved in the IDE, the R.string class is generated (see R.java in the generated sources folder for the project). This provides a static int that can be used to reference the string in code:

tview = (TextView) findViewById(R.id.textview1);
    tview.setText(R.string.text1);

The R class should never be edited, because it is generated by the SDK and any changes you do make will be overwritten. In Android Studio the text attribute of a textual View object can be accessed via the Properties pane. The ellipsis button to the right of the name field allows an existing resource to be chosen or a new one generated (see Figure 20-2).

ack2 20in01
Figure 20-2. Assigning a string resource to the Text property of a View

In the strings.xml file, an entry can duplicate another string by referencing it the same way as a layout file:

<string name="text1">This is text</string>
<string name="text2">@string/text1</string>

This results in:

ack2 20in02

Since @ is used to indicate another string resource, trying to set the text to a single @ using <string name="text1">@</string> will not work. Nor will text that starts with an @, such as <string name="text2">@mytwittername</string>:

ack2 20in03

The first @ needs to be escaped with a (backslash), as in @ and @mytwittername:

ack2 20in04

If the @ does not start a string or is being set in code, it does not need to be escaped; you can use android:text=Twitter:@mytwittername or tview.setText("@mytwittername");, for example. This problem of @ as the first character, or only character, also applies to the ? (question mark). If it appears at the start of a string, it also needs escaping, as in android:text=?. An alternative to escaping the @ or ? is to use quotes (speech marks); the closing quote mark is optional:

<string name="text1">"@"</string>
<string name="text2">"?"</string>

In fact, any number of quotes and any whitespace before and after the text will be dropped. The two lines in the preceding code snippet produce an identical result to these two lines:

<string name="text1">""""""""""@"""""""</string>
<string name="text2">         "?"      </string>

There is, however, a character for which this approach will not work:

<string name="text1">War & Peace</string>
<string name="text2">War and Peace</string>

The first line will result in an error because of the &. This is because of the XML file format itself. XML requires balanced pairs of tags—for example, <string> and </string>—and each start tag and end tag is enclosed in opening (<) and closing (>) angle brackets. Once a start tag is encountered, the editor is on the lookout for the opening bracket of the end tag. This produces a problem if the content of the XML tags contains the open angle bracket itself:

<string name="question">Is 5 < 6?</string>

This will not work. The solution is to use an XML internal entity; this is similar to using an escape character but is in a specific format for XML. The format is an ampersand, &, followed by the entity name and then a semicolon. For the open angle bracket, or less-than symbol, the name is lt, and therefore the full entity is &lt; as in:

<string name="question">Is 5 &lt; 6?</string>

Depending on what is required in an XML file at a particular point, there are five internal entities defined for XML that can be used, as this table shows:

Entity Name Usage

The left angle bracket (<)

lt

&lt;

The right angle bracket (>)

gt

&gt;

The ampersand (&)

amp

&amp;

The single quote or apostrophe (')

apos

&apos;

The double quote (")

quot

&quot;

Now we can see why the ampersand causes us a problem. It is used to define an internal entity, and thus when one is required, the amp entity itself must be used. Therefore, <string name="text1">War & Peace</string> becomes <string name="text1">War & Peace</string>:

ack2 20in05

However, the XML internal entity apos, while valid for XML, is reported as an error when the file is saved:

<string name="text1">This isn't working</string>
<string name="text2">This isn&apos;t working either</string>

This is another character that requires escaping or wrapping in quotes:

<string name="text1">This'll work</string>
<string name="text2">"This'll work as well"</string>

To use quotes (speech marks) themselves—even the XML internal entity version—escape them:

<string name="text1">Quote: "to be, or not to be"</string>
<string name="text2">Quote: &quot;to be, or not to be&quot;</string>

Either form will work, so these two lines display identically:

ack2 2002

When defining a string that requires space before or after it, again use quotes:

<string name="text1">  No spaces before and after  </string>
<string name="text2">"  Two spaces before and after  "</string>

The strings will support a newline by escaping the letter n:

<string name="text1">Split over
two lines</string>
<string name="text2">2 TextViews
4 Lines</string>
ack2 20in06

Escaping a t adds a tab to the defined string:

<string name="text1">Tab stops	a		b</string>
<string name="text2">				c		d</string>
ack2 20in07

To see the escape character (backslash), use two of them:

<string name="text1">Backlash:\</string>
<string name="text2">Slash:/</string>

The android:textstyle attribute of a TextView in a layout file can be used to set the text to bold or italic (or both):

android:textStyle="bold"
android:textStyle="italic"
android:textStyle="bold|italic"

This can be achieved in the strings.xml file using a bold (<b>) or italic (<i>) tag. It also supports an underline tag (<u>). However, instead of applying the formatting to the whole text of the TextView, it can be used for individual portions of the text:

<string name="text1">Hey look:<b>bold</b> and <i>italic</i>.</string>
<string name="text2">And look: <u>underline</u> and <b><i><u>bold
 italic underline
</u></i></b>.</string>

This results in:

ack2 20in08

See Also

The developer documentation on string resources.

Source Download URL

The source code for this project is in the Android Cookbook repository, in the subdirectory StringsXML (see “Getting and Using the Code Examples”).

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

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