Writing Win32 multi-lingual user interface applications
Windows 2000 has developed new enhanced support standards for global markets, offering many internationalization features, such as full Unicode support, preset support for hundreds of languages, and mirroring technology for right-to-left languages. Several articles have been published in Microsoft Systems Journal (MSJ) to illustrate how to write Unicode, complex scripts, and mirrored applications for Windows 2000来.
The Windows 2000 Multilingual User Interface (also known as MUI) is one of the most useful internationalization features in Windows 2000 that allows users to set the system's user interface (UI) language and change it to any localized language that Windows 2000 has published.
This article describes how to provide switchable UI functionality in your application. In this article, you will:
Learn why you should consider investing in multilingual applications.
See examples of three different ways to implement a Multilingual User interface.
Find best practices and guidelines and code examples for recommendations.
Contents of this page
Why bother writing multi-lingual applications?
Why use Unicode?
Implementing a Multilingual User interface
Summary
List of terms
Why bother writing multi-lingual applications?
There are several reasons why you need to write a multilingual application. The most obvious reason, of course, is to increase revenue and increase software sales by breaking into international markets (which, after all, are now in the internet era), with two strong points:
Publish a single binary file. by publishing a core functional binary to all platforms (and all different language versions), you can minimize the complexity and cost of development, thereby avoiding compilation conditions and maintenance of the source code for each language separately. Microsoft Windows 2000, Microsoft Office 2000, and Microsoft Internet Explorer 5.0 are single binaries – for example, US English, Japanese, and Arabic versions of Windows 200 0 are accompanied by the same core Gdi32.dll. Potential updates to this module in future Service packs can be applied to all languages without any other technical work.
avoid becoming your own competitor. delaying the release of software in different languages can seriously impact customer deployments and, in turn, affect your revenue. After publishing the English version of the application 1.0, it seems no big deal to let customers wait a few months to release the German version of 1.0. Many customers, however, wait until the application's first update is released before deployment. If you add an updated release increment to the original release increment, the delay to the customer deployment will be far beyond your expectations. Ensuring that your application is a multilingual application helps to avoid this situation.
Back to top of page
Why use Unicode?
It is important to know that regardless of which method is used to publish a localized product, the application must fully support Unicode. By implementing Unicode support, you will no longer have to worry about the language and code page support issues for the application running platform. Unicode is a 16-bit character encoding that represents most languages commonly used around the world, which is significantly different from old 8-bit character encodings (such as ANSI, which restricts language support to about 220 different characters). Unicode-enabled applications can process and display characters in any of these languages. To learn more about implementing Unicode and publishing a single binary file that will run on Windows 9x and Windows NT, see the article on developing applications using Microsoft Layer for Unicode:
Windows 2000 supports traditional ANSI applications through the system locale (shown in the Regional Options control Panel configuration, 1) option. This means that the behavior of an ANSI-based application depends entirely on the system settings (Figure 2). A better approach is to fully use Unicode character encoding.
Figure 1: The system locale is set by default in the Regional Options Control Panel ... button to configure the. |
Figure 2: ANSI localized message box running on a Windows 2000 system that does not match the application language in the system locale. |
Back to top of page
Implementing a Multilingual User interface
There are typically three ways to implement a Multilingual User interface binary:
Language-dependent binary files and built-in resources
A core binary file and an internationalized resource DLL
A core binary file and a resource DLL corresponding to each target language
Figure 3 shows the file distribution for internationalized applications in English, German, and Japanese using each of these methods.
Language-dependent binary files
A core binary file and an internationalized resource DLL
Language-based satellite DLLs
Figure 3: file Distribution Comparison
The following sections describe the advantages, disadvantages, and technical limitations of each implementation. The following is an important assumption: regardless of the language of the user interface, a core binary file is published and the core functionality of the application is the same. This should be the ultimate goal in all implementations of the Multilingual User interface.
Method 1: Language-dependent binary files
This traditional approach involves compiling a binary file with both source code and resources. A separate binary file is required for each target language.
| Summary of advantages and disadvantages of Method 1 (language-dependent binaries) |
| Advantages |
Disadvantages |
|
|
Separating the source tree required for each language
Resource changes require a full binary compilation
Switching UI requires a different application instance
Wasted disk space: Multiple copies of a core binary file
|
The disadvantage of a language-dependent binary file makes this method obsolete. The two approaches described below are more appropriate for applications that need to switch the UI.
Method 2: A resource DLL for all languages
The main idea of this approach is to isolate resources from source code and create a resource-only DLL that contains all the localized resources for all target languages. Multiple copies of the same resource ID are defined in the RC file under different language tags. In the following example, the string ID ids_enumstrtest is defined for French and English.
// French (France) resources
#ifdef _WIN32LANGUAGE LANG_FRENCH, SUBLANG_FRENCH
#pragma code_page(1252)
#endif //_WIN32
// String Table
STRINGTABLE DISCARDABLE
BEGINIDS_ENUMSTRTEST "Cette phrase est en français...(France)"
END
#endif // French (France) resources
// English (U.S.) resources
#ifdef _WIN32
LANGUAGE LANG_ENGLISH,SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif _win32
// String TableSTRINGTABLE DISCARDABLE
BEGINIDS_ENUMSTRTEST "This is an English string...(USA)"
END
#endif // English (U.S.) resources
To access resources at run time, the resource DLL is loaded through the LoadLibrary API. You can then use enumresourcelanguages to find a list of available languages for a given control/resource, and use FindResourceEx to determine the location of the resource with the specified type, name, and language. To display a given language, just select the correct resource in the DLL. The language can be toggled by reloading the newly selected resource and refreshing the client area.
It is important to note that the Windows resource loader always uses the default current user locale (the thread locale inherits the user locale of the currently logged-on user – see Figure 1). getthreadlocale allows querying thread locale settings. In this method, the pre-defined resources for the API are loaded (LoadIcon,LoadString,loadcursor... ) always returns the resources associated with this locale. For example, in the resource example above, if the system locale is set to English (0x0409), you will not be able to use LoadString to load French resources. But this is not really the case: the thread locale is inherited from the user locale when the thread is created, which means that in this case, you can use SetThreadLocale to change the thread locale to French (0x040c), and then call LoadString to get the French string.
// if our thread locale is english to start with ...
G_hinst = loadlibrary (_text ("Intl_res.dll"));
LoadString (G_HINST, IDS_ENUMSTRTEST,G_SZTEMP, MAX_STR);
// g_sztemp would then point to the english resources
// changing our thread locale to french.
// always make sure that french is in fact one of the
// valid Languages returned by enumresourcelanguages ()
// save a copy of the current thread-locale to set back later
Lcid = getthreadlocale (); SetThreadLocale (Makelcid (0x040c, sort_default));
LoadString (G_HINST, IDS_ENUMSTRTEST, G_SZTEMP, MAX_STR);
// g_sztemp would then Point to the French resources
It is important to note that if the thread locale is the same as the currently selected user locale, the system's resource loader will use language ID 0 (neutral) by default. This value is returned if the required resource is defined as a neutral language. Otherwise, all language resources (in the language ID order) are enumerated and the first matching resource ID (regardless of language) is returned.
For example, in the US English and French French resources, where the user locale is set to Japanese, the original thread locale will be Japanese (0x0411). If the string ID is not found and the enumeration is English (0x0409) before French (0x40c), the first call to LoadString will return the English resource after the 0x0411 version. Next, if the user locale is first French: The original thread locale will be French (0x040c). Because the user locale and thread locale match, the system resource loader uses language ID 0 and begins enumerating the resources and returns to the English version again!
The best workaround for this is to discard the change thread locale setting first. You can manually load resources from the multilingual resource file by calling FindResourceEx . Another workaround is to define the resource in the owner-defined language tag. In the RC example above, the English part is defined as:
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
This prevents the thread locale from being switched back to US English. However, the resource is defined as:
LANGUAGE LANG_ENGLISH, SUBLANG_NEUTRAL
The success of the SetThreadLocale call is guaranteed because the language ID does not have a matching user locale and continues to use the predefined resource loader APIs.
| Summary of pros and Cons of Method 2 (one resource DLL for all languages) |
| Advantages |
Disadvantages |
|
|
Updating to a new language is difficult
Installing a character set for the UI language is difficult
Unsupported languages can waste memory and disk space
There is no easy way to implement direct resources Loading interfaces (loadmenu,LoadString , etc.)
Unable to reroute a thread locale in Windows 9x
|
Method 3: One resource DLL per target language
This is an extension of the previous technology. Instead of including all languages in a single DLL, you are creating a separate DLL for each language. This method is used by Microsoft Office 2000, Microsoft Internet Explorer 5.x, and Microsoft Windows 2000. Each product is implemented differently, and the name of this method varies, but the satellite DLL is the common name for this method.
This method once again uses the resource DLL to be loaded through the LoadLibrary API. But this time, you must select the appropriate language DLL. It is generally assumed that the Windows UI language is the user's preferred language, and the application can be started by loading this language resource. Next, when the user chooses a different language, the current language is freed and the new language is loaded. Of course, this method re-creates the window and initializes the dialog box with the newly loaded resource.
The detection of the system UI language is handled differently in different versions of Windows:
In Windows 2000, you can use the getuserdefaultuilanguage API to find the current user's UI language (note that in the Windows 2000 MultiLanguage version, you can have different users who have selected different UI languages).
In Windows 95, Windows 98, and Windows 98 SE, the UI language is stored in the registry at the following location: HKCU\Control Panel\desktop\resourcelocale. This registry key returns the language ID (LangID) of the UI as a 16 binary, for example, English is 00000409.
In Windows NT 3.5x and Windows NT 4.0, because of the lack of relevant APIs and consistent registry entries, the safest way to check the operating system language is to check for NTDLL. The version stamp of the DLL. The language of this file is the same as the language of the user interface. Again, the only exception is Arabic, Hebrew, and Thai, where the version stamp helps you detect which operating system is enabled. The steps are as follows:
Load the Ntdll.dll file
Enumerating languages in a version stamp resource
If there are multiple languages available, it will be non-American English language
If you only find us English, you may still need to work with the enabled language, and you should check the active code page.
Because language detection APIs and registry keys return the LangID of the UI language, it is useful to name the satellite DLLs appropriately: Do not name the English file Res_eng.dll or Res_en.dll, but use Res409.dll.
The following code example shows how to detect the UI language and load the correct resource DLL in Windows 2000:
wLangId = GetUserDefaultUILanguage();
_stprintf(g_tcsTemp, _TEXT("res%x.dll"), wLangId);
if((hRes = LoadLibrary(g_tcsTemp)) == NULL)
{
// we didn‘t find the desired language satellite DLL, lets go with English (default).
hRes = LoadLibrary(_TEXT("res409.dll"));
}
If the appropriate language DLL could not be loaded (for example, only a subset of the operating system language is supported in the application), the only solution is to assume that there is a default/Preferred language DLL in the system.
Another possible approach is to include the English (or default language) resource in the executable master file and publish the satellite DLLs for the other languages (blending of methods 1 and 3). Because this method has no practical advantage, and English is just another localized language, this technique is no longer discussed in detail.
| Summary of advantages and disadvantages of Method 3 (one resource DLL per target language) |
| Advantages |
Disadvantages |
Allow Switch User interface
Allows for "no compilation" Resource updates
Full control of the installed language
Easily update to new language
Language-specific updates do not affect all languages
Don't worry about user, system, and thread locales
Can be implemented in Windows 9x, Windows NT, and Windows 2000
|
|
Back to top of page
Summary
Writing a single binary code is the first step towards a truly global, universal product and helps reduce development and support costs. Implementing a multi-lingual user interface to allow users to switch between all supported languages is the next logical step towards satisfying the global customer. You can achieve these goals by writing Unicode-aware code and creating satellite DLLs for language resources, which may be more relaxed than you might imagine.
Back to top of page
List of terms
thread Locale -the locale in which the given thread is located. is inherited from the current user locale at creation time, and can be changed to a valid any locale (by thread) at run time. By calling the NLS API, you can use this locale to format numbers, dates, times ...
system Locale -not a true regional setting. Determines which scripting non-Unicode applications will be supported. So, in the application perspective, it is the locale that the system simulates. The system locale is scoped to the entire system and therefore applies to all users. Changing the system locale requires a restart.
UI Language -the language that the operating system uses to display its menus, Help files, and dialog boxes.
user Locale -the user's preferences when formatting items such as dates, currencies, numbers, and so on. The user locale is set for each user and does not require a restart or logoff/logon.
Windows official multi-language scenario