Programming and Designing a Global Application

To compete successfully in international markets, your software must easily accommodate differences in language, culture, and hardware. The most effective way to accomplish this is to take international considerations into account at the beginning of the product cycle and throughout development. By planning ahead, you can create software in a single effort that accommodates multiple languages, instead of just one.

The process of developing an application whose features and code designs do not make assumptions based on a single language or locale and whose source code simplifies the creation of different language editions of an application is known as globalization. The process of creating globalized software is divided into two areas—internationalization, which covers generic coding and design issues, and localization, which involves translating and customizing a product for a specific market.

This section focuses on internationalization and, specifically, the issues that you must consider when designing the code and user interface for a global application.


Internationalizing Software

The goal of internationalization is to present users with a consistent look, feel, and functionality across different language editions of a product. Users expect localized software to support the same basic set of features that the original language edition of the product does, and they expect it to achieve the same level of quality. They also expect different language editions to interact smoothly with one another.

Internationalizing software involves designing a user interface and a code base that are generic enough to work for most of the product’s intended language editions. Of course, some customization may be necessary, but the fewer changes needed for international editions, the faster you can release the product.

A key prerequisite to creating an internationalized code base for your application is that all language editions share the same source files. Maintaining separate source files for different language editions of the same product is error-prone, a waste of time and disk space, and unnecessary for code that is properly internationalized.

The process of coding an application that supports multiple language editions requires you to perform three tasks:

  • Create a consistent user interface that accommodates language changes.
  • Support international characters, as well as international date, time, currency, and numeric formatting and sorting conventions in your code.
  • Implement smart coding practices that save time and money during the localization process.


Creating an International User Interface

A major aspect of creating an international user interface involves translating the text used in title bars, menus, other controls, messages, and registry entries. To make this process easier, store interface text as resources in your application’s resource file, rather than including it in the source code of the application. Also translate any menu commands that your application stores for its file types in the system registry.

When translating text, remember that each language has its own syntax and grammar. The following are some general guidelines to keep in mind when translating text:

  • Avoid using vague words that can have several meanings in different contexts.
  • Avoid colloquialisms, jargon, acronyms, and abbreviations.
  • Use good grammar. Translation is a difficult task even when a translator does not have to deal with poor grammar.
  • Avoid dynamic, or run-time, concatenation of different strings to form new strings—for example, composing messages by combining frequently used strings. An exception is the construction of file names and names of paths.
  • Avoid hard-coding file names in a binary file. File names may need to be translated.
  • Avoid including text in images and icons. Doing so requires that these also be translated.

Translation of interface text often increases the length of text by 30 percent or more. In some extreme cases, the character count can increase by more than 100 percent; for example, the English word “move” becomes “verschieben” in German. Accordingly, if the amount of space for displaying text is strictly limited, as in a status bar, restrict the length of the interface text to approximately one-half of the available space. In contexts that allow more flexibility, such as dialog boxes and property sheets, allow 30 percent for text expansion in the interface design. Text in message boxes, however, should allow for text expansion of about 100 percent. Avoid having your software rely on the position of text in a control or window because translation may require movement of the text.

Expansion due to translation affects other aspects of your product. A localized version is likely to affect file sizes, which potentially can change the layout of your installation disks and setup software.

Additionally, translation is not always a one-to-one correspondence. A single word can have multiple translations in another language. Adjectives and articles sometimes change their spelling according to the gender of the nouns they modify. Therefore, be careful when reusing a string in multiple places. Similarly, several words may have only a single meaning in another language. This is particularly important when creating keywords for the Help index for your software.


Adhering to International Conventions

When you are internationalizing a user interface, language is not the only factor to consider. Several countries can share a common language but have different conventions for expressing information. In addition, some countries can share a language but use different keyboard conventions.

International keyboards differ. Avoid using punctuation character keys as shortcut keys because they are not always found on international keyboards or easily produced by the user. What seems like an effective shortcut because of its mnemonic association—for example, CTRL+B for Bold—may need to be changed to fit a particular language. Similarly, macros or other utilities that invoke menus or commands based on access keys are not likely to work in an international version because the command names on which the access keys are based differ.

Additionally, keys do not always occupy the same positions on all international keyboards. Even when they do, the interpretation of the unmodified keystroke can be different. For example, on US keyboards, SHIFT+8 results in an asterisk character. However, on French keyboards, it generates the number 8. Similarly, avoid using CTRL+ALT combinations, because the system interprets this combination for some language versions as the ALTGR key, which generates alphanumeric characters. Similarly, avoid using the ALT key as a modifier because it is the primary keyboard interface for accessing menus and controls. In addition, the system uses many specialized versions for special input. For example, ALT+~ invokes special input editors in Asian versions of Windows. For text fields, pressing ALT+number enters characters in the upper range of a character set. Similarly, avoid using the following characters when assigning shortcut keys.

@ £ $ { } [ ] \ ~ | ^ ' < >


Preparing for Cultural Differences

A more subtle factor to consider when you are preparing software for international markets is cultural differences. For example, users in the US may recognize a rounded mail box with a flag on the side as an icon for a mail program, but this image may not be recognized by users in other countries. Sounds and their associated meanings may also vary from country to country.

It is best to review the proposed graphics for international applicability early in your design cycle. Localizing graphics can be a time-consuming process.

Although graphics communicate more universally than text, graphical aspects of your software—especially icons and toolbar button images—may also need to be revised to address an international audience. For example, a toolbar image that includes a magic wand to represent access to a wizard interface does not have meaning in many countries and requires a different image.

When possible, choose generic images and glyphs. Even if you can create custom designs for each language, having different images for different languages can confuse users who work with more than one language version.

Many symbols with a strong meaning in one culture do not have any meaning in another. For example, many symbols for holidays and seasons are not shared around the world. Importantly, some symbols can be offensive in some cultures; for example, the open palm commonly used at US crosswalk signals is offensive in some countries. Some metaphors also may not apply in all languages.


Supporting International Characters and Formatting

Character encoding is the most basic foundation for any form of text processing; if it is handled poorly, the software is difficult to localize or internationalize. A program also requires functionality that observes language rules and cultural conventions. Windows CE provides support for numerous character codes as well as linguistic and cultural conventions through Unicode and national language support (NLS). Unicode is a universal character encoding system, while NLS carries information on date, time, calendar, number, and currency formats. NLS also provides sorting and character-type information for all the locales supported by the operating system. For more information on how to use Unicode and NLS when creating a global application, see Programming with Unicode and NLS.


Coding for Internationalization

When you are coding your application, several coding practices can make the internationalization process easier. A few of these practices are the following:

  • Do not hard-code localizable elements.

    Hard-coded strings, characters, constants, screen positions, file names, and file paths are difficult to track down and localize. Isolate all localizable items into resource files, and minimize compile dependencies.

  • Do not make buffers too small to handle localized text.

    Buffers that are declared to be the exact size of a word or a sentence will probably overflow when text is translated. Consider the following example. Your application declares a 2-byte buffer size for the word “OK.” In Spanish, however, when it refers to the text in an OK button, the same word is translated as “Aceptar,” which would cause your application to overflow.

  • Do not perform string composition.

    For example, translating “wrong file” and “wrong directory” to Italian results in “file errato” and “cartella erratta,” respectively. If you try to perform string composition using the syntax “wrong%s”, it does not work.

Another potential problem involves declaring a single string and displaying it in a number of different contexts: on a menu, in a dialog box, and perhaps in several messages. The problem with using all-purpose strings is that in European languages, adjectives and some nouns have from 4 to 14 different forms, such as masculine, feminine, and neuter singular; and masculine, feminine, and neuter plural, that must match the nouns they modify. A single string displayed in different contexts is correct in gender and number in some cases but incorrect in others.

One way to ensure that your coding practices works in an international market is to substitute your language strings with a pseudolanguage, and then test your code. Any potential problems should surface immediately.


Programming with Unicode and NLS

Windows CE includes national language support (NLS) APIs, as well as Unicode, to assist you in creating global applications.

NLS functions help Windows CE–based applications to support the differing language-specific and location-specific needs of users around the world. These functions enable you to specify a locale so that you can correctly display times, dates, and other language-specific and location-specific information in your application. NLS also includes support for different keyboard layouts and language-specific fonts.

Unicode is a worldwide character-encoding standard that treats all characters as having a fixed width of 2 bytes. It can represent all the world’s characters in modern computer use, including technical symbols and special characters used in publishing. Because Unicode characters are 2 bytes, Unicode-enabled functions are often referred to as wide-character functions.

Unicode defines semantics for each character, standardizes script behavior, provides a standard algorithm for bidirectional text, and defines cross-mappings to other standards. Because each Unicode character is 16-bits wide, it is possible to have separate values for up to 65,536 characters.

Windows CE uses Unicode exclusively at the system level for character and string manipulation. By implementing Unicode in your applications, you can provide your application with universal data exchange capabilities for global marketing, using a single binary file for every possible character code. This simplifies localization of software and improves multilingual text processing.

The following sections provide an overview of the Unicode standard, and then explain how to use NLS functions in Windows CE–based applications.


Understanding the Unicode Standard

The Unicode standard defines codes for characters in most major languages written today. Scripts include Latin, Greek, Cyrillic, Armenian, Hebrew, Arabic, Devanagari, Bengali, Gurmukhi, Gujarati, Oriya, Tamil, Telugu, Kannada, Malayalam, Thai, Lao, Georgian, Tibetan, Japanese Kana, the complete set of modern Korean Hangul, and a unified set of Chinese/Japanese/Korean (CJK) ideographs. There are also several other scripts that have recently been added, including Ethiopic, Canadian Syllabics, Cherokee, Sinhala, Syriac, Burmese, Khmer, and Braille.

The Unicode standard also includes punctuation marks, diacritics, mathematical symbols, technical symbols, arrows, and dingbats. It supports diacritics, which are character marks such as the tilde (~). Diacritics are used in conjunction with base characters to encode accented or vocalized letters; for example, ñ. In all, the Unicode standard provides codes for nearly 39,000 characters from the world’s alphabets, ideograph sets, and symbol collections.

In addition, there are approximately 18,000 unused code values that have been reserved for future use. The Unicode standard also contains 6,400 code values that software and hardware developers can assign internally for their own characters and symbols.


Defining a Character Set

Written languages are represented by textual elements—called code elements or characters—that are used to create words and sentences. These elements can be letters such as s or V, characters such as those used in Japanese Hiragana to represent syllables, or ideographs such as those used in Chinese to represent full words or concepts.

A code element is an abstract concept, defined as the smallest component of a written language that has semantic value. A single 16-bit number is assigned to each code element defined by the Unicode standard. Each of these 16-bit numbers is called a code value and, when referred to in text, is listed in hexadecimal form following the prefix U. For example, the code value U+0041 is the hexadecimal number 0041, which is equal to the decimal number 65. It represents the character A in Unicode.

Each code element is also assigned a unique name that specifies it and no other. For example, U+0041 is assigned the character name LATIN CAPITAL LETTER A. U+0A1B is assigned the character name GURMUKHI LETTER CHA.

Code elements are grouped logically throughout the range of code values, which is called the codespace. The coding begins at U+0000 with standard ASCII characters, and then continues with Greek, Cyrillic, Hebrew, Arabic, Indic, and other scripts. Then symbols and punctuation are inserted, followed by Hiragana, Katakana, and Bopomofo. The complete set of modern Hangul appears next, followed by the unified ideographs. The end of the codespace contains code values that are reserved for further expansion, private use, and a range of compatibility characters.

The following illustration shows Unicode’s encoding layout.

The Unicode standard defines how characters are interpreted. It is not responsible for rendering characters on screen or paper. The software or hardware is responsible for the appearance of the characters on the screen or in print. For example, the character identified by a Unicode code value as BENGALI DIGIT 5 is an abstract entity. The mark made on the screen or paper—called a glyph—is a visual representation of the character. The Unicode standard does not define the glyph image. It does not specify the size, shape, or orientation of the character. It simply defines how the character is interpreted by the software or device.

Occasionally, you may choose to render multiple characters together. This is referred to as creating a composite character. For example, “â” is a composite character created by rendering “a” and “^” together. A composite character is typically made up of a base letter, which occupies a single space, and one or more non-spacing marks, which are rendered in the same space as the base letter.

The Unicode standard specifies the order of characters used to create a composite character. The base character comes first, followed by one or more non-spacing marks. If a code element is encoded with more than one non-spacing mark, you can render the non-spacing marks in any order as long as the marks do not interact typographically. If they do interact, the order must be considered. The Unicode standard specifies how competing non-spacing characters are applied to a base character.

As an alternative to rendering your own composite characters, the Unicode standard offers precomposed characters to retain compatibility with established standards such as Latin 1, which includes many precomposed characters such as “ü” and “ñ”. Each precomposed character is represented by a single code value, rather than two or more code values which may combine during rendering. For example, the character “ü” can be encoded as the single code value U+00FC “ü” or as the base character U+0075 “u” followed by the non-spacing character U+0308 “¨”.

Precomposed characters may also be decomposed. For example, an application importing a text file containing the precomposed character “ü” may decompose that character into a “u” followed by the non-spacing character “¨”. This allows easy alphabetical sorting for languages where character modifiers do not affect alphabetical order. The Unicode standard defines decomposition for all precomposed characters.


Specifying Locales with NLS

The national language support functions enable you to target an application for a specific locale. A locale is a collection of language-related user preference information represented as a list of values. Each system has at least one installed locale and often has many locales from which the user can choose.

The system assigns a locale to each thread. Initially, the system assigns the system default locale to the thread. This default locale is set by the user when the system is installed or through the Regional Settings program in Control Panel. If a thread is run in a process belonging to a user, the system assigns the user-default locale to the thread. An application can override either default by using the SetThreadLocale function to explicitly set the locale for a thread.

Each locale has a unique locale identifier (LCID), which is a 32-bit value that consists of a language identifier and a sort identifier. The LCID is constructed using the MAKELCID macro. The following illustration shows the format of the bits in an LCID.

+-------------+---------+-------------------------+
|   Reserved  | Sort ID |      Language ID        |
+-------------+---------+-------------------------+
31         20 19     16 15                      0   bit

The following locale identifiers are predefined:

  • LOCALE_SYSTEM_DEFAULT, which identifies the system default locale.
  • LOCALE_USER_DEFAULT, which identifies the locale of the current user.
  • LOCALE_NEUTRAL, which identifies the default language-neutral locale.

The LOCALE_NEUTRAL identifier is the same as LOCALE_USER_DEFAULT. An application can retrieve the current locale identifiers by using the GetSystemDefaultLCID and GetUserDefaultLCID functions.

A language identifier is a standard international numeric abbreviation for a country or geographical region. Each language has a unique language identifier (LANGID), which is a 16-bit value that consists of a primary language identifier and a secondary language identifier. The LANGID is constructed using the MAKELANGID macro. The following illustration shows the format of the bits in a LANGID.

+-------------------------+-------------------------+
|  Secondary Language ID  |   Primary Language ID   |
+-------------------------+-------------------------+
15                    10  9                         0   bit

The following language identifiers are predefined:

  • LANG_SYSTEM_DEFAULT, which identifies the system default language.
  • LANG_USER_DEFAULT, which identifies the language of the current user.

An application can retrieve the current language identifiers by using the GetSystemDefaultLangID and GetUserDefaultLangID functions.

It is often necessary to get specific information on available languages and locales in order to handle strings appropriately. Each element of locale information has a corresponding LCTYPE constant. To get the locale information, call the GetLocaleInfo function with the constant that corresponds to the information that is needed.

Most LCTYPE constants are mutually exclusive, so usually only one type of information can be retrieved at a time. The exceptions to this are LOCALE_NOUSEROVERRIDE, LOCALE_USE_CP_ACP, and LOCALE_RETURN_NUMBER, which can combined with other LCTYPE constants by using the binary OR operator.

Locale information is always stored and manipulated as a null-terminated string. No binary data is allowed; any numeric values must be specified as text. Each type of information has a particular format. Also, several of the types are linked together, so that changing one changes the value of the other as well.

Although a specified locale identifier may be supported, it is not available for use by an application unless it is also installed.


Retrieving Time and Date Strings

One of the most useful sets of NLS calls is the GetDateFormat, GetTimeFormat, EnumDateFormats, EnumTimeFormats, and EnumCalendarInfo functions, which return formatted date and time picture strings. EnumDateFormats and EnumTimeFormats enumerate the date and time picture strings that the system carries for a particular locale. With these two functions, you can build a list of possible date and time strings to present to the user.

GetDateFormat and GetTimeFormat each return a single string. When you use GetDateFormat, your application can request a string containing the correct date in the default short date format (DATE_SHORTDATE) or the default long date format (DATE_LONGDATE) for a particular locale.

For GetTimeFormat, the time values in the SYSTEMTIME structure pointed to by lpTime must be valid. The function checks each of the time values to determine that it is within the appropriate range of values. If any of the time values are outside the correct range, the function fails and sets the last error to ERROR_INVALID_PARAMETER.

The function ignores the date portions of the SYSTEMTIME structure pointed to by lpTime. If a time marker exists and the TIME_NOTIMEMARKER flag is not set, the function localizes the time marker, based on the specified locale identifier. Examples of time markers are “AM” and “PM” for US English, and “de.” and “du.” for Mexican Spanish.

The function does not return an error for a bad format string. The function simply forms the best time string that it can. If more than two hour, minute, second, or time marker format pictures are passed in, the function defaults to two.

For GetDateFormat, the date values in the SYSTEMTIME structure pointed to by lpDate must be valid. The function checks each of the date values: year, month, day, and day of week. If the day of the week is incorrect, the function uses the correct value and returns no error. If any of the other date values are outside the correct range, the function fails and sets the last error to ERROR_INVALID_PARAMETER.

The day name, abbreviated day name, month name, and abbreviated month name are all localized based on the specified locale identifier. The function ignores the time portions of the SYSTEMTIME structure pointed to by lpDate.

To obtain the short and long date format for the default locale calendar, use the GetLocaleInfo function with the LOCALE_SSHORTDATE or the LOCALE_SLONGDATE flag. To get the date format for an alternate calendar, use GetLocaleInfo with the LOCALE_IOPTIONALCALENDAR flag. To get the date format for a particular calendar, use GetCalendarInfo. To return all the date formats for a particular calendar, use the EnumCalendarInfo or the EnumDateFormatsEx function.

To obtain the time format without performing any actual formatting, use the GetLocaleInfo function with the LOCALE_STIMEFORMAT flag set.


Defining Calendar Formats

Most locales use the standard Gregorian calendar and a set number of date formats. These default choices for date formats are available for display by using the EnumDateFormats function. Other locales require special considerations when you are creating a complete list of format choices. Some of these require you to insert text strings within the date format string; others require a completely different method of computation of the values. These special requirements are addressed by the addition of certain LCTYPE and CALTYPE values.

Each LCID has a default calendar type associated with it. A locale identifier can also have an alternate calendar type. To have an alternate calendar type for an LCID, you must set LOCALE_IOPTIONALCALENDAR to the alternate calendar type for this locale.

The CALTYPE constants are used in the EnumCalendarInfo and GetCalendarInfo functions to define particular pieces of calendar information. Some of these types are also used for the SetCalendarInfo function.

The following table shows CALTYPE constants that are mutually exclusive—that is, they cannot be used in combination with each other in a function call.

Type
Description
CAL_ICALINTVALUE | An integer value indicating the calendar type of the alternate calendar.
CAL_IYEAROFFSETRANGE | One or more null-terminated strings that specify the year offsets for each of the era ranges. The last string has an extra terminating null character.
CAL_SABBREVDAYNAME1 | Abbreviated local name of the first day of the week.
CAL_SABBREVDAYNAME2 | Abbreviated local name of the second day of the week.
CAL_SABBREVDAYNAME3 | Abbreviated local name of the third day of the week.
CAL_SABBREVDAYNAME4 | Abbreviated local name of the fourth day of the week.
CAL_SABBREVDAYNAME5 | Abbreviated local name of the fifth day of the week.
CAL_SABBREVDAYNAME6 | Abbreviated local name of the sixth day of the week.
CAL_SABBREVDAYNAME7 | Abbreviated local name of the seventh day of the week.
CAL_SABBREVMONTHNAME1 | Abbreviated local name of the first month of the year.
CAL_SABBREVMONTHNAME2 | Abbreviated local name of the second month of the year.
CAL_SABBREVMONTHNAME3 | Abbreviated local name of the third month of the year.
CAL_SABBREVMONTHNAME4 | Abbreviated local name of the fourth month of the year.
CAL_SABBREVMONTHNAME5 | Abbreviated local name of the fifth month of the year.
CAL_SABBREVMONTHNAME6 | Abbreviated local name of the sixth month of the year.
CAL_SABBREVMONTHNAME7 | Abbreviated local name of the seventh month of the year.
CAL_SABBREVMONTHNAME8 | Abbreviated local name of the eighth month of the year.
CAL_SABBREVMONTHNAME9 | Abbreviated local name of the ninth month of the year.
CAL_SABBREVMONTHNAME10 | Abbreviated local name of the tenth month of the year.
CAL_SABBREVMONTHNAME11 | Abbreviated local name of the eleventh month of the year.
CAL_SABBREVMONTHNAME12 | Abbreviated local name of the twelfth month of the year.
CAL_SABBREVMONTHNAME13 | Abbreviated local name of the thirteenth month of the year, if it exists.
CAL_SCALNAME | Local name of the alternate calendar.
CAL_SDAYNAME1 | Local name of the first day of the week.
CAL_SDAYNAME2 | Local name of the second day of the week.
CAL_SDAYNAME3 | Local name of the third day of the week.
CAL_SDAYNAME4 | Local name of the fourth day of the week.
CAL_SDAYNAME5 | Local name of the fifth day of the week.
CAL_SDAYNAME6 | Local name of the sixth day of the week.
CAL_SDAYNAME7 | Local name of the seventh day of the week.
CAL_SERASTRING | One or more null-terminated strings that specify each of the Unicode codepoints specifying the era associated with the specified CAL_IYEAROFFSETRANGE. The last string has an extra terminating null character.
CAL_SLONGDATE | Long date formats for this calendar type.
CAL_SMONTHNAME1 | Local name of the first month of the year.
CAL_SMONTHNAME2 | Local name of the second month of the year.
CAL_SMONTHNAME3 | Local name of the third month of the year.
CAL_SMONTHNAME4 | Local name of the fourth month of the year.
CAL_SMONTHNAME5 | Local name of the fifth month of the year.
CAL_SMONTHNAME6 | Local name of the sixth month of the year.
CAL_SMONTHNAME7 | Local name of the seventh month of the year.
CAL_SMONTHNAME8 | Local name of the eighth month of the year.
CAL_SMONTHNAME9 | Local name of the ninth month of the year.
CAL_SMONTHNAME10 | Local name of the tenth month of the year.
CAL_SMONTHNAME11 | Local name of the eleventh month of the year.
CAL_SMONTHNAME12 | Local name of the twelfth month of the year.
CAL_SMONTHNAME13 | Local name of the thirteenth month of the year, if it exists.
CAL_SSHORTDATE | Short date formats for this calendar type.

If the local name for the day of the week or for a month is an empty string, that name is identical to the name given in the corresponding locale information and therefore is not duplicated here.

The CAL_IYEAROFFSETRANGE and CAL_SERASTRING values vary in format, depending on the type of optional calendar. The following example shows the values for these types—for each supported alternate calendar type—along with the formula for how to use the CAL_IYEAROFFSETRANGE value to compute the correct year given the Gregorian current year value Y.

CAL_ICALINTVALUE = "1"
CAL_IYEAROFFSETRANGE = ""
CAL_SERASTRING = ""

CAL_ICALINTVALUE = "2"
CAL_IYEAROFFSETRANGE = ""
CAL_SERASTRING = ""

CAL_ICALINTVALUE = "3"
CAL_IYEAROFFSETRANGE = "1989\01926\01912\01868\0"
CAL_SERASTRING = "Ux337B\0Ux337C\0Ux337D\0Ux337E\0"
if (Y>=1989) { Y = (Y-1989)+1; }
if (Y>=1926 && Y<1989) { Y = (Y-1926)+1; }
if (Y>=1912 && Y<1926) { Y = (Y-1912)+1; }
if (Y>=1868 && Y<1912) { Y = (Y-1868)+1; }
if (Y<1868) { Y = Y; }

CAL_ICALINTVALUE = "4"
CAL_IYEAROFFSETRANGE = "1912\0"
CAL_SERASTRING = "Ux4E2D\0Ux83EF\0Ux6C11\0Ux570B\0"
if (Y>=1912) { Y = (Y-1912)+1; }
if (Y<1912) { Y = Y; }

CAL_ICALINTVALUE = "5"
CAL_IYEAROFFSETRANGE = "2333\0"
CAL_SERASTRING = ""
Y = Y+2333;


Working with the Input Method Editor

The Input Method Editor (IME) in Windows CE simplifies the process of providing input for users. In particular, IMEs are required for many Asian languages in order to input characters from the keyboard. These languages are often made up of thousands of distinct characters, which makes it impossible to show all of the characters on a single keyboard. To facilitate composition, the IME converts the keystrokes into the characters of the target language as a user types. Depending on the IME, these characters may be further converted. The IME in Windows CE that is localized for Japanese, for example, converts Roman keystrokes entered by a user to Kana or Hiragana. Then, an additional conversion changes characters to Kanji.

The IME can also present a list of alternatives—called the candidate list—in situations in which the composition is ambiguous. A Windows CE application uses the Input Method Manager (IMM) to communicate with the IME.


Overview of the Input Method System

The main parts of the Input Method system are the following:

  • The IME kernel contains the knowledge of the specific language that a user is inputting.
  • The IME user interface (UI) consists of a Status window, a default Composition window, a Candidate window, and a Guideline window.
  • The Input Method Manager (IMM) coordinates the interaction among the window system, the application, and the IME.
  • An input context maintains the current state of user interaction with the IME. In particular, it maintains the composition string, which is the characters that a user is in the process of inputting.
  • An IME Control window routes unhandled IME messages to the IME.

The basic operation is as follows.

A user presses keys on a keyboard. These keystrokes are routed to the IME by the IMM. The IME uses the keys either as commands or to generate characters in the composition string. As the IME carries out its operations, it sends notification messages to the window that currently has the focus. If this window does not process the messages, the messages are sent to the IME Control window, which then routes them back to the IME for default processing. Windows that do not process IME messages are called IME-unaware windows. The IME provides the entire user interface, and the window is completely unaware that an IME is operational. Windows that intercept and process IME messages are called IME-aware windows. By intercepting IME messages, these windows may provide their own UI. By using IMM functions, these windows communicate with the IME.


Overview of the IME User Interface

The IME-provided UI consists of the Status, Composition, Candidate, and Guideline windows. By default, the IME creates and manages these windows for all windows that require text input. For most applications, this default processing is sufficient. An application that relies entirely on the IME for its UI is considered IME-unaware because it is unaware that an IME is functioning in the system.

In contrast, an IME-aware application participates in the operation of the IME. Such an application can control the operation, position, and appearance of the IME, or can even provide its own view of the composition string and candidate list.


Working with the IME Status Window

The Status window provides information on the status of the IME and allows a user to set the IME conversion mode.

The following screen shot shows an active Status window.

IME hot keys turn the IME on or off or, in some situations, switch IMEs. An application can call the ImmGetHotKey function to retrieve, or the ImmSetHotKey function to set, the value of an IME hot key. This is not typically done by applications, however, because most users become accustomed to the particular hot-key for the IME that they are using.

Applications cannot add hot keys to the system. Applications can initiate the same action as a hot key by using the ImmSimulateHotKey function.


Using the IME Composition Window

As a user types, characters are built into a composition string. The string is displayed in the default Composition window if the window that has the focus is IME-unaware. IME-aware windows display the composition string in their own window. The remainder of this section assumes that the string is displayed in the default Composition window. Depending on the conversion mode, the Composition window displays either converted or unconverted text.

An application can call the ImmGetCompositionWindow function to retrieve information about the Composition window contained in the COMPOSITIONFORM structure, or it can call the ImmGetCompositionFont function to get the logical font currently used to display characters in the Composition window.


Working with IME Composition Strings

The IME composition string is the current text in the Composition window. Each composition string consists of one or more clauses, where a clause is the smallest combination of characters that have a meaning in the language.

As a user enters text in the Composition window, the IME tracks the composition status, which includes attribute information, clause information, typing information, and cursor position.

Use the ImmGetCompositionString and ImmSetCompositionString functions to retrieve and set the composition status, or to retrieve and set the characters, attributes, and clauses of the composition string. ImmSetCompositionString also allows notification messages to be sent to the Composition window to ensure that this window updates its appearance based on the changes specified in this call. Applications that set a combination of composition status elements typically set the fNotify parameter to FALSE for all but the last call to this function so that only one notification message is generated for the Composition window.

The attribute information is an array of 8-bit values that specifies the status of characters in the composition string. In this array, all characters of one clause must have the same attribute. For each value in the array, bits 0 through 3 can be a combination of the values in the following table.

Value
Description
ATTR_INPUT | The character being entered by a user, not yet converted by the IME.
ATTR_INPUT_ERROR | The character is the error character and cannot be converted by the IME.
ATTR_TARGET_CONVERTED | The character converted by the IME. A user has selected this character, and the IME has converted it.
ATTR_CONVERTED | A converted character. The IME has already converted this character.
ATTR_TARGET_NOTCONVERTED | The character being converted. A user has selected this character, but the IME has not yet converted it.
ATTR_FIXEDCONVERTED | Characters that are not converted. The IME no longer converts these characters.

The clause information is an array of 32-bit values that specifies the positions of the clauses in the composition string. Each clause has one value in the array that specifies the beginning of the clause. These clause positions are followed by a final value that specifies the length of the full string. Each value in the array specifies the offset, in bytes, from the beginning of the composition string to the clause. The first value is always 0 because the first clause always starts at the beginning of the string. For example, if a string has two clauses, the clause information has three values: the first value is 0, the second value is the offset of the second clause, and the third value is the length of the string.

The typing information is a null-terminated character string that represents the characters entered at the keyboard.

The cursor position is a value indicating the position of the cursor relative to the characters in the composition string. This value is the offset, in bytes, from the beginning of the string. If this value is 0, the cursor is immediately before the first character in the string. If the value is equal to the length of the string, the cursor is immediately after the last character. If the value is –1, the cursor is not present.


Working with the IME Candidate Window

The Candidate window contains a list of candidate characters for the selected character in the Composition window. Users can scroll through this list and select the character that they want before composition of the character is completed in the Composition window. A user can compose the text that they want in this manner until the composition string is finalized and the Candidate window is closed. The following screen shot shows the Composition window with Hiragana text and the Candidate window with Kanji conversion options.

Once the character that a user wants has been determined, the IME sends this character to the application in the form of WM_IME_CHAR or WM_IME_COMPOSITION/GCS_RESULT messages. If the application does not process these messages, the DefWindowProc function translates them into one or more WM_CHAR messages.

The characters in the candidate list are arranged in an array of strings in the CANDIDATELIST structure. Use the ImmGetCandidateList function to retrieve a candidate list and copy the list to a buffer. Use the ImmGetCandidateListCount function to retrieve the total size, in bytes, of all the candidate lists. To get or set information about the Candidate window in the CANDIDATEFORM structure, use the ImmGetCandidateWindow and ImmSetCandidateWindow functions.


Handling IME Window Messages

The system sends IME window messages to the window that has focus when certain events occur. For example, the system sends the WM_IME_SETCONTEXT message when a window is activated. IME-unaware windows pass these messages to the DefWindowProc function, which sends them to the corresponding default IME window. IME-aware windows either process these messages or forward them to their own IME windows.

To have an IME window carry out an action, use the WM_IME_CONTROL message. To have the IME notify an application about changes to the composition string, use the WM_IME_COMPOSITION message. Use the WM_IME_NOTIFY message for general changes to the status of IME windows.

To process the WM_IME_COMPOSITION message, applications test the bits in the lParam parameter and call the ImmGetCompositionString function to retrieve the indicated string or data. The following code example checks for the result string, allocates sufficient memory for the string, and retrieves the result string from the IME.

HIMC hIMC;
HWND hWnd;
DWORD dwSize;
HGLOBAL hstr;
LPSTR lpstr;

case WM_IME_COMPOSITION:
    if (lParam & GCS_RESULTSTR)
    {
        hIMC = ImmGetContext(hWnd);

        If (!hIMC)
            MyError(ERROR_NULLCONTEXT);

        // Get the size of the result string.
        dwSize = ImmGetCompositionString(hIMC, GCS_RESULTSTR, NULL, 0);

        // Increase buffer size for NULL terminator;
        // maybe it is in unicode.
        dwSize += sizeof(WCHAR);

        lpstr = (LPSTR) LocalAlloc(LPTR, dwSize);
        if (lpstr == NULL)
             MyError(ERROR_GLOBALLOCK);

        // Get the result string that is generated by IME into lpstr.
        ImmGetCompositionString(hIMC, GCS_RESULTSTR, lpstr, dwSize);
        ImmReleaseContext(hWnd, hIMC);

        // Add this string into the text buffer of the application.

        LocalFree(lpstr);
    }


Working with Input Contexts

An input context stores information about the status of the IME. An input context is an internal structure maintained by the IME. The system creates and assigns a default input context to each thread. Within the thread, this default input context is a shared resource and is associated with each newly created window. Input contexts require considerable system resources, so use them conservatively.

To retrieve or set information in the IME, an application must first obtain a handle to the input context associated with a specified window. To obtain this handle, use the ImmGetContext function. You can use the returned handle in subsequent calls to the IMM functions to retrieve and set IME values, such as the Composition window style, the composition style, and the Status window position. The ImmNotifyIME function notifies the IME about changes to the input context. Once you are finished with the context, you must release it by using the ImmReleaseContext function.

Because the default input context is a shared resource, any changes that you make to it apply to all windows in the thread. However, you can override this default behavior by creating and associating your own input context for one or more windows in the thread. The changes that you make to your own input context apply only to the windows with which it is associated.

To create an input context, use the ImmCreateContext function. To assign an input context to a window, use the ImmAssociateContext function. ImmAssociateContext returns the handle of the previously associated input context. If you have not previously associated an input context with a window, the returned handle is for the default input context. When an application has finished with an input context that it has created, it calls ImmDestroyContext to free the memory that was allocated. It is the responsibility of the application to ensure that the input context being destroyed is not associated with a window. ImmAssociateContext is used to restore the original context of a window.

To create a new component that is a member of an input context structure, use the ImmCreateIMCC function. This function returns an input context component handle and initializes the component with 0s. To retrieve or change the size of the input context component, use the ImmGetIMCCSize and ImmReSizeIMCC functions. Use ImmDestroyIMCC to free the memory of an input context component.

To obtain a handle to an input context, an IME calls the ImmLockIMC function. To obtain a handle to an input context component that is a member of an input context, an IME calls the ImmLockIMCC function. With each call to these functions, the total number of handles, or the lock count, is incremented for the corresponding input context or input context component. To get the lock count of an input context or input context component, use the ImmGetIMCLockCount and ImmGetIMCCLockCount functions. To release a handle and decrement the lock count, use the ImmUnlockIMC or ImmUnlockIMCC function.

Once an input context is associated with a window, the system automatically selects that context when the window receives the input focus. The style and other information in the input context affects subsequent keyboard input for that window, and determines the appearance and operation of the IME.