locale - Microsoft

advertisement
1
Developing Middle East Applications Using Microsoft Visual C++
Introduction
[This Is an Initial Version Updates Could Be Available in later Stages]
In the following paper we will highlight some of the most important issues
concerning developing a Localized or Globalized version of the applications
developed using the different tools supplied with Microsoft Visual Studio
Package. This paper will be stressing on these topics included in Microsoft
Visual C++ ver. 6.0 (included in MS Visual Studio 6.0), and some general
terms and definitions that are commonly used in the areas of Localization and
Globalization of application. More emphasis on Arabic language will be applied
when considering language specific programming tips.
If you have any inquiries or a bug list you wish to submit please contact us
through our e-mail:
vsarabic@microsoft.com
Microsoft Visual Studio Team
2
Best Practices for Globalization
Globalization is the practice of designing and implementing software that is not locale
dependent, i.e., can accommodate any locale. In software design, a locale is defined as a set
of user preferences associated with a user’s language. A locale in Windows 2000 includes
formats for date, time, currency and numbers; rules and tables for sorting and comparison;
and tables of character classifications.
Other user preferences that a globalized application should accommodate include userinterface language, default font selection, language rules for use in spell checking and
grammar, and input methods such as keyboard layouts and input method editors.
Guidelines for developing a globalized application include the following:
Technical Considerations
1. Use Unicode as your character encoding to represent text. If your application must
also run on Windows 9x, consider a method for using Unicode on those platforms, as
described in recent Microsoft Systems Journal articles. If you cannot use Unicode for
whatever reason, you will need to implement DBCS enabling, BiDi enabling, codepage
switching, text tagging, etc. Guidelines related to this functionality are available
elsewhere (see for example, http://www.microsoft.com/globaldev).
2. Consider multilingual user interface: launch the application in the default user
interface language, and offer the option to change to other languages.
3. Use the Win32 API NLS functions to handle locale sensitive data.
4. Watch for windows messages that indicate changes in the input language, and use
that information for spell checking, font selection, etc.
5. Use the Script APIs (Uniscribe) to layout formatted text on a page. This will allow
your application to display multilingual text and complex scripts such as Arabic,
Hebrew, Hindi, Tamil, and Thai.
6. Test your application on all language variants of Windows 2000, using all possible
locales.
Microsoft Visual Studio Team
3
Cultural and Political Considerations
1. Avoid slang expressions, colloquialisms, and obscure phrasing in all text. At best they
are difficult to translate; at worst they are offensive.
2. Avoid images in bitmaps and icons that are ethno-centric or offensive in other
cultures. For example, the US style mailbox is sometimes used to indicate an
electronic mailbox, but many Europeans have no idea what it is (they think it looks
like a tunnel).
3. Avoid maps that include controversial national boundaries – they are a notorious
source of political offense.
Best Practices for Localizability
In contrast to globalization, localization is the process of modifying an application so that its
user interface is in the language of the user. Well designed software can be localized to any
of the languages supported by Windows 2000 without changes to the source code, i.e.,
without compilation. In addition to the guidelines for globalization mentioned above, those for
localizability include the following:
1. Isolate all user interface elements from the program source code. Put them in
resource files, message files or a private database.
2. Use the same resource identifiers throughout the life of the project. Changing
identifiers makes it difficult to update localized resources from one build to another.
3. Make multiple copies of the same string if it is used in multiple contexts. The same
string may have different translations in different contexts.
4. Do not place strings that should not be localized in resources. Leave them as string
constants in the source code.
5. Allocate text buffers dynamically, since text size may expand when translated. If you
must use static buffers, make them extra large to accommodate localized strings
(e.g., double the English string length).
6. Keep in mind that dialog boxes may expand due to localization. Thus, a large dialog
box that occupies the entire screen in low-resolution mode may have to be resized to
an unusable size when localized.
7. Avoid text in bitmaps and icons, as these are difficult to localize.
8. Do not create a text message dynamically at runtime, either by concatenating
multiple strings or by removing characters from static text. Word order varies by
language, so dynamic composition of text in this manner requires code changes to
localize to some languages.
Microsoft Visual Studio Team
4
9. Similarly, avoid composing text using multiple insertion parameters in a format string
(e.g., in sprintf or wsprintf), because the order of insertion of the arguments changes
when translated to some languages.
10. If localizing to a Middle Eastern language such as Arabic or Hebrew, use the right-toleft layout APIs to layout your application right to left. See the Microsoft Systems
Journal article for details.
11. Test localized applications on all language variants of Windows 2000. If your
application uses Unicode, as recommended, it should run fine with no modifications.
If it uses a Windows codepage you will need to set the system locale to the
appropriate value for your localized application, and reboot, before testing.
Microsoft Visual Studio Team
5
Locales and Code Pages
A “locale” reflects the local conventions and language for a particular geographical
region. A given language may be spoken in more than one country; for example, Portuguese
is spoken in Brazil as well as in Portugal. Conversely, a country may have more than one
official language. For example, Canada has two: English and French. Thus, Canada has two
distinct locales: Canadian-English and Canadian French. Some locale-dependent categories
include the formatting of dates and the display format for monetary values.
The language determines the text and data formatting conventions, while the country
determines the national conventions. Every language has a unique mapping, represented by
“code pages,” which includes characters other than those in the alphabet (such as
punctuation marks and numbers). A code page is a character set and is related to the current
locale and language. As such, a locale is a unique combination of language, country, and
code page. The code page setting can determine the locale setting and can be changed at
run time by calling the setlocale function.
Different languages may use different code pages. For example, the ANSI code page 1252 is
used for American English and most European languages, and the ANSI code page 932 is
used for Japanese Kanji. Virtually all code pages share the ASCII character set for the lowest
128 characters (0x00 to 0x7F).
Any single-byte code page can be represented in a table (with 256 entries) as a mapping of
byte values to characters (including numbers and punctuation marks), or glyphs. Any
multibyte code page can also be represented as a very large table (with 64K entries) of
double-byte values to characters. In practice, however, it are usually represented as a table
for the first 256 (single-byte) characters and as ranges for the double-byte values.
The C run-time library has two types of internal code pages: locale and multibyte. You can
change the current code page during program execution (see the documentation for the
setlocale and _setmbcp functions). Also, the run-time library may obtain and use the value
of the operating system code page. In Windows NT, the operating system code page is the
“system default ANSI” code page. This code page is constant for the duration of the
program’s execution.
When the locale code page changes, the behavior of the locale-dependent set of functions
changes to that dictated by the chosen code page. By default, all locale-dependent functions
begin execution with a locale code page unique to the “C” locale. You can change the internal
locale code page (as well as other locale-specific properties) by calling the setlocale
function. A call to setlocale(LC_ALL, "") will set the locale to that indicated by the operating
system’s default code page.
Similarly, when the multibyte code page changes, the behavior of the multibyte functions
changes to that dictated by the chosen code page. By default, all multibyte functions begin
Microsoft Visual Studio Team
6
execution with a multibyte code page corresponding to the operating system’s default code
page. You can change the internal multibyte code page by calling the _setmbcp function.
The C run-time function setlocale sets, changes, or queries some or all of the current
program’s locale information. The _wsetlocale routine is a wide-character version of
setlocale; the arguments and return values of _wsetlocale are wide-character strings.
Microsoft Visual Studio Team
7
SETLOCALE, _WSETLOCALE
Define the locale
char *setlocale( int category, const char *locale );
wchar_t *_wsetlocale( int category, const wchar_t *locale );
Routine
Required Header
Compatibility
setlocale
<locale.h>
ANSI, Win 95, Win NT
_wsetlocale
<locale.h> or <wchar.h>
Win 95, Win NT
For additional compatibility information, see Compatibility in the Introduction.
Libraries
LIBC.LIB
Single thread static library, retail version
LIBCMT.LIB
Multithread static library, retail version
MSVCRT.LIB
Import library for MSVCRT.DLL, retail version
Return Value
If a valid locale and category are given, the function returns a pointer to the string associated
with the specified locale and category. If the locale or category is invalid, the function returns
a null pointer and the current locale settings of the program are not changed.
For example, the call
setlocale( LC_ALL, "English" );
sets all categories, returning only the string English_USA.1252. If all categories are not
explicitly set by a call to setlocale, the function returns a string indicating the current setting
of each of the categories, separated by semicolons. If the locale argument is a null pointer,
setlocale returns a pointer to the string associated with the category of the program’s
locale; the program’s current locale setting is not changed.
The null pointer is a special directive that tells setlocale to query rather than set the
international environment. For example, the sequence of calls
Microsoft Visual Studio Team
8
// Set all categories and return "English_USA.1252"
setlocale( LC_ALL, "English" );
// Set only the LC_MONETARY category and return "French_France.1252"
setlocale( LC_MONETARY, "French" );
setlocale( LC_ALL, NULL );
returns
LC_COLLATE=English_USA.1252;
LC_CTYPE=English_USA.1252;
LC_MONETARY=French_France.1252;
LC_NUMERIC=English_USA.1252;
LC_TIME=English_USA.1252
which is the string associated with the LC_ALL category.
You can use the string pointer returned by setlocale in subsequent calls to restore that part
of the program’s locale information, assuming that your program does not alter the pointer or
the string. Later calls to setlocale overwrite the string; you can use _strdup to save a
specific locale string.
Parameters
category
Category affected by locale
locale
Locale name
Remarks
Use the setlocale function to set, change, or query some or all of the current program locale
information specified by locale and category. “Locale” refers to the locality (country and
language) for which you can customize certain aspects of your program. Some localedependent categories include the formatting of dates and the display format for monetary
values.
_wsetlocale is a wide-character version of setlocale; the locale argument and return value
of _wsetlocale are wide-character strings. _wsetlocale and setlocale behave identically
otherwise.
Microsoft Visual Studio Team
9
Generic-Text Routine Mappings
TCHAR.H
_UNICODE & _MBCS
Routine
Not Defined
_tsetlocale
setlocale
_MBCS Defined
_UNICODE Defined
setlocale
_wsetlocale
The category argument specifies the parts of a program’s locale information that are affected.
The macros used for category and the parts of the program they affect are as follows:
LC_ALL
All categories, as listed below
LC_COLLATE
The strcoll, _stricoll, wcscoll, _wcsicoll, and strxfrm functions
LC_CTYPE
The character-handling functions (except isdigit, isxdigit, mbstowcs, and mbtowc, which
are unaffected)
LC_MONETARY
Monetary-formatting information returned by the localeconv function
LC_NUMERIC
Decimal-point character for the formatted output routines (such as printf), for the dataconversion routines,
and
for
the
nonmonetary-formatting
information returned
by
localeconv
LC_TIME
The strftime and wcsftime functions
The locale argument is a pointer to a string that specifies the name of the locale. If locale
points to an empty string, the locale is the implementation-defined native environment. A
value of “C” specifies the minimal ANSI conforming environment for C translation. The “C”
locale assumes that all char data types are 1 byte and that their value is always less than
256. The “C” locale is the only locale supported in Microsoft Visual C++ version 1.0 and
earlier versions of Microsoft C/C++. Microsoft Visual C++ supports all the locales listed in
Language and Country Strings. At program startup, the equivalent of the following
statement is executed:
setlocale( LC_ALL, "C" );
The locale argument takes the following form:
locale :: "lang[_country[.code_page]]"
| ".code_page"
Microsoft Visual Studio Team
10
| ""
| NULL
The set of available languages, countries, and code pages includes all those supported by the
Win32 NLS API. The set of language and country codes supported by setlocale is listed in
Appendix A, Language and Country Strings.
If locale is a null pointer, setlocale queries, rather than sets, the international environment,
and returns a pointer to the string associated with the specified category. The program’s
current locale setting is not changed. For example,
setlocale( LC_ALL, NULL );
returns the string associated with category.
The following examples pertain to the LC_ALL category. Either of the strings ".OCP" and
".ACP" can be used in place of a code page number to specify use of the system default OEM
code page and system-default ANSI code page, respectively.
setlocale( LC_ALL, "" );
Sets the locale to the default, which is the system-default ANSI code page obtained from the
operating system.
setlocale( LC_ALL, ".OCP" );
Explicitly sets the locale to the current OEM code page obtained from the operating system.
setlocale( LC_ALL, ".ACP" );
Sets the locale to the ANSI code page obtained from the operating system.
setlocale( LC_ALL, "[lang_ctry]" );
Sets the locale to the language and country indicated, using the default code page obtained
from the host operating system.
setlocale( LC_ALL, "[lang_ctry.cp]" );
Sets the locale to the language, country, and code page indicated in the [ lang_ctry.cp] string.
You can use various combinations of language, country, and code page. For example:
setlocale( LC_ALL, "French_Canada.1252" );
// Set code page to French Canada ANSI default
setlocale( LC_ALL, "French_Canada.ACP" );
// Set code page to French Canada OEM default
setlocale( LC_ALL, "French_Canada.OCP" );
setlocale( LC_ALL, "[lang]" );
Microsoft Visual Studio Team
11
Sets the locale to the country indicated, using the default country for the language specified,
and the system-default ANSI code page for that country as obtained from the host operating
system. For example, the following two calls to setlocale are functionally equivalent:
setlocale( LC_ALL, "English" );
setlocale( LC_ALL, "English_United States.1252" );
setlocale( LC_ALL, "[.code_page]" );
Sets the code page to the value indicated, using the default country and language (as defined
by the host operating system) for the specified code page.
The category must be either LC_ALL or LC_CTYPE to effect a change of code page. For
example, if the default country and language of the host operating system are “United
States” and “English,” the following two calls to setlocale are functionally equivalent:
setlocale( LC_ALL, ".1252" );
setlocale( LC_ALL, "English_United States.1252");
For more information see the setlocale pragma in Preprocessor Reference.
Microsoft Visual Studio Team
12
Example
/* LOCALE.C: Sets the current locale to "Germany" using the
* setlocale function and demonstrates its effect on the strftime
* function.
*/
#include <stdio.h>
#include <locale.h>
#include <time.h>
void main(void)
{
time_t ltime;
struct tm *thetime;
unsigned char str[100];
setlocale(LC_ALL, "German");
time (&ltime);
thetime = gmtime(&ltime);
/* %#x is the long date representation, appropriate to
* the current locale
*/
if (!strftime((char *)str, 100, "%#x",
(const struct tm *)thetime))
printf("strftime failed!\n");
else
printf("In German locale, strftime returns '%s'\n",
str);
Microsoft Visual Studio Team
13
/* Set the locale back to the default environment */
setlocale(LC_ALL, "C");
time (&ltime);
thetime = gmtime(&ltime);
if (!strftime((char *)str, 100, "%#x",
(const struct tm *)thetime))
printf("strftime failed!\n");
else
printf("In 'C' locale, strftime returns '%s'\n",
str);
}
Output
In German locale, strftime returns 'Donnerstag, 22. April 1993'
In 'C' locale, strftime returns 'Thursday, April 22, 1993'
Microsoft Visual Studio Team
14
Locale Constants
Use the setlocale function to change or query some or all of the current program
locale information. “Locale” refers to the locality (the country and language) for which you
can customize certain aspects of your program. Some locale-dependent categories include
the formatting of dates and the display format for monetary values. For more information,
see Locale Categories.
Locale-Dependent Routines
setlocale Category
Routine
atof, atoi, atol
Use
Setting Dependence
Convert character to floating-point,
integer,
or
long
integer
LC_NUMERIC
value,
respectively
is Routines
Test
given
integer
for
particular
LC_CTYPE
condition.
isleadbyte
Test for lead byte ()
LC_CTYPE
localeconv
Read appropriate values for formatting
LC_MONETARY,
numeric quantities
LC_NUMERIC
Maximum length in bytes of any
LC_CTYPE
MB_CUR_MAX
multibyte character in current locale
(macro defined in STDLIB.H)
_mbccpy
Copy one multibyte character
LC_CTYPE
_mbclen
Return length, in bytes, of given
LC_CTYPE
multibyte character
Mblen
Validate and return number of bytes in
LC_CTYPE
multibyte character
_mbstrlen
For
multibyte-character
validate
each character
strings:
LC_CTYPE
in string;
return string length
mbstowcs
Convert
sequence
of
multibyte
LC_CTYPE
Microsoft Visual Studio Team
15
characters to corresponding sequence
of wide characters
mbtowc
Convert
multibyte
character
to
LC_CTYPE
corresponding wide character
printf functions
Write formatted output
LC_NUMERIC (determines
radix character output)
scanf functions
Read formatted input
LC_NUMERIC (determines
radix character recognition)
setlocale,
Select locale for program
Not applicable
strcoll, wcscoll
Compare characters of two strings
LC_COLLATE
_stricoll, _wcsicoll
Compare characters of two strings
LC_COLLATE
_wsetlocale
(case insensitive)
_strncoll,
Compare first n characters of two
_wcsncoll
strings
_strnicoll,
Compare first n characters of two
_wcsnicoll
strings (case insensitive)
strftime, wcsftime
Format date and time value according
LC_COLLATE
LC_COLLATE
LC_TIME
to supplied format argument
_strlwr
Convert, in place, each uppercase
LC_CTYPE
letter in given string to lowercase
strtod,
wcstod,
strtol,
wcstol,
Convert character string to double,
LC_NUMERIC (determines
long, or unsigned long value
radix character recognition)
Convert, in place, each lowercase
LC_CTYPE
strtoul, wcstoul
_strupr
letter in string to uppercase
strxfrm, wcsxfrm
Transform string into collated form
LC_COLLATE
according to locale
tolower, towlower
Convert
given
character
to
LC_CTYPE
Microsoft Visual Studio Team
16
corresponding lowercase character
toupper, towupper
Convert
given
character
to
LC_CTYPE
Convert sequence of wide characters
LC_CTYPE
corresponding uppercase letter
wcstombs
to
corresponding
sequence
of
multibyte characters
wctomb
Convert
wide
character
to
LC_CTYPE
corresponding multibyte character
_wtoi, _wtol
Convert wide-character string to int or
LC_NUMERIC
long
Microsoft Visual Studio Team
17
Locale Categories
#include <locale.h>
Remarks
Locale categories are manifest constants used by the localization routines to specify
which portion of a program's locale information will be used. The locale refers to the locality
(or country) for which certain aspects of your program can be customized. Locale-dependent
areas include, for example, the formatting of dates or the display format for monetary values.
Locale Category
Parts of Program Affected
LC_ALL
All locale-specific behavior (all categories)
LC_COLLATE
Behavior of strcoll and strxfrm functions
LC_CTYPE
Behavior of character-handling functions (except isdigit,
isxdigit, mbstowcs, and mbtowc, which are unaffected)
LC_MAX
Same as LC_TIME
LC_MIN
Same as LC_ALL
LC_MONETARY
Monetary formatting information returned by the localeconv
function
LC_NUMERIC
Decimal-point character for formatted output routines (for
example, printf), data conversion routines, and nonmonetary
formatting information returned by localeconv function
LC_TIME
Behavior of strftime function
Microsoft Visual Studio Team
18
Localization of MFC Components
This part describes some of the designs and procedures you can use to localize your
component, be it an application or an OLE control, or a DLL, which uses MFC.
Overview
There are really two issues to resolve when localizing a component, which uses MFC. First of
all, you must localize your own resources – strings, dialogs, and other resources that are
specific to your component. Most components built using MFC also include and use a number
of resources that are defined by MFC. You must provide localized MFC resources as well.
Fortunately, several languages are already provided by MFC itself.
In addition, your component should be prepared to run in its target environment (European,
or DBCS enabled environment). For the most part, this depends on your application treating
characters with the high bit set correctly and handling strings with double byte characters.
MFC is enabled, by default, for both of these environments, such that it is possible to have a
single “world wide” binary that is used on all platforms with just different resources plugged
in at setup time.
Localizing your Component’s Resources
Localizing your application or DLL should involve simply replacing the resources with
resources that match the target language. For your own resources, this is relatively simple:
edit the resources in the resource editor and build your application. If your code is written
properly there will be no strings or text that you wish to localize hard-coded into your C++
source code, all localization can be done by simply modifying resources. In fact, you can
implement your component such that all providing a localized version does not even involve a
build of the original code. This is more complex, but is well worth it and is the mechanism
chosen for MFC itself. It is also possible to localize an application by loading the EXE or DLL
file into the resource editor and editing the resources directly. While this is possible, it is
requires re-application of those changes each time you build a new version of your
application.
One way to avoid that is to locate all resources in a separate DLL, sometimes called a satellite
DLL. This DLL is then loaded dynamically at runtime and the resources are loaded from that
DLL instead of from the main module with all your code. MFC directly supports this approach.
Consider an application called MYAPP.EXE; it could have all of its resources located in a DLL
called MYRES.DLL. In the application’s InitInstance it would perform the following to load
that DLL and cause MFC to load resources from that location:
Microsoft Visual Studio Team
19
CMyApp::InitInstance ()
{
// One of the first things in the init code
HINSTANCE hInst = LoadLibrary(“myres.dll”);
if (hInst != NULL)
AfxSetResourceHandle(hInst);
// other initialization code would follow
.
.
.
}
From then on, MFC will load resources from that DLL instead of from myapp.exe. All
resources, however, must be present in that DLL – MFC will not search the application’s
instance in search of a given resource. This technique applies equally well to regular DLLs as
well as OLE Controls. Your setup program would copy the appropriate version of MYRES.DLL
depending upon which resource locale the user would like.
It is relatively easy to create a resource only DLL. You create a DLL project, add your .RC file
to it, and add the necessary resources. If you have an existing project that does not use this
technique, you can copy the resources from that project. After adding the resource file to the
project you are almost ready to build the project. The only thing you must do is set the linker
options to include /NOENTRY. This tells the linker that the DLL has no entry point – since it
has no code, it has no entry point.
Note The resource editor in Visual C++ 4.0 and later supports multiple languages per .RC
file. This can make it very easy to manage your localization in a single project. Preprocessor
directives generated by the resource editor control the resources for each language.
Using the Provided MFC Localized Resources
Any MFC application that you build re-uses two things from MFC: code and resources. That is,
MFC has various error messages, built-in dialogs, and other resources that are used by the
MFC classes. In order to completely localize your application, you need to localize not only
your application’s resources, but also the resources, which come directly from MFC. MFC
provides a number of different language resource files automatically, so that if the language
you are targeting is one of the languages MFC already supports, you just need to make sure
you use those localized resources.
As of this writing MFC supports: Chinese, German, Spanish, French, Italian, Japanese, and
Korean. The files which contain these localized versions are in the MFC\INCLUDE\L.* (the ‘L’
stands for localized) directories. The German files are in MFC\INCLUDE\L.DEU, for example.
Microsoft Visual Studio Team
20
In order to cause your application to use these RC files instead of the files located in
MFC\INCLUDE, simply add a /IC:\PROGRAM FILES\DEVSTUDIO\VC\MFC\INCLUDE\L.DEU
to your RC command line (this is just an example – you would need to substitute your locale
of choice as well as the directory into which you installed Visual C++).
The above instructions will work if your application links statically with MFC. Most
applications link dynamically (because that is the AppWizard default). In this
scenario, not only the code is dynamically linked – so are the resources. As a
result, you can localize your resources in your application, but the MFC
implementation resources will still be loaded from the MFC4x.DLL (or a later
version) or from MFC4xLOC.DLL if it exists. You can approach this from two
different angles.
Feature Only in Professional and Enterprise Editions
Static linking to MFC is supported only in Visual C++ Professional and Enterprise
Editions. For more information, see Visual C++ Editions.
The more complex approach is to ship one of the localized MFC4xLOC.DLLs (such
as MFC4xDEU, for German, MFC4xESP.DLL for Spanish, etc.), or a later version,
and install the appropriate MFC4xLOC.DLL into the system directory when the
user installs your application. This can be very complex for both the developer
and the end user and as such is not recommended. See Technical Note 56 for
more information on this technique and its caveats.
The simplest and safest approach is to include the localized MFC resources in your
application or DLL itself (or its satellite DLL if you are using one). This avoids the
problems of installing MFC4xLOC.DLL properly. To do so, you follow the same
instructions for the static case given above (setting the RC command line properly
to point to the localized resources), except that you must also remove the
/D_AFXDLL define that was added by AppWizard. When /D_AFXDLL is defined,
AFXRES.H (and the other MFC RC files) don’t actually define any resources
(because they will be pulled from the MFC DLLs instead).
Microsoft Visual Studio Team
21
ActiveX Controls: Localizing an ActiveX Control
This article discusses procedures for localizing ActiveX control interfaces.
If you want to adapt an ActiveX control to an international market, you may want to localize
the control. Windows supports several languages in addition to the default English, including
German, French, and Swedish. This can present problems for the control if its interface is in
English only.
In general, ActiveX controls should always base their locale on the ambient LocaleID
property. There are three ways to do this:

Load resources, always on demand, based on the current value of the ambient
LocaleID property. The MFC ActiveX controls sample LOCALIZE, listed under
CONTROLS, uses this strategy.

Load resources when the first control is instanced, based on the ambient LocaleID
property, and use these resources for all other instances. This article demonstrates
this strategy.

Note This will not work correctly in some cases, if future instances have different
locales.

Use the OnAmbientChanged notification function to dynamically load the proper
resources for the container’s locale.
Note This will work for the control, but the run-time DLL will not dynamically update its
own resources when the ambient LocaleID property changes. In addition, run-time DLLs for
ActiveX controls use the thread locale to determine the locale for its resources.
The rest of this article describes two localizing strategies. The first strategy localizes the
control’s programmability interface (names of properties, methods, and events). The second
strategy localizes the control’s user interface, using the container’s ambient LocaleID
property. For a demonstration of control localization, see the MFC ActiveX controls sample
LOCALIZE, listed under CONTROLS.
Microsoft Visual Studio Team
22
Localizing the Control’s Programmability Interface
When localizing the control’s programmability interface (the interface used by programmers
writing applications that use your control), you must create a modified version of the control
.ODL file (a script for building the control type library) for each language you intend to
support. This is the only place you need to localize the control property names.
When you develop a localized control, include the locale ID as an attribute at the type library
level. For example, if you want to provide a type library with French localized property
names, make a copy of your SAMPLE.ODL file, and call it SAMPLEFR.ODL. Add a locale ID
attribute to the file (the locale ID for French is 0x040c), similar to the following:
[ uuid(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx), version(1.0), lcid(0x040c) ]
Change the property names in SAMPLEFR.ODL to their French equivalents, and then use
MKTYPLIB.EXE to produce the French type library, SAMPLEFR.TLB.
To create multiple localized type libraries you can add any localized .ODL files to the project
and they will be built automatically.
To add an .ODL file to your ActiveX control project
1. With your control project open, on the Project menu, click Add to Project and then
Files.
2. The Insert Files Into Project dialog box appears.
3. If necessary, select the drive and directory to view.
4. In the Files of Type box, select Interface Definition Files (*.odl,*.idl).
5. In the file list box, double-click the .ODL file you want to insert into the project.
6. Close the Insert Files Into Project dialog box when you have added all necessary
.ODL files.
Because the files have been added to the project, they will be built when the rest of the
project is built. The localized type libraries are located in the current ActiveX control project
directory.
Within your code, the internal property names (usually in English) are always used and are
never localized. This includes the control dispatch map, the property exchange functions, and
your property page data exchange code.
Only one type library (.TLB) file may be bound into the resources of the control
implementation (.OCX) file. This is usually the version with the standardized (typically,
English) names. To ship a localized version of your control you need to ship the .OCX (which
has already been bound to the default .TLB version) and the .TLB for the appropriate locale.
This means that only the .OCX is needed for English versions, since the correct .TLB has
Microsoft Visual Studio Team
23
already been bound to it. For other locales, the localized type library also must be shipped
with the .OCX.
To ensure that clients of your control can find the localized type library, register your localespecific .TLB file(s) under the TypeLib section of the Windows system registry. The third
parameter (normally optional) of the AfxOleRegisterTypeLib function is provided for this
purpose. The following example registers a French type library for an ActiveX control:
STDAPI DllRegisterServer(void)
{
AFX_MANAGE_STATE(_afxModuleAddrThis);
if (!AfxOleRegisterTypeLib(AfxGetInstanceHandle(), _tlid))
return ResultFromScode(SELFREG_E_TYPELIB);
AfxOleRegisterTypeLib(AfxGetInstanceHandle(), _tlid,
_T("samplefr.tlb"))
if (!COleObjectFactoryEx::UpdateRegistryAll(TRUE))
return ResultFromScode(SELFREG_E_CLASS);
return NOERROR;
}
When your control is registered, the AfxOleRegisterTypeLib function
automatically looks for the specified .TLB file in the same directory as the control
and registers it in the Windows registration database. If the .TLB file isn’t found,
the function has no effect.
Localizing the Control’s User Interface
To localize a control’s user interface, place all of the control’s user-visible resources (such as
property pages and error messages) into language-specific resource DLLs. You then can use
the container’s ambient LocaleID property to select the appropriate DLL for the user’s locale.
The following code example demonstrates one approach to locate and load the resource DLL
for a specific locale. This member function, called GetLocalizedResourceHandle for this
example, can be a member function of your ActiveX control class:
HINSTANCE CSampleCtrl::GetLocalizedResourceHandle(LCID lcid)
{
LPCTSTR lpszResDll;
HINSTANCE hResHandle = NULL;
LANGID lang = LANGIDFROMLCID(lcid);
switch (PRIMARYLANGID(lang))
{
Microsoft Visual Studio Team
24
case LANG_ENGLISH:
lpszResDll = “myctlen.dll”;
break;
case LANG_FRENCH:
lpszResDll = “myctlfr.dll”;
break;
case LANG_GERMAN:
lpszResDll = “myctlde.dll”;
break;
case 0:
default:
lpszResDll = NULL;
}
if (lpszResDll != NULL)
hResHandle = LoadLibrary(lpszResDll);
#ifndef _WIN32
if(hResHandle <= HINSTANCE_ERROR)
hResHandle = NULL;
#endif
return hResHandle;
}
Microsoft Visual Studio Team
25
Note that the sub language ID could be checked in each case of the switch statement, to
provide more specialized localization (for example, local dialects of German). For a
demonstration of this function, see the GetResourceHandle function in the MFC ActiveX
controls sample LOCALIZE, listed under CONTROLS.
When
the
control
first
loads
itself
into
a
container,
it
can
call
COleControl::AmbientLocaleID to retrieve the locale ID. The control can then pass the
returned locale ID value to the GetLocalizedResourceHandle function, which loads the
proper resource library. The control should pass the resulting handle, if any, to
AfxSetResourceHandle:
m_hResDll = GetLocalizedResourceHandle( AmbientLocaleID() );
if (m_hResDll != NULL)
AfxSetResourceHandle(m_hResDll);
Microsoft Visual Studio Team
26
Place the code sample above into a member function of the control, such as an override of
COleControl::OnSetClientSite. In addition, m_hResDLL should be a member variable of
the control class.
You can use similar logic for localizing a control’s property page. To localize the property
page, add code similar to the following sample to your property page’s implementation file (in
an override of COlePropertyPage::OnSetPageSite):
LPPROPERTYPAGESITE pSite;
LCID lcid = 0;
if((pSite = GetPageSite()) != NULL)
pSite->GetLocaleID(&lcid);
HINSTANCE hResource = GetLocalizedResourceHandle(lcid);
HINSTANCE hResourceSave = NULL;
if (hResource != NULL)
{
hResourceSave = AfxGetResourceHandle();
AfxSetResourceHandle(hResource);
}
// Load dialog template and caption string.
COlePropertyPage::OnSetPageSite( );
if (hResource != NULL)
AfxSetResourceHandle(hResourceSave);
Microsoft Visual Studio Team
27
locale
class locale {
public:
class facet;
class id;
typedef int category;
static const category none, collate, ctype, monetary,
numeric, time, messages, all;
locale();
explicit locale(const char *s);
locale(const locale& x, const locale& y,
category cat);
locale(const locale& x, const char *s, category cat);
bool operator()(const string& lhs,
const string& rhs) const;
string name() const;
bool operator==(const locale& x) const;
bool operator!=(const locale& x) const;
static locale global(const locale& x);
static const locale& classic();
};
The class describes a locale object that encapsulates a locale. It represents culture-specific
information as a list of facets. A facet is a pointer to an object of a class derived from class
facet that has a public object of the form:
static locale::id id;
You can define an open-ended set of these facets. You can also construct a locale object that
designates an arbitrary number of facets.
Microsoft Visual Studio Team
28
Predefined groups of these facets represent the locale categories traditionally managed in the
Standard C library by the function setlocale.
Category collate (LC_COLLATE) includes the facets:
collate<char>
collate<wchar_t>
Category ctype (LC_CTYPE) includes the facets:
ctype<char>
ctype<wchar_t>
codecvt<char, char, mbstate_t>
codecvt<wchar_t, char, mbstate_t>
Category monetary (LC_MONETARY) includes the facets:
moneypunct<char, false>
moneypunct<wchar_t, false>
moneypunct<char, true>
moneypunct<wchar_t, true>
money_get<char, istreambuf_iterator<char> >
money_get<wchar_t, istreambuf_iterator<wchar_t> >
money_put<char, ostreambuf_iterator<char> >
money_put<wchar_t, ostreambuf_iterator<wchar_t> >
Category numeric (LC_NUMERIC) includes the facets:
num_get<char, istreambuf_iterator<char> >
num_get<wchar_t, istreambuf_iterator<wchar_t> >
num_put<char, ostreambuf_iterator<char> >
num_put<wchar_t, ostreambuf_iterator<wchar_t> >
numpunct<char>
numpunct<wchar_t>
Category time (LC_TIME) includes the facets:
Microsoft Visual Studio Team
29
time_get<char, istreambuf_iterator<char> >
time_get<wchar_t, istreambuf_iterator<wchar_t> >
time_put<char, ostreambuf_iterator<char> >
time_put<wchar_t, ostreambuf_iterator<wchar_t> >
Category messages (LC_MESSAGE) includes the facets:
messages<char>
messages<wchar_t>
(The last category is required by Posix, but not the C Standard.)
Some of these predefined facets are used by the iostreams classes to control the conversion
of numeric values to and from text sequences
An object of class locale also stores a locale name as an object of class string. Using an
invalid locale name to construct a locale facet or a locale object throws an object of class
runtime_error. If the stored locale name is "*", no C-style locale corresponds exactly to that
represented by the object. Otherwise, you can establish a matching locale within the
Standard C library by calling setlocale( LC_ALL, x.name. c_str()).
In this implementation, you can also call the static member function:
static locale empty();
to construct a locale object that has no facets. It is also a transparent locale -- the
template function use_facet consults the global locale if it cannot find the requested facet in
a transparent locale. Thus, you can write:
cout.imbue(locale::empty());
Subsequent insertions to cout are mediated by the current state of the global locale. You can
even write:
locale loc(locale::empty(), locale("C"), locale::numeric);
cout.imbue(loc);
Numeric formatting rules remain the same as in the C locale even as the global locale
supplies changing rules for inserting dates and monetary amounts.
Microsoft Visual Studio Team
Download