What's New
Q & A
Tip Jar
C# Helper...
Follow VBHelper on Twitter
MSDN Visual Basic Community
  Tip: Internationalization  

WJK has these tips for internationalization.

  • I consider using format() and val() functions to be coding errors. They have been replaced by internationally aware functions that actually work better IMHO. International code is a large subject. Entire books are devoted to the topic in Visual Basic. Get a book, get a book. Below are a couple of quick things to consider.
  • Get used to using the internationally aware functions;
  • Do Not Use the format() function, instead use FormatNumber() and FormatCurrency() instead. The functions allow use of the Windows User's Regional Settings. I had problems with numbers using the comma (,) as the decimal point.
  • Do Not Use Val() function, instead use of the Type Conversion Functions; CCur, CDate, CInt, Cstr, CDbl etc.
  • Dates formats vary widely...and customers do not appreciate seeing dates displayed in a format odd to them.
  • Program controls... I have seen storage of the text for each control stored in the system registry. I prefer not to use the registry. I use a database table with a column for each individual languages.
  • Be vary aware of the language capability of the custom controls you purchase. I have found that grids often do not allow the Arabic and Asian characters to be displayed. If you expect sales in those markets, make sure the controls work before investing a lot of time developing code for the control.
  • Well then, another hint... The format$, Str and val function work in the US version of WindowsXp when the German regional settings are used. (decimal=, and Kgroup Symbol=.)

    When the German Version of Xp is used, the same program malfunctions. It was a mess to get our software cleaned up and working again. We use Access as the database engine. Access seems to work well in international situations. This does not include the Arabic and Asian languages for which I have no experience. Truegrid does not support these languages. It is very important to select controls that are internationally capable. If you expect to see any foreign sales.

  • You used Cbool in your example, you should also stress using the type conversion functions to reduce numerical errors when converting from one variable type to another. If you want accuracy, then you have to use these functions (instead of Val() Function). These functions work internationally. The date variable format is especially troublesome overseas. The Val and Format$ functions are specifically banned from our programming work.

    Type Conversion Functions:

    • CBool(expression)
    • CByte(expression)
    • CCur(expression)
    • CDate(expression)
    • CDbl(expression)
    • CDec(expression)
    • CInt(expression)
    • CLng(expression)
    • CSng(expression)
    • CStr(expression)
    • CVar(expression)
    'Currency Function 
    Private Const LOCALE_SCURRENCY = &H14 'Local monetary symbol
    Private Const LOCALE_SINTLSYMBOL = &H15 'Intl Monetary Symbol

    Private Declare Function GetLocaleInfoA Lib "kernel32" _
        (ByVal lcid As Long, ByVal LCType As Long, _
        ByVal lpData As String, ByVal cchData As Integer) As Integer

    Public Function GetLocaleInfoC(Locale As Long, LCType As Long) As String
        Dim lcid As Long, stBuff As String, cch As Long
        stBuff = String(255, vbNullChar)
        cch = GetLocaleInfoA(Locale, LCType, ByVal stBuff, Len(stBuff))
        If (cch > 0) Then GetLocaleInfoC = Left$(stBuff, cch)
    End Function

    'Get Currency Symbol
    'stCurrencySymbol = GetLocaleInfoC(1043, LOCALE_SCURRENCY)
    stCurrencySymbolDefault = GetLocaleInfoC(1024, LOCALE_SCURRENCY)
    Select Case Left(stCurrencySymbolDefault, 1)
        Case "$" 'US Dollar
            CurrSymb$ = "Dollars"
        Case "€" 'Euro
            CurrSymb$ = "Euros"
        Case "£" 'England
            CurrSymb$ = "Pounds"
        Case Else
            CurrSymb$ = ""
            'add other currencies here...some are multiple character symbols
    End Select

I have not done much work on applications for use outside of the United States but on some of those occasions I did see problems with numbers, dates, monetary values, and other localizable data stored in strings. For example, if you store the value 12/31/06 in a string, it won't parse properly if the computer assumes you are using dd/mm/yy format.
Luke Emmet responds:

Just wanted to give you a few thoughts on your item on internationalisation in the last newsletter. I dont normally get much involved in responding to articles, except on this occasion my perspective is almost the *opposite* of that by WJK (as per the recent newsletter). So I could not resist emailing my own internationalisation tips...

I am responsible for an application that must function reliably across national boundaries, and we use XML as a native file format. This causes immense problems when serialising non-string data, due to the inherent localisation-aware CStr, CBool, CSng etc functions.

In theory it is possible to write an application that solely uses these functions to manipulate data in a locale-specific way, but this causes problems if users will share data across national boundaries. So our approach was to use Val() and Str() as the reliable way to achieve reliable data storage for storing (e.g. numeric) data that would allow a French user to create a file that would be read by an English user. I can see that MS Access might help here as a storage repository in that the data is stored in a more native format, but if you want to serialise/deserialise your own data, or use text based files such as XML, this wont help.

So I definitely do not consider Val() and Str() to be a coding error if it is used in the correct context! We ended up reviewing our whole application for all type conversion routines to check for the correct context of usage of all the native Cstr, CBool functions which caused us many more issues due to their non-deterministic behaviour.

I do agree that when an application presents information to a user, it should be formatted according to their own locale settings though. But my message to your readers is:

"Do NOT use the inbuilt VB6 locale aware type conversion functions to serialise/deserialise any data that needs to be reliably interpreted unless that data will only live in the locale within which it was created."

(I'm sure VB.Net is more sophisticated, but I dont have experience of this...)

Some code from Hugh Lerwill:
Re internationalization; the following may or may not be of interest;

    Function dpChar$()
        dpChar$ = Mid$(Format$(0.1, "fixed"), 2, 1)
    End Function
    Function Val#(ByVal txt$)
        'Wraps VBA.Val
        ' makes VAL able to handle decimal point chars other than "."
        ' VBA val only works on decimals when the decimal char is "."
        Static normal As Boolean, init As Boolean, dp$
        If Not init Then
            dp$ = dpChar()
            normal = (dp$ = ".")
            init = True
        End If
        If normal Then
            Val = VBA.Val(txt$)
            Val = VBA.Val(Replace(txt$, dp$, "."))
        End If
        'VB help recommends use of CDbl but..
        'the following does'nt work with strings that contain numbers and text
        ' like eg. "5.4 mp" because the error is tripped and 0 is returned
        'On Error Resume Next
        'Val = CDbl(txt$)
    End Function

From Trevor Finch:
These comments are from a very small developer, with only a few users Our software uses measurements (feet/cm, Gallons/Ml) but not currency.

We save all text for 'captions' and 'ToolTipText' (labels, command buttons, menus, etc) in a separate file for each language The file is a '~' delimited text file, with an ID number for each text (e.g: 12345~ &Print~ Print the document) The ID number is entered in the control tag, in the IDE

(We have a utility programme to generate the english text file by reading all the *.frm files in the project)

In Form_Load a routine loads the Captions and ToolTipText from the language file

All MsgBox text comes from the same file

[This is a common and effective strategy used by Visual Basic 6 developers. Visual Basic .NET lets you build customized interfaces for different locales so, for example, you can specify the text to display in a label in a default language (for example, English if you expect most of your customers to be in the United States), German, and French. Then at run time, the program automatically selects the right strings based on the regional settings of the computer running the program. It's pretty cool. Rod]

An advantage of the system is that a local agent in a country can edit the text file (using Notepad) A user with the same language can also customise the text used in the application.

A new language is created by copying and renaming the english file, and emailing to the local agent to translate The local agent can then distribute the file directly to their users (with no reference here)

Using a single database table, with columns for each language, would involve a version control system, with all translation managed in the same place.

We don't do anything with the 'locale' settings in the Registry.

Sometimes the actual user of the software prefers, say Spanish (e.g. in the USA), and not the locale setting on the PC In other countries (e.g. China) users sometimes prefer English to Chinese

And, after various attempts, we don't even use Windows Regional date setting

On a single form in the application the user manually selects a language file, and chooses units, formats etc

All user settings are saved in an INI file in the application folder. This can be emailed so we can see exactly how the user is configured, and we can even email it back.
All data is saved internally in metric, and converted to the user preferred units at run time (I assume VB and Access save everything in twips, but display in user units)
On the comment from Luke Emmet about data needing to be transferred between different locales...

Our big problem is reading dates - d-m-yyyy m-d-yyyy yyyy-mm-dd

Our applications handle output from data loggers, and each manufacturer has their own idea of what is wanted.

(When date and time is output in separate fields it suggests that they never have tried graphing in Excel !)

The problem is data being generated in one locale but read in another locale.

You can *output* in any date format, but VB.DateValue() and VB.TimeValue() seem to use the locale setting in the registry to interpret dates.

Microsoft software *can* be configured at runtime - the Excel import wizard allows users to choose 'd/m' or 'm/d'

I assume VB and Excel are using the same routines, but have not been able to find out how the rest of us can do it.

Until we can solve this problem of configuring 'DateValue()', if we output dates as text we use the format 'd mmm yyyy'.

This seems to be unambiguous across english-speaking locales.

And I would agree with Hugh Lerwill on the need to wrap VB conversion functions

VB can do some very odd things with 'decimal' points in dates and times

We have a DateTimeValue() as Double to handle both dates and times, and their quirks.

From Ariel Carnievsky:
Hello Rod. Do you remember that a few weeks ago I asked you about something simillar?

I found that ADO has problems with the comma separated values when working with periodic numbers. I use ADODB.Commands with parameters. When I had to update a Double value, for example the debit balance of a client, I made this command:

    Const SQL_CLI_M = "UPDATE Clientes SET " & _
        "Surname = ?, Name = ?, Balance = ? " & _
        "WHERE IdClient = ?"
    Set ComCliEd = New ADODB.Command
    With ComCliEd
        Set .ActiveConnection = Con 
        .Parameters.Append .CreateParameter("Surname", adVarWChar, adParamInput, 50)
        .Parameters.Append .CreateParameter("Name", adVarWChar, adParamInput, 50) 
        .Parameters.Append .CreateParameter("Balance", adDouble, adParamInput, 8) 
        .Parameters.Append .CreateParameter("IdClient", adBigInt, adParamInput, 4)
        .CommandText = SQL_CLI_M 
    End With 
If the value was "$ 4,00" (here in argentina, we use the comma to separate the decimal part from the integer part of a value), then I assigned:
    With ComCliEd 
        .Parameters("Balance").Value = CDbl(txtBal) 'txtBal.Text = "4,00" 
        '... fill the other parameters 
    End With 
In this case, there will be no problem in updating the value. But if the value was "3,99" instead of "4,00", the database connector would save the value "4,654656E+34" (for example). The only solution I found was replacing the ADODB.Command with a direct SQL instruction to the DB Connector (Con). But before doing this, remind replacing the comma "," with a point ".", because in this case the connector won't "cast" the value.
If you have other internationalization tips, email me.


Copyright © 1997-2006 Rocky Mountain Computer Consulting, Inc.   All rights reserved.