Welsh, Turkish and Polish - a case for internationalisation
Background
We had looked at various methods of providing versions of our own programs that support languages other than English, and were under some pressure to provide a Welsh language version of our wildlife and biological recording and mapping software
AditSite
We were considering whether the support would have to await a new version, or could reasonaly be retro-fitted to the existing code, when we were asked to help another software house produce Turkish and Polish versions of their management information system.
Their needs (and to a large extent their timetable) initiated the hunt for a simple robust solution.
Major Objectives
The major objective became a simple method of supporting one or more 'foreign' languages without major changes to the way existing code worked, and ideally from a single executable (rather than different programs for each language).
Previous experience with supporting German had identified two areas that were considered important for this project:
Context
Meaningful translation requires context. Most words and even many short phrases would be translated differently, depending on the surrounding context. Sometimes this simply indicates whether a word is a noun or verb (colour), but often is rather more subtle (record).
We had the idea that it would be good for the final translation work (the polish, not the Polish) to be done by a user actually running the program.
String Size
Captions on labels and buttons are typically restricted in length by screen layout. English may have shorter words than some other languages, or we may have a wider choice of word than in some other languages - either way it is common to find that a suitable translation produces a word (or words) that do not fit in the available space.
Sometimes this can be easily addressed by the programmer (increasing the space for all), but at other times would require a major reorganisation of the form. Context can help produce an abbreviation, but tooltips were the saving grace.
Somewhat thankfully, for these particular projects, we were not concerned with other internationalisation issues such as currency and measurement, though it is likely that these will become more important in due course - particularly for the Turkish example.
Of course, with any translation work, care must be taken not to upset the end-user with inappropriate text - you can see some examples by following this link.
Method
Using a local database to store the foreign language text was a simple decision (in a multi-user environment, different users can work in different languages). Each program module would need to be able to access translations for all of the 'static' items (most menus, labels and button captions), messages, and headings (for example grid/table column headings).
Our simplified table layout for captions could look something like this:
EXENAME - a string holding the module name
FORMNAME - a string holding the form name
CONTROLNAME - a string holding the control name
CONTROLINDEX - a long holding the control array index
ROW - a long holding the row where the control is multi-rowed
COL- a long holding the column where the control is multi-columned
DEFAULT - the caption in the default language (english)
CAPTION - the caption in the 'foreign' language
TOOLTIP - tooltiptext in the 'foreign' language
and even simpler for general messages:
EXENAME - a string holding the module name
DEFAULT - the caption in the default language
TEXT - the caption in the 'foreign' language
We actually added some font information, and a method for distinguishing between multiple possible translations of the same message, but I am sure that you get the general idea.
Then it is easy enough - as each form loads it calls a standard routine that populates all the captions - example in our code = LanguageForm.Populate Me
Each message in the program calls a new function instead of the standard window messagebox (most changes can be done using global find and replace) - example in our code = LanguageForm.MessageBox "The Program Could Not Access The Viewer", vbInformation, "Help"
Each explicit caption assignment calls a new routine - example in our code for a grid column heading = .TextMatrix(0, 0) = LanguageForm.Message("Name")
Of course some of your messages will be more complicated, often involving variables, and sometimes word order will be a problem, but this is a good start.
Now for the interactive bit. Assuming we have a set of basic translations in the database, how do we do the polishing?
On each form, we add code that allows the user via a reasonably unlikely keyboard combination (we used Ctrl+Alt+Shift Right-Click) to edit the captions on the fly - example for our code:
Private Sub Form_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
....On Error Resume Next
....If Button = vbRightButton Then
......If Shift = (vbShiftMask Or vbCtrlMask Or vbAltMask) Then
........LanguageForm.DoMenuAndCaption Me
........LanguageForm.Populate Me 'refresh after change
........Exit Sub 'jump out if other code in sub
......End If
....End If
End Sub
Bells and Whistles
Routine to export the database to a spreadsheet for the initial translation work
Routine to load the initial translation work
Routine to create a simple dictionary from the translation work
Example Screen Shots
Interactive Caption Setting
Welsh AditSite
Turkish Estimating