This chapter discusses these localization topics:
Localization Guidelines
Locales
Overlays
Dates and Times
Numbers
Obtaining Locale Information
In addition to this chapter, also see Chapter 1, "Text," which describes how to work with text and characters in a way that makes your application easily localizable.
This chapter does not cover how to actually perform localization of resources. For more information on this subject, see the documentation for your toolset.
Localization Guidelines
If there is a possibility that your application is going to be localized, you should follow these guidelines when you start planning the application. It's a good idea to follow these guidelines even if you don't think your application is going to be localized.
- If you use the English language version of the software as a guide when designing the layout of the screen, try to allow:
- Don't put language-dependent strings in code. If you have to display text directly on the screen, remember that a one-line warning or message in one language may need more than one line in another language. See the section "Strings" in Chapter 1, "Text," for further discussion.
- Don't depend on the physical characteristics of a string, such as the number of characters, the fact that it contains a particular substring, or any other attribute that might disappear in translation.
- Internal database names must use only 7-bit ASCII characters (0x20 through 0x7E). Store the user-visible name of your application in an APP_ICON_NAME_RESOURCE so that it can be translated to other languages.
- Use the functions described in this book when working with characters, strings, numbers, and dates.
- Consider using string templates as described in the section "Dynamically Creating String Content" in Chapter 1. Use as many parameters as possible to give localizers greater flexibility. Avoid building sentences by concatenating substrings together, as this often causes translation problems.
- Abbreviations may be the best way to accommodate the particularly scarce screen real estate on the Palm Powered™ device.
- Remember that user interface elements such as lists, fields, and tips scroll if you need more space.
The book Exploring Palm OS: User Interface Guidelines provides further user interface guidelines.
Locales
A locale specifies a place and is used to determine which formats, languages, and encodings to use for locale-specific items such as dates, numbers, and strings. In Palm OS®, a locale is identified by both a language and a country. In general, the language determines which character encoding is used on the device. The country specifies a dialect. For example, the language "English" uses different dialects for "USA" and for "Britain."
The system maintains several locale variables, allowing it to tailor system resources and functionality to fit the locale in which the device is being used. Most of the time, these variables all point to the same locale.
- The ROM locale is the locale stored in ROM on the device. The ROM locale is used to initialize the other locale variables after a hard reset. Use
LmGetROMLocale()
if you need to determine the ROM locale. - The system locale is the locale that Palm OS code uses to obtain various locale settings. The system locale is initially set to the ROM locale, but it can be different. For example, on an EFIGS ROM (which is a ROM for English, French, Italian, German, and Spanish), the user chooses from among several languages when Palm OS starts up, and doing so changes the system locale but not the ROM locale. Use
LmGetSystemLocale()
to determine which is the system locale. This function also returns the character encoding used on the device. - The formats locale is the locale that most applications should use to obtain locale-specific settings. The formats locale is initially the same as the system locale, but the user can change it in the Formats Preference panel. Use
LmGetFormatsLocale()
to obtain this locale. You can also useLmSetFormatsLocale()
to change it, but do not do so without the user's permission.In most cases, the user is able to override the locale-specific settings using individual preferences on the Formats Preference panel. Therefore, you should check for a preference setting where one is available. See "Obtaining Locale Information" for more information.
- The overlay locale is the locale that the Database Manager uses to decide which overlay to load. Thus, the overlay locale controls the target language of system and application user interface elements. It is initially set to the system locale. Use
DmGetOverlayLocale()
to obtain the value of this locale, and useDmSetOverlayLocale()
if you need to change it. - The fallback overlay locale is the locale that the Database Manager uses if an overlay for the overlay locale does not exist. By default, it uses the ROM locale. Use
DmGetFallbackOverlayLocale()
to obtain the value of this locale and useDmSetFallbackOverlayLocale()
if you need to change it.
Overlays
You localize Palm OS resource databases using overlays. Localization overlays provide a way of localizing a software module without requiring a recompile or modification of the software. Each overlay database is a separate resource database that provides an appropriately localized set of resources for a single software module (the PRC file, or base database) and a single target locale (language and country).
No requirements are placed on the base database, so for example, third parties can construct localization overlays for existing applications without forcing any modifications by the original application developer. In rare cases, you might want to disable the use of overlays to prevent third parties from creating overlays for your application. To do so, you should include an APP_LAUNCH_PREFS_RESOURCE
with ID 0 in the database and set its ALPF_FLAG_NO_OVERLAY
flag to true
.
An overlay database has the same creator as the base database, but its type is 'ovly'
, and a suffix identifying the target locale is appended to its name. For example, Datebook.prc
might be overlaid with a database named Datebook_jaJP
, which indicates that this overlay is for Japan. Each overlay database has an OVERLAY_RESOURCE
with ID 1000.
When a resource file is opened, the Database Manager looks for an overlay matching the base database and the overlay locale. The overlay database's name must match the base database's name, its suffix must match the locale's suffix, and it must have an OVERLAY_RESOURCE
with ID 1000. If the name, suffix, and overlay resource are all correct and the overlay passes various checks to ensure it's appropriate for use with the base database, the overlay is opened in addition to the base database. When the base database is closed, its overlay is closed as well.
The overlay is opened in read-only mode and is hidden from the programmer. When you open a database, you'll receive a reference to the base database, not the overlay. You can simply make Database Manager calls like you normally would, and the Database Manager accesses the overlay where appropriate.
When accessing a localizable resource, do not use functions that search for a resource only in the database you specify. For example, see Listing 3.1.
Listing 3.1 Wrong way to access resources
// WRONG! searches only one database. DmOpenRef dbP = DmNextOpenResDatabase(NULL); uint16_t resIndex = DmFindResource(dpP, strRsc, strRscID, NULL); MemHandle resH = DmGetResourceByIndex(dbP, resIndex);
In Listing 3.1, dbP
is a reference to the most recently opened database, which is typically the overlay version of the database. Passing this reference to DmFindResource()
means that you are searching only the overlay database for the resource. If you're searching for a non-localized resource, DmFindResource()
won't be able to locate it. Instead, you should use DmGetResource()
, which searches a database and its overlay for a resource
Listing 3.2 Correct way to access resources
// Right. DmGetResource searches both databases. MemHandle resH = DmGetResource(dbRef, strRsc, strRscID);
The Database Manager opens an overlay only if the base database is opened in read-only mode. If you open a resource database in read-write mode, the associated overlay is not opened. What's more, if you modify an overlaid resource in the base database, the overlay database may no longer be valid. Thus if you change the base database, you must also change the overlay database.
For more information on overlays and resource databases, see Exploring Palm OS: Memory, Databases, and Files.
Dates and Times
If your application deals with dates and times, it should abide by the values the user has set in the system preference for date and time display. The default preferences at startup vary among locales, and the default values can be overridden by the user.
To check the system preferences call PrefGetPreference()
with one of the values listed in the second column of Table 3.1. The third column lists an enumerated type that helps you interpret the value.
Table 3.1 Date and time preferences
Time formats (i.e., use a 12-hour clock or use a 24-hour clock) |
||
Minutes east of Greenwich Mean Time (GMT), also known as Universal Coordinated Time (UTC). |
To work with dates and times in your code, use the Date and Time Manager API. It contains functions such as DateToAscii()
, TimeToAscii()
, DayOfMonth()
, DayOfWeek()
, DaysInMonth()
, TimeToInt()
, TimeIs24HourFormat()
, and DateTemplateToAscii()
, which allow you to work with dates and times independent of the user's preference settings.
Numbers
If your application displays large numbers or floating-point numbers, you must check and make sure you are using the appropriate thousands separator and decimal separator for the device's country by doing the following (see Listing 3.3):
- Store number strings using US conventions, which means using a comma (,) as the thousands separator and a decimal point (.) as the decimal separator.
- Use
PrefGetPreference()
andLmGetNumberSeparators()
to retrieve information about how the number should be displayed. - Use
StrLocalizeNumber()
to perform the localization. - If a user enters a number that you need to manipulate in some way, convert it to the US conventions using
StrDelocalizeNumber()
.
Listing 3.3 Working with numbers
// store numbers using US conventions. char *jackpot = "20,000,000.00"; char thou; // thousand separator char dp; // decimal separator // Retrieve user's preferred number format. LmGetNumberSeparators((NumberFormatType) PrefGetPreference(prefNumberFormat), &thou, &dp); // Localize jackpot number. Converts "," to thou // and "." to dp. StrLocalizeNumber(jackpot, thou, dp); // Display string. // Assume inputNumber is a number user entered, // convert it to US conventions this way. Converts // thou to "," and dp to "." StrDelocalizeNumber(inputNumber, thou, dp);
Obtaining Locale Information
Some applications may require information about a specific locale. For example, an application might need to know the country name for a locale.
The information that most applications require is stored in the system preferences structure and can be obtained using PrefGetPreference()
. This is the recommended way of obtaining locale-specific settings because the user can override many of these settings. Applications should always honor the user's preferences rather than the locale defaults.
Other locale-specific settings can not be set by the user and are not stored in the system preferences. Instead, these settings are stored in a private resource that contains information about several possible locales, including the locale currently used by the system. For example, the user cannot change the symbol used for the local currency. If your application needs this information, it must use the Locale Manager function LmGetLocaleSetting()
to retrieve it. Listing 3.4 shows how to use LmGetLocaleSetting()
.
Listing 3.4 Retrieving a locale setting using Locale Manager
LmLocaleType locale; char currencySymbol[kMaxCurrencySymbolLen+1]; uint16_t index; // Find out what the formats locale is. LmGetFormatsLocale(&locale); // Find out which index in the locale resource // contains info about that locale. index = LmBestLocaleToIndex(&locale); // Get the currency symbol stored in the locale at // that index. LmGetLocaleSetting(index, lmChoiceCurrencySymbol, currencySymbol, sizeof(currencySymbol));
Table 3.2 shows which types of information about the formats locale should be retrieved from the system preferences and which types should be retrieved from the locale resource. Of course, if you want to retrieve information about a different locale or if you want to look up the default used for the formats locale, you would always use the Locale Manager instead of the Preferences Manager.
Table 3.2 Obtaining locale information