Internationalization and localization tools


Locale-Sensitive Visual Basic Methods and Properties

  • I18n Overview
  • I18n Support for Dates and Times
  • Win32 API calls from Visual Basic
  • Locale-Sensitive Visual Basic Method List
  • Win32 Locale Functions
  • Locale-Sensitive Win32 Functions
  • Locale-Sensitive Win32 Character Set Names
  • I18n Overview

    Although Visual Basic employs UTF-16 Unicode (technically UCS-2) as its base technology for strings, it often converts these Unicode Strings to what Microsoft calls "ANSI" form and back again. This can cause problems and corruption of data since the conversion process uses the system code page (CP_ACP). In the case of a backend that serves many clients in many locales, the system code page often is not the correct code page for the client being served. In addition, VB uses the system locale settings for Date and Time formatting, which is also incorrect in a server environment where the client is not on the host machine.

    A summary of the areas that need to be specifically analyzed in a VB application are:

    • Date, Time, and Currency formatting methods
    • Calls to native WinAPI calls via Declare statements
    • Problems with certain methods that do String conversion
    • Methods that unexpectedly do conversion under the covers
    • Avoid Forms properties (and controls)
    • Embedded Display Strings such as Error Messages need to be isolated and placed in a satellite resource dll

    I18n Support for Dates and Times

    In an internationalized application, code that contains formatting strings such as "mm/dd/yyyy" should be refactored if the dates are to be displayed to an end user. For example, whereas May 03, 2005 in the U.S. would be displayed as 05/03/2005, in Spain it would be displayed as 03/05/2005 ("dd/mm/yyyy").

    Unfortunately, the locale-sensitive formatting methods that VB provides, are solely dependent on the system locale. As an example, VB's FormatDateTime method always formats according to the system locale. This is inadequate when using VB in a server component, and unfortunately there is no method in VB that will take an argument to specify to use a locale other than the system locale for formatting.

    Another problem with the FormatDateTime method is that it always uses the system timezone setting when formatting dates and times. This is incorrect in a server setting as the dates and times will be wrong if a user/client is in a different timezone.

    Problematic Methods - System Setting Dependent

    The following methods behave differently based on Locale, and have the appearance of being locale-aware, but unfortunately only work based on the System's Locale setting:

    • WeekdayName
    • MonthName
    • Format/Format$
    • FormatDateTime
    • DatePart
    • DateValue
    • TimeValue
    • IsDate
    • CDate/CVDate

    Problematic Methods - Hidden Conversions

    The following methods are intended to be Independent of Regional Locale settings. However, care must be taken in parameter usage, since they will accept Variants as well as Dates for parameters. If the Variant happens to be a String, the System Regional Locale is used to convert the String to a Date. This effectively ruins their "independent" nature and can lead to quite unexpected results:

    • Date,Date$
    • Time,Time$
    • Now
    • DateAdd
    • DateDiff
    • DateSerial
    • Day
    • Hour
    • Minute
    • Month
    • Second
    • TimeSerial
    • Year

    The Solution

    To solve these formatting problems, all the problematic locale-sensitive Date and Time methods listed above will need to be completely avoided and replaced with calls to a new layer which accepts a locale and is capable of properly formatting Dates and Times. This can either be an in-house COM object that uses native WinAPI calls, or a third-party package such as ICU. We recommend the ICU package, as there is a significant amount of logic that needs to be written around the native WinAPI calls to properly interface between VB and these calls to handle both locale and timezone information.

    For more information on Visual Basic functions, see Microsoft's Visual Basic 6.0.

    Win32 API calls from Visual Basic

    The Microsoft Win32 API can be accessed from Visual Basic by using the Declare statement. However, special care must be taken to ensure that Unicode UTF-16 data is not corrupted. Although Windows 2000 and newer Microsoft operating systems natively use UTF-16 in their Windows libraries, recall that for Windows 98 and earlier operating systems, the Windows libraries were natively implemented entirely in ANSI. For backwards compatibility reasons, Windows 2000 and later operating systems provide two interfaces to almost all exported methods; one that ends in "A" for ANSI, and one that ends in "W" for wide, or UTF-16.

    In a similar vein, although VB Strings are natively implemented in UTF-16, Microsoft made the decision for backwards compatibility reasons with Windows 98 and earlier to always convert Strings in VB Declare statements to ANSI, a conversion it does for you "under the covers". Unfortunately, it always uses the system code page (CP_ACP) for this conversion, which, in a server environment, is often the incorrect code page.

    Therefore, in order to "trick" VB into not thinking that a value is string (and thus avoiding the unwanted ANSI conversion), use the native UTF-16 "W" interfaces.

    As an example, note that the Declare statement accesses the "A" version of RegQueryValueEx:

    Private Declare Function RegQueryValueEx Lib "advapi32" Alias "RegQueryValueExA" (ByVal hKey As Long, ByVal lpValueName As String, ByVal lpReserved As Long, ByRef lpType As Long, ByVal szData As String, ByRef lpcbData As Long) As Long
    ...
    Public Function GetKey(ByVal KeyRoot As rkKey, ByVal SubKey As String, ByVal
    KeyElement As String) As Variant

    Dim lpHandle As Long, lBuffLen As Long, sBuffer As String * 255
    lBuffLen = 255
    lpHandle = OpenKey(KeyRoot, SubKey)
    RegQueryValueEx lpHandle, KeyElement, 0, 0, sBuffer, lBuffLen
    CloseKey lpHandle
    If lBuffLen > 0 Then GetKey = Left(sBuffer, lBuffLen - 1)
    End Function

    In order to avoid the potentially corrupting effects of the String conversion, use the "W" Unicode UTF-16 call directly (RegQueryValueExW). In addition, to make the Unicode UTF-16 call, you must also declare the function to use Long instead of String and modify the call to pass a pointer to that string with the undocumented StrPtr call as shown.

    Private Declare Function RegQueryValueEx Lib "advapi32" Alias "RegQueryValueExW" (ByVal hKey As Long, ByVal lpValueName As Long, ByVal lpReserved As Long, ByRef lpType As Long, ByVal szData As Long, ByRef lpcbData As Long) As Long
    ...
    Public Function GetKey(ByVal KeyRoot As rkKey, ByVal SubKey As String, ByVal KeyElement As String) As Variant

    Dim lpHandle As Long, lBuffLen As Long, sBuffer As String * 255
    lBuffLen = 255
    lpHandle = OpenKey(KeyRoot, SubKey)
    RegQueryValueEx lpHandle, StrPtr(KeyElement), 0, 0, StrPtr(sBuffer), lBuffLen
    CloseKey lpHandle
    If lBuffLen > 0 Then GetKey = Left(sBuffer, lBuffLen - 1)
    End Function

    Note that even with correct calling of a Win32 function to avoid data corruption, you must still ensure that the function is being used properly to support the current locale of the application. For information on the locale-dependent Windows functions, see Locale-Sensitive Windows C++ Functions.

    Locale-Sensitive Visual Basic Method List

    Click on a specific method below to get more information (usually from Microsoft's Visual Basic website). In some cases, where the method is supported via Microsoft's Win32 API, the link leads to Globalyzer's C++ Help, which can be used to determine internationalization solutions in your Visual Basic application.

    Locale-Sensitive Visual Basic Properties List

    Click on a specific property below to get more information (usually from Microsoft's Visual Basic website). In some cases, where the property or constant is supported via Microsoft's Win32 API, the link leads to Globalyzer's C++ Help, which can be used to determine internationalization solutions in your Visual Basic application.

    Win32 Locale Functions

    For internationalization information and guidelines, click here. Otherwise, click on a specific function for more information. Note that the help for these functions are part of the C++ Help, but the internationalization issues they discuss are applicable to a Visual Basic program calling into the Win32 API via the Declare statement.

    ConvertDefaultLocale

    GetACP

    GetCPInfo, GetCPInfoEx

    GetGeoInfo

    GetLocaleInfo

    GetOEMCP

    GetSystemDefaultLangID

    GetSystemDefaultLCID

    GetSystemDefaultUILanguage

    GetThreadLocale

    GetUserDefaultLangID

    GetUserDefaultLCID

    GetUserDefaultUILanguage

    GetUserGeoID

    MAKELANGID

    MAKELCID

    SetLocaleInfo

    SetThreadLocale

    SetUserGeoID

    Locale-Sensitive Win32 Functions

    For internationalization information and guidelines, click here. Otherwise, click on a specific function for more information. Note that the help for these functions are part of the C++ Help, but the internationalization issues they discuss are applicable to a Visual Basic program calling into the Win32 API via the Declare statement.

    CharLower

    CharLowerBuff

    CharNext

    CharNextExA

    CharPrev

    CharPrevExA

    CharUpper

    CharUpperBuff

    CompareString

    FindResource, FindResourceEx

    FoldString

    FormatMessage

    GetCalendarInfo

    GetCurrencyFormat

    GetDateFormat

    GetNumberFormat

    GetStringTypeA

    GetStringTypeEx

    GetStringTypeW

    GetTimeFormat

    IsCharAlpha

    IsCharAlphaNumeric

    IsCharLower

    IsCharUpper

    IsDBCSLeadByte

    IsDBCSLeadByteEx

    LCMapString

    lstrcat

    lstrcmp

    lstrcmpi

    lstrcpy

    lstrcpyn

    lstrlen

    MultibyteToWideChar

    StrCatBuff

    StrNCat

    WideCharToMultibyte

    wnsprintf

    wsprintf

    wvnsprintf

    wvsprintf

    Locale-Sensitive Win32 Character Set Names

    For internationalization information and guidelines, click here. Note that the help for these functions are part of the C++ Help, but the internationalization issues they discuss are applicable to a Visual Basic program calling into the Win32 API via the Declare statement.

    • ANSI_CHARSET
    • ARABIC_CHARSET
    • BALTIC_CHARSET
    • CHINESEBIG5_CHARSET
    • DEFAULT_CHARSET
    • EASTEUROPE_CHARSET
    • ELF_CULTURE_LATIN
    • FS_ARABIC
    • FS_BALTIC
    • FS_CHINESESIMP
    • FS_CHINESETRAD
    • FS_CYRILLIC
    • FS_GREEK
    • FS_HEBREW
    • FS_JISJAPAN
    • FS_JOHAB
    • FS_LATIN1
    • FS_LATIN2
    • FS_SYMBOL
    • FS_THAI
    • FS_TURKISH
    • FS_VIETNAMESE
    • FS_WANSUNG
    • GB2312_CHARSET
    • GREEK_CHARSET
    • HANGEUL_CHARSET
    • HANGUL_CHARSET
    • HEBREW_CHARSET
    • JOHAB_CHARSET
    • MAC_CHARSET
    • OEM_CHARSET
    • PAN_CULTURE_LATIN
    • RUSSIAN_CHARSET
    • SHIFTJIS_CHARSET
    • SYMBOL_CHARSET
    • THAI_CHARSET
    • TURKISH_CHARSET
    • VIETNAMESE_CHARSET

     

     Locale-Sensitive Methods Table of Contents

     

    Lingoport internationalization and localization services and software