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$)
Else
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
.Execute
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.
|