Part two or our little localization tutorial.
For a quick recap, we learned first
here how to use string resources to leverage translation in localized applications.
The image below can help refresh your memory.
This solves the problem of translating the content of strings we put in your layout xml files. But what if we need to access those strings programmatically? Thanks to the fantastic Android framework, it couldn't be easier.
Using the Resources class you can access and resource embedded in your app, including strings.
Call
Resources.getString(int resourceID) to get the desired string. Resource strings are defined under
R.string.*.
For instance, consider the following resource string
<resources>
<string name="app_title">Test app</string>
</resources>
calling
getString(R.string.app_title) returns "Test app".
You can call
getResources() from the the app context to get ta resource object. More information
here.
Now, what if I need different images and icons for different countries? Like the exit signs in English and French.
No problem, just name the
drawable directory the same way we learned to name the
values directory.
For instance,
drawable-fr contains icons/images for the French locale,
drawable-es for Spanish and you got the idea.
But we all learned that we should use different drawables for different screen densities, that's why we have by default the directories
drawable-ldpi,
drawable-mdpi and
drawable-hdpi. In order to have localizable icon/images taking density under consideration, combine both qualifiers in the name of the directory. Like:
drawable-fr-mdpi, which contains drawable resources for French and high density devices. More details about how to combine different qualifiers can be found
here.
Ok, enough with translation, let's go talk about something more interesting now.
No, wait! Just a few more things about translating your app:
- Keep in mind that translated text will most likely have a different length than the original one. People say sentences in English are shorter than in French. I can't vouch for that :) but design the layout of your app considering text of variable size;
- it's a good idea to use resource strings for any text that will be displayed, even if that text does not need translation
- translating is not just about "replacing words". Be careful with the cultural significance of sentences and words.
To the other good stuff now: localizing: making your app support other cultural attributes like currency, date/time and number format.
1) The Locale class
All starts with the
Locale class, which let's you get information about the current locale and all other available locales installed. To get an instance of the default
Locale call the static method
Locale.getDefault().
To get an instance of another available locale use one of the public fields in Locale, like
Locale.GERMAN (default locale for Germany).
We'll talk more about it in the next sessions but you can find all you need to know about
Locale here.
2) Date and Time
If you just want to access date or time formatted for the current locale, the easiest way is to use Calendar. (Actually I recommend calendar for and Date/Time reference. Never instantiate a Date object directly).
To get a Calendar object for a specific locale call the factory method
Calendar.getInstance(Locale)
For instance
Calendar.getInstance(Locale.getDefault()) returns the calendar for the default locale
Calendar.getInstance(Locale.FRENCH) returns the calendar for the default French locale. Note that the locale in this case is more about country/culture than language.
Remember when dealing with dates you have to consider the time zone (no, just setting the locale isn't enough). To get a Calendar for a specific locale and time zone call
Locale.getInstance(Timezone, Locale).
More information
here.
More formatting
Formatting date and time is a very common task and to do it properly - taking the Locale under consideration - it's better to use
SimpleDateFormat.This class provides facilities to format date and time anyway you need.
To instantiate a
SimpleDateFormat object with a pattern and locale use
SimpleDateFormat(String template, Locale locale). Don't forget to set the time zone calling
SimpleDateFormat.setTimeZone(). More information
here.
3) Currency and number
Formatting currency and numbers in general kind of go together. But not quite :)
First, to get general information about number formatting (like decimal separator, infinity representation, etc), use
DecimalFormalSymbols. To get the decimal symbols for a particular locale use the constructor
DecimalFormatSymbols(Locale).
The CurrencyClass helps us to get currency symbols for several locales (although you can get the almost the same things from
DecimalFormatSymbol). Use
CurrencyClass.getInstance(Locale) to get an instance representing currency for the target locale.
Formatting currency and numbers in general is a more elaborate task. The class used to format numbers in general is
DecimalFormat.
DecimalFormat will take in consideration the current locale so the code prints
DecimalFormat formatter=new DecimalFormat("##.#");
System.out.println(formatter.format(123.4);
123.4 for a US (English) locale and
123,4 for a French locale.
Consult the
documentation to learn more about formatting patterns but on you want to keep in mind is "$", used to format currency.
The problem with
DecimalFormat is that it's not possible to get a localized instance of a DecimalFormat object through the class itself (to the moment, AFAIK).To get a localized object we need to use
NumberFormat.
A call to
NumberFormat.getInstance(Locale) will return an localized version of an object that has
NumberFormat as base class. The documentation says it's possible that this object will not be a
DecimalFormat object but so far the Android SDK always returns
DecimalFormat (since it's the only numeric subclass of
NumericFormat).
Calling
NumberFormat.getInstance(Locale) will return a
NumberFormat object that can be used to format numbers in general. If you don't care about a pattern, just call
format(...) and you're done. If you need to specify a custom pattern for formatting, call
applyLocalizedPattern if the pattern is already localized or
applyPattern if the pattern is not localized (not using localized numeric symbols).
And this concludes our localization chat.
Have fun!
---
Programming tricks and tips for android developers