Loading...
 
Multi-Language Add-In for Visual Studio

Multi-Language Add-In for Visual Studio


Need for MultiLang.xx.resx ?

We've been using the ML add in for quite a while now within our company and it occurred to us that we always end up with essentialy two translation databases in our project files. One is the *_ml.xml file with supposedly all translations inside. The other are the MultiLang.*.resx files in the project root. (In case of web projects, the MultiLang.*.resx files live in the App_GlobalResources folder.)

MultiLang.*.resx files seem to hold all the strings ever used in a project. But then why do we need additional .resx files for every control and dialog? They too duplicate translation strings.

I had a look at ML documentation for explanation and was unable to find much on the subject. Is anyone able to shed some light on why ML generates a central *.resx file, while at the same time producing 'private' .resx files for controls and forms?

Will appreciate all feedback, this has been on our minds for a while smile

Germany

This is a good question, and I want to answer it in detail. You are right, that the texts are stored more than once, and it might not be necessary.

I think that there are really two separate questions, one about the how strings are stored in resource files and one about the project database. I will go into these questions separately.

Another pertinent question is, why should you care? There are a few reasons, which I would like to mention right at the start:

  • program bloat
  • the large number of files, in particular local resource files
  • a potential source of error, if resource files are not updated


I will come back to all three of these points.

Resource files

Let's look at the organization of the resource files. We can split the resource files up into

  • local resource files
  • global resource files


Local resource files are associated with a single component, for example a form, usercontrol or web page. Global resource files are not associated with any specific component and can be used anywhere in a project.

The terms local and global are used in ASP projects, where the resource files must be stored in the directories App_LocalResources or App_GlobalResources. I don’t think that Microsoft use the terms local and global for resource files in Windows Forms projects, but the concept is pretty much the same.

Local resource files

Local resource files have a very specific function in localizing the user interface, i.e. the texts defined in the forms designer or a web page. These are the texts which the Add-In shows in the Controls or ASP/HTML tab.

Visual Studio has built in support for localizing the user interface. For Windows Forms projects it uses the ComponentResourceManager class and in particular the ApplyResources method. For ASP Projects it uses the meta:resourcekey attribute in ASP tags.

In both cases, the resources are fetched from local resource files.

It is clearly Microsoft’s concept to isolate the user interface of each component into a self contained set of files, which can be localized as a separate unit.

In fact, Microsoft provides a tool for doing just this, called WinRes. This tool, which can be freely used by localizers, provides a stripped down version of the forms designer. If you set the Localizable property of a form to True, then Visual Studio stores enough information in the .resx file, to load the complete layout into a designer window. Using WinRes, you can create a localized version and save it as a localized resource file.

I would stress, that local resource files are Microsoft’s concept (and certainly not my invention). Unless there is a good reason not to, I think it best to follow Microsoft’s recommended practice.

Global resource files

The Multi-Language Add-In generates the neutral language resource file MultiLang.resx and additional localized versions of this file. This resource file is used to localize strings in the program source code, which are loaded using the function ml_string().

Technically, I suppose that source code strings could also be loaded from local resource files, but this seems somewhat more complicated, and I can’t any real benefit.

As you have spotted, the Add-In actually stores all texts to MultiLang.resx, even if they are only used in the user interface (the controls tab) and not in the source code. Clearly, this will make the program and the satellite dlls larger than necessary, although I am not sure that it is really significant.

There are two reasons for this approach and the first reason is mostly laziness. It is quite simply easier to export all strings, than to filter out only the strings which are really used in the source code. That being said, it really wouldn’t be difficult.

The second reason is described below:

Runtime language switching

Runtime language switching is a way to change the language of the user interface after it has been loaded and initialized.

To support this, the Add-In generates a function called ml_UpdateControls() in each component (e.g. form, usercontrol or web page). This function loads each individual property using the function ml_string() and its string ID. Effectively, it is loading the user interface from the global resource files and not the local resource files.

What this means is, that if you use this feature (in the way it is implemented at present), then you really do need to have all strings in the global resource file.

At this point, there are two very fair objections:

  • why does ml_UpdateControls() load the user interface strings from the global and not the local resource file?
  • why doesn't ml_UpdateControls() use the resources.ApplyResources() method, in the same way that it is used in the InitializeComponent() function?


There is no particular justification for not loading the strings from the local resource file. It would probably make sense, but I have just never done it.

The second objection is more interesting. By using the resources.ApplyResources() method, the function would be significantly simpler. I expect that there are some complications which I haven’t thought of, but on the face of it, this would be a sensible change.

Of course, if you are not using the runtime language switching feature, then the Add-In could restrict the texts in MultiLang.resx to the strings used in the source code. If you think this is important, I can implement it.

Alternative runtime support

I think it is worth mentioning the feature alternative runtime support which works in a very similar manner to runtime language switching.

With alternative runtime support, the Add-In also generates the function ml_UpdateControls() and adds a call to this function immediately after the component is loaded.

The critical point is that it does not set the localizable property to True, and it does not require the local resource files at all!

Thus, if you don’t like having all the many local resource files in your project, you could actually avoid them by using the alternative runtime support.

That said, using local resource files is the ‘’official’’ Microsoft method, and I recommend that you stick with it.

Historically, I implemented the ‘’alternative resource support’’ for smart device projects with Visual Studio 2002 and 2003, which did support the standard method. With Visual Studio 2005 this no longer applies.

Project database

Again, you are right that the Add-In stores all the texts in the project database and all the texts in the MultiLang.resx resource file (and its localized versions).

The original idea, going back to the first version of the Add-In for VB6, was that the Add-In:

  • stores everything it needs in a project database
  • exports the strings to some file which is used at runtime
  • generates support code to load the strings at runtime.


Originally, I wasn’t even sure that I wanted to use resource files. (The first version stored the localized strings in a gigantic case statement!)

I see roughly three alternative design approaches:

  • the current design, with a project database including all translations
  • a project database containing less information and no translations
  • no project database at all

The current design

The current design is flexible, because:

  • it is not tied to a single runtime implementation and
  • it can support different project types


Although resource files are the standard method to store localized strings, we could use some other method, for example loading them from a database. To be fair, there is no very good reason to do this, so it is not a very important argument.

Supporting different project types is, however, a serious issue. At present the Add-In supports Windows Forms, ASP.NET, WPF and unmanaged C++/MFC. I haven’t tried it yet, but Silverlight will be next on the list.

Historically, the step to store the translations to resource files has been a source of error, either because users forgot to do it, or because they didn’t understand that it was necessary. However, the Add-In now stores all translations directly to the resource files (local or global) which should make it more useable.

Given this change, it is reasonable to wonder why the Add-In stores the strings in the project database at all.

Project database without translations

A realistic change would be to continue to store some information in a reduced project database, but to store all translations directly to the resource files MultiLang.resx and its localized versions. Effectively, the resource files would be an extension of the project database.

I have thought about doing this, and the program structure is now such that I could make this change fairly easily. I could also make it an optional feature.

I would probably not support this feature for unmanaged C++ projects (which do not use resx files), but that wouldn’t affect many users anyway.

No project database

This is not a serious issue for me now, but if I was starting this project from scratch, I might try to do without the project database entirely. There is very little information in the project database which cannot be regenerated by scanning the project.

Summary

I doubt if program bloat is a big issue, but I could restrict the strings in MultiLang.resx to strings used in the source code.

You could do without the local resource files by using alternative resource support, but I don’t really recommend it. Using local resource files is the official Microsoft method.

I could remove the translations from the project database, and modify the Add-In so that it uses MultiLang.resx (and the localized version of this file) as its primary store for translations. In principle this would be a good thing.

By the way, these changes are in part mutually exclusive. If I took the translations out of the project database, they would all have to be stored in MultiLang.resx


Sorry that my reply has turned into a rambling essay, but I hope it makes some sense,
Phil


I would like to ask for more info about this topic - did I get it right:
All translations are actually stored in XML project database (from code and from controls) - so if we want to manually change the translation (quick change, fixing a bug on a remote server ect.) we have to do this in XML file not in the RESX files because strings in RESX files (global or local) are actually not taken into consideration?

Germany

No, it's the other way round.

The XML file is only used by the Add-In. The web server knows nothing about it.

If you have to make a quick fix on the server, and you are not going to use the Add-In, then the file to update is the resx file.

Of course, I would recommend using the Add-In to make the fix, and then copy the modified resx files onto the server (but then it's my tool).

Phil


Phil, I really appreciate you taking the time to go into a detailed explanation. We're all the more happier to continue and expand on our use of your add-in knowing that you care, and are actively interested in small and bigger improvements should they be neccessary.

First of all, I definitely agree with you that if Microsoft envisioned a particular way of handling localization (namely, local resx files), then we ought to stick to it unless we have a really, really good reason not to.

But let me go through some of the ideas you describe, and see if the following strategy would make sense.

Runtime language switching

Even if we don't use this functionality ATM, it sure does sound like a desirable feature, so I won't even think of removing it for the sake of reducing bloat, etc. Having said that, would I be correct saying that switching to using resources.ApplyResources() method would take us to the Microsoft side of things? In other words, would this function automatically load updated resources from the local resx file the same way they're loaded upon form/control initialization?

Resource files

If the above were the case then I guess we would be free to remove the extra strings in MultiLang.resx, and only leave those that are applied in code? This would leave us with translations local to a particular control really kept where they belong - locally respective of that control.

Project database

I have to say I was very positively surprised that you're willing to consider making such substantial changes to your add-in - rather than say 'it was designed that way therefore it will be that way'.

In my view it would be desirable to change the current strategy to the second option you describe: storing only project settings in the project database, with translations in the global MultiLang.resx (code) and local resx files (controls). I see multiple advantages:

  • Easier syncing of changes. We had a few cases when developers changed local resx files rather than the project database - because it was quicker, because they didn't know, etc.
  • Easier SVN merges. We have one less file to worry about, and that's no small file but a complex structure that may sometimes be difficult to merge. If it only stored settings, any changes to this file would happen on special occasions, enhancing project integrity and security.
  • Program bloat. A minor issue, but it's there nonetheless wink
  • Elegance. Just a personal view that it's best to keep separate things separate (translations vs settings), and not duplicate information unless neccessary.

Summary

Not sure if I understood all the details correctly. Other issues could be lurking around, as I'm not aware of all the nitty gritty of language support in VS.

Would be great if you could comment on the above stategy, whether it's viable, desirable in general, and whether you think other clients could benefit too. Would you be willing to implement them in the next version of the add-on? wink

Germany

Hi,

I'm looking into how easy or difficult these changes would be. I'll get back to you soon.

Phil