The FormPreparer Component's StoreCaptions Method
If you've read this far, you've probably surmised that when the user picks a language, the translation mechanism is going to look up each text string on the form and replace it with the translation corresponding to the currently selected language that it finds in table Translations. But if you've already translated a screen with the word
Country on it to Spanish, you now have
País on the screen, which you won't find in the Original column of the Translated table.
The trick to translating GUI elements is that you have to base translations on the
initial caption, not on whatever's currently on the screen. Thus you have to save the original captions that are on the screen when it loads. I use a
collection, for reasons that you'll see shortly. I originally used the
Tag property of each control, but ... well, you'll see.
The trick to translating GUI elements is that you have to base translations on the initial caption, not on whatever's currently on the screen. Thus you have to save the original captions that are on the screen when it loads.
|
|
In order to prepare each form for translation, I built a component called FormPreparer, which contains just one method
StoreCaptions. FormPreparer is a component, just as DAC is. To create this component:
- Create a class library project called FormPreparer.
- Delete the Class1.vb module and add a new component. Name it FormPreparer.
- Enter the code in Listing 2.
- Compile the project and add it to the Components tab as you did with the DAC component.
You can now drop it on any form whose captions need to be stored in the Original table in preparation for translation. It should be called in the form's Load event, as you'll see in the inheritable form class shown in
Listing 4.
The
Text properties of labels, buttons, checkboxes, and radiobuttons, as well as grid column headers, will need translation. That's pretty straightforward; you iterate through the
Controls collection of the form and store the text property in a collection named
Captions, using the control's name as the key. (The syntax is
collection.add (Value, Key).
I do this in my base form class so that every inherited form will automatically be prepared for translation.
My first inclination was to store the initial caption in the
Tag property. Labels, checkboxes, and other standard Windows form controls have a
Tag property that's available for "users" (that's us.) But forms can also have menus, which consist of MenuItem controls. Menuitems are really, really different. For one thing, they don't have a
Tag property. They don't even have a
Name property! Besides, I wanted to handle all form controls with captions in a similar fashion.
In my solution, I create a unique identifier for each MenuItem and store it in a collection named
Captions. And as long as I'm using the
Captions collection for menu items, why not use it for all of the other controls as well, using each control's name as its key?
In my inheritable form class, BaseForm, I populate the
Captions collection using the control's
Text property for the value and the control's
Name property for the key:
For Each Ctrl As Control In Controls
Captions.Add ( Ctrl.Text, Ctrl.Name )
End For
But how do I get a unique reference to each MenuItem (they don't have a
Name property, remember)? It really doesn't matter what the keys are, as long as they're unique, and can be reproduced again when it's time to look up the corresponding menu captions for translation. So I create a string called
mLevel, which starts life as a null string, and store consecutive integers (converted to strings), representing each MenuItem's horizontal position in the menu hierarchy, as identifiers. Thus the keys for the first row of MenuItems are "0", "1", "2", etc. But MenuItem "0",
File, has a dependent pad named "Exit" below it, which is a child menu item. So, I call the routine recursively, passing it the current value of
mLevel instead of the null string. Hence the generated key for File, Exit is "00", the generated key for Tables, Clients is "10", and so forth. If you have several forms with menus in your application, prefix this key with the unique form name.
The FormPreparer component stores the translatable items on each form in the
Captions collection, and the
InsertWord method stores the caption in the
Original table if it's not there already. And since menu items can have their own
MenuItems collections, the code that translates menu items recursively calls itself if the menu item's
MenuItems.Count property is greater than zero. (You read right:
menuitem1.menuitems.count > 0 means that menu item1 has a submenu.)
When it's time to translate a menu on a form, I follow the same methodology to re-create the keys, and then use them to retrieve the original text captions for the corresponding menu items from the
Captions collection and translate them. To translate form controls, I just iterate through the
Controls collection, use each control's name to find the original text value in the
Captions collection, and again pass them to the translation method, which assigns the translation to the
Text property (see
BaseForm.vb in
Listing 4).
The
StoreCaptions method is called in the
Load event of each inheritable form; it passes the form itself as its only parameter, returning the resulting collection to a form field named
CaptionCollection:
CaptionCollection = _
StoreCaptions1.StoreCaptions(Me)