Multi-Language for Visual Studio
Quick Tour (Windows Forms)
After you have installed Multi-Language for Visual Studio, there will be a new command in the Tools menu in Visual Studio.
This menu item activates Multi-Language and shows it in a tool-window. You can dock this window at the side of the main Visual Studio window, or drag it into a tab group, like other tool windows in Visual Studio. It's probably best to place it at the bottom, in a wide format.
Selecting a project
A Visual Studio solution may contain multiple projects. With Multi-Language you usually work with a single project. To get started, you must first select the project. There are two ways to do this.
|from a dialog||When you select Multi-Language from the tools menu, it will probably show a list of the projects in a dialog. |
Simply select the project and click on OK.
This dialog can be disabled, so if somebody else has already worked with Multi-Language, it might not appear.
|from a drop down list||At the top of Multi-Language's main window, just below the toolbar, there is a drop down project list |
You can select a different project at any time from this list.
Initialising the project for localization
When you select a project for the first time, you must specify what the original language of the project is. Multi-Language will show a list of the languages supported by Windows.|
Select the original language and click on OK.
The buttons below the list change the way the list is displayed. In particular, the button English simply shows the language names in English. It does not select English in the list.
As a second step, you can select one of two ways to handle strings in the source code:|
Named resources is the standard method in Visual Studio. This is recommended for new users.
ml_string() is a custom implementation used in older versions of Multi-Language.
This dialog will probably be followed by second dialog, with options for scanning the project.|
Just click on OK to accept the default options.
Multi-Language will now add one or two new files to your project.
|<project name>_ml.xml||This is the project database used by Multi-Language. It is added to the project with BuildAction=None.|
|mlstring.vb or |
mlstring.h + .cpp
|This module contains support functions, in particular the function ml_string(), which is used to load localized strings.|
This file is not added if you selected the option Named Resources.
Scanning the project
Multi-Language will now scan the project. This is performed in two phases:
- scanning controls
- scanning source code
In the first phase, the controls in each Form and each UserControl are scanned for properties with the localizable attribute. In the second phase, the source code of the project is scanned for texts which may require translation.
The results of the scan are displayed in two separate grids in Multi-Language's window.|
You can switch between these grids using the tabs at the bottom of the window.
Adding a second language
To add a new language to the project, click on the
symbol on Multi-Language's toolbar|
This brings up a dialog very similar to one where we selected the original language of the project.
Select a new language from the list.|
If either the Microsoft or the Google Translator supports both
Select one of these options if you want the texts to be translated automatically.
Click on OK to add this language to the project.
Some languages, such as German or French, are considered to be neutral languages. The regional variations, such as German(Germany), German(Austria), French(France) or French(Canada) are referred to as specific languages. You should always add the neutral language before adding a related specific language.
The .NET framework uses a resource fallback mechanism. For example, if a resource is defined for German, but not for German(Austria), then an application using German(Austria) will automatically use the resource defined for German. The best practice is usually to specify all translations for the neutral language, and only regional differences (if any) for the specific language.
After adding the second language, Multi-Language will
- add a new column to the grids for the new language
- search for translations of common texts such as OK and Cancel in the Global Translations Database
- if you selected the Microsoft or the Google Translator option, then it will translate all other texts online the online translation service
Now we will take a closer look at the two grids.
The controls grid
To see the controls grid, make sure that the controls tab at the bottom of the window is selected. The grid should look something like the following screen shot.
By default, the properties are shown in a hierarchical manner, showing the association of properties to controls. As an alternative, you can view the properties on each Form or UserControl in a flat list. To select this mode, click on the button on the toolbar and then select Flat list from the menu, as shown below:
The controls grid will then look something like the following screen shot.
To enter a translation, simply click in cell and type in the new text. If you are able to translate the text yourself, this is the easiest way enter translations.
The background colour indicates the status of the translation, which has one of the following six values.
The original text in the project.
|Edited text||The text has been edited in the grid.|
The text was inserted from the global database.
The text was imported from the a spreadsheet (Excel or OpenOffice).
The text was translated automatically using the Microsoft or the Google Translator.
|Imported localization||The text was imported from the project localization, made without Multi-Language.|
If a text is shown in light grey, then it is the text which would be retrieved at runtime via the resource fallback mechanism. In general, if a text is not defined for a specific language (e.g. Spanish(Mexico)) then the text for the neutral language (e.g. Spanish) would be used. If this has not been defined, then the original text in the project would be used.
The check boxes to the left of the property name indicate that the text has been selected for translation. By default, all properties which contain a non empty string are selected. In the screen shot below, you can see two menu separators with the text "-". It makes no sense to translate these, so we can deselect them by clicking on the check boxes.
In fact, we never really want to see these properties again, so we can go one step further and hide them completely. To do this, we first click on the sun symbol at the left hand margin. The sun symbol is replaced by a moon symbol . Then click on the sun/moon toggle button ( , or ) on the toolbar to actuall hide the texts. See also below (Hiding Texts).
By default, properties containing empty strings are not shown in the grid. It very rarely makes sense to translate empty strings, so this if sensible setting. If you do want to see localizable properties containing empty strings, then you can modify this option in the settings dialog. To show this dialog click on the project settings button on the toolbar. Details of this and other options are described in the online help file.
The source code grid
To see the source code grid, make sure that the source code tab at the bottom of the window is selected. The grid should look something like the following screen shot.
As in the controls grid:
- you can select a text for translation by clicking on the checkbox or
- hide the text by clicking on the sun symbol .
In contrast to the controls grid however, none of the texts are selected automatically.
When you click on a line in the source code grid, the corresponding line in the source code will be shown in the source editor.
If you click on a text in the source editor with the right (or secondary) mouse button, you can select the command "Show in Multi-Language" from the context menu, which will locate the text in the source code grid.
This is identical to placing the cursor on a string and clicking on the "Goto line" symbol on Multi-Language's toolbar.
When you select a string for translation by clicking on the check-box, Multi-Language will modify the source code in one of two ways.
If you selected the option Named resources, then Multi-Language will generate a resource string containing the text.|
It will then replace the text with a reference to the named resource.
In the screen shot, the text "Save" has been replaced with a resource string named Save. Multi-Language will automatically generate a resource name based on the text itself.
Note: After compiling the project, Visual Studio should show the content of the resource string as a tooltip on the resource name, as shown below.
If you selected the option ml_string(), then Multi-Language will assign a string ID number to the text and insert a call to the function ml_string() into the source code.|
As you can see, this function has two parameters:
The exact format of the function ml_string depends on the computer language used in the project.
The function ml_string is implemented in the module mlstring which was added to the project from a template file.
All template files are listed in the Multi-Language options pages. You can make a modified copy of any template file and update the configuration to use your modified version.
Once a string has been selected for translation, you can enter a translation simply by typing a new text into the grid.
As with the controls grid, you also have the option of hiding texts which do not require translation, by clicking on the sun symbol at the left hand margin, which is then replaced by a moon symbol . See also below (Hiding Texts).
Hiding texts which should not be translated is an important feature of Multi-Language for Visual Studio.
There are several reasons:
|Dangerous translations||There may be some texts in the source code which must not be translated.|
In some cases, it will cause a program error is a text is translated.
|Handling all texts||It is good practice to either select or hide all texts.|
After further development of your project you may have to review which new texts require translation. If you hide texts which do not require translation, then you will never have to look at them again.
If you follow this practice, then any text which is neither selected nor hidden must be a new text.
To hide a text, simply click on the sun symbol at the left hand margin. The sun symbol is replaced by a moon symbol .
You can actually hide these lines by clicking on the sun/moon toggle button , , on the toolbar, which toggles between the modes "show hidden items" and "hide hidden items" . (The sunset symbol indicates a dirty state after additional items have been hidden.)
When you hide a string in the source code, Multi-Language adds the comment //MLHIDE ('MLHIDE in VB) to the end of the source code line, to mark it as hidden.
For example, the property name passed to the function NotifyPropertyChanged (in the screen shot below) must not be translated.
The best practice is to go through every text and either:
- select it for translation or
- hide it.
This makes it easier to maintain the localization in future versions of your application, because you can easily identify new texts in the project.
Filtering texts with regular expressions
In many cases, you will be able to select, or alternativly hide, texts in the source code based on simple rules. For example you will probably not want to translate SQL strings, or the parameters to an event logging function. On the other hand, you will probably always want to translate the parameters to the MessageBox function.
You can handle operations like this easily with the feature "Filter with regular expressions". You can select this option from the tools menu as shown below, or from the context menu in the source code grid.
This command shows the filter dialog, in which you can enter a search string as a regular expression. For example in the screen shot shown below, the search string "^select " will search for any string starting with the word select, with the option to hide the string.
The dialog offers the option to match only the string, or the complete source code line. By matching the complete code line, it is easy to detect strings used in particular function calls.
When the string is found, Multi-Language asks whether the specific string should actually be hidden.
Using the translation memory
Aside from the project database used to store the translations for an individual project, Multi-Language also stores all translations in a global translations database. When it detects an exact match between a new text and a translation stored in the global database, it will insert the translation automatically. This is great for common terms like OK and Cancel.
If you are able to translate the texts yourself, then the translation memory provides an additional help, based on the translations stored in the global database. To use the translation memory, first click in the cell you want to edit, and then hit F3 or F12. The following screen shot shows the translation memory dialog with the text Add a new language to the project, which should be translated into German.
In the lower part of the dialog, there is list of existing translations, containing one or more of the same words as the string to be translated. The list is sorted according to the number of matching words, so the most useful translations should be near the top. You can copy words out of the grid and edit the new translation in the edit box marked Translation.
Obviously, the common words "a", "to" and "the" will not be of much help. If you select the tab Ignored words, you can select the words which are to be ignored. Multi-Language keeps a list of ignored words in the global database and automatically ignores these words in future.
The translation memory feature is particularly useful for technical terms where you might not be familiar with the correct translation and where you want to use terms consistently.
Using the Spreadsheet export
If you are not able to translate the texts yourself, you will have to give them to a translator. For this purpose you can use the Spreadsheet export/import feature. There are two versions of this feature:
- the simple version with one worksheet and no macros (for Excel or OpenOffice)
- the three worksheet structured export with macro support (for Excel)
Both versions are accessed via the Excel symbol on the toolbar.
The screen shot below shows the format of the simple spreadsheet file. As you can see, the first column contains the string ID number, followed by a column for each language. The first two rows contain the language names and the locale ID numbers.
The translator should update texts in the appropriate language column, without changing the format of the file. When the translator is complete, you can import the translations back into the project.
The three worksheet format contains two additional worksheets, which closely resemble the controls grid and the source code grid in the Multi-Language window. These worksheets provide more context information for the translator. Because all texts in the additional worksheets are also present in the third worksheet, Excel macros are used to maintain consistency within the file. The macros also provide some additional functionality, similar to some of the functions provided by Multi-Language.
The three worksheet is to be recommended, unless your organization is adamantly opposed to using macros.
Exporting the texts to resource files
Multi-Language for Visual Studio automatically ||
The screen shot to the left shows resource files generated by Multi-Language in the solution explorer.|
In this case the project is using the option Named resources.
There are two categories of resources file:
All of these files have the BuildAction=EmbeddedResource.
If you are using the option ml_string(), Multi-Language stores the texts in an additional resource file names MultiLang.resx, instead of using the global resource file resources.resx. |
As already mentioned, this option is not recommended for new projects.
When you compile your project, additional so called satellite dlls will be generated. These dlls are created in subdirectories of the bin\Debug or bin\Release directories, named according to the language abbreviation (ISO 639-1 and ISO 3166, formerly RFC 1766).
The compiled project is now localized. Windows will initialise the application to use the language of the Windows user interface. For example, if you have added support for German, and you install your application (with the satellite dlls) on computer running a German version of Windows, then German resources will automatically be used.
Selecting the language of the application
It is very practical to be able to select the language for your application, independently of the language used by Windows. This is almost essential for testing, but you may also want to make it a permanent feature of your application. We must consider two cases separately:
- selecting the language when the program starts
- changing the language of the running program
Technically, it is very easy to set the language of a program when it starts up. It is only necessary to set the CurrentUICulture property of the main thread, before the main form of the program is called.
Multi-Language can add a form to your project which allows the user to select the language when the program starts, as shown in the following screen shot.|
With this dialog you can select the language, and specify whether the dialog should be shown next time the program is started.
To add this feature, click on the runtime support symbol
on the toolbar and select the command Add language selection form to your project from the dropdown menu.||
This brings up a dialog showing which actions are necessary to add this dialog to your project. |
Click on Add Now to perform start the operation.
This will add a form called SelectLanguage to your project, and some code to show the form when the program starts up.
Changing the language of a running program
A little more effort is required to change the language once the user interface of the program has been initialised. The problem breaks down into three operations:
|Selecting a new language||To select a new language, it is simply necessary to show the SelectLanguage form, described above. This can easily be added to a menu command in your application.|
|Broadcasting events||Your application may use multiple Forms, each of which may contain UserControls. When the language is changed, it is necessary to update the user interface of each active component. The best way to handle this is with an event mechanism. |
In general, you may use UserControls in multiple, separately compiled components. For this purpose, events must be generated via a public interface. The easiest way to handle this is to generate the events using a singleton object, compiled in a separate project.
|Updating the user interface||Each Form and UserControl requires an event handler, which updates the user interface when the language is changed. The function InitializeComponent generated by Visual Studio is not designed to be called repeatedly. To update the user interface, Multi-Language can generate a function called ml_UpdateControls to reinitialise the properties in the user interface.|
Multi-Language can generate the code for the second two points automatically, via the menu command Support for Runtime Language Switching.|
A minor change is required to the SelectLanguage form, in order to generate a language changed event.
This is described in a tutorial in Multi-Language's help file.