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 methodStoreCaptions
. 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
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 )
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
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 = _