Browse DevX
Sign up for e-mail newsletters from DevX


Localize Your .NET Windows Forms Apps : Page 3

It's a small world. For the price of a nice pair of shoes, you can get on a plane, have dinner, watch a movie, sleep a few hours, and wake up on another continent. Your software can travel even more easily. When it gets there, will it be ready to go to work?




Building the Right Environment to Support AI, Machine Learning and Deep Learning

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:

  1. Create a class library project called FormPreparer.
  2. Delete the Class1.vb module and add a new component. Name it FormPreparer.
  3. Enter the code in Listing 2.
  4. 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)

Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.



Thanks for your registration, follow us on our social networks to keep up-to-date