Introduction & Background
During the development of my pet project, I had to solve the problem of localization. C# offers an almost perfect solution for it. If your form(s) Localization
property is set to true and you give the default language, studio (or C# express) will generate a series of localizable text properties of your components, which are deployed in the project. You can edit the values in a table by you double-clicking on the *.resx file. Furthermore the editor generates a Resourcename.Designer.cs file, which contains the localized properties as static members of a class, called as the “resourcename”.
This allows for referring to any of the text values via a unique static property, to find the appropriate string values of your variables, based on the CultureInfo
properties (for details please refer to the msdn). If you would like to give an additional language to your project, you have to create a new .resx file with the same name, but the name must contain the culture id. For example: LocalisationSample.resx (default resx file), LocalisationSample.en-EN.resx (English version).
Now if your project is recompiled, and the OS language settings are changed from your default language (Hungarian in my case) to English, all the translated text will be displayed in English.
Although this is a robust solution for the localization problem, I have some extra demands:
- I wouldn’t like to recompile the project to add new languages.
- I wouldn’t like the code size and compilation time increased by new languages.
- I want to provide facility to the user to translate displayed texts to any language.
Result
To satisfy the above requirements, the data must be stored in a user editable XML file (.resxml). The file name of these files are created according to the following rule:
filename + _lang + .resxml, e.g.
MyResources.resxml -> English
MyResources_hu.resxml -> Hungarian
The structure of the resxml file is really simple; the following example (Fruits.resxml) contains (ids, values, description) triplets for some fruits:
="1.0" ="utf-8"
<root>
<it id="APPLE_ID" value="apple" description="my favorite fruit" />
<it id="PEAR_ID" value="pear" description="a sweet fruit" />
</root>
A static class RManager
is defined, which is able to load the appropriate resxml files based on the language setting at the very beginning of the execution of your application. This class must be included in your project. To use the string variables (static properties) as global variables, an additional static class called “filename” in a filename.cs (Fruits.cs in the sample) file should be defined.
using RMTool;
using System.ComponentModel;
namespace FruitsNm{
public static class Fruits
{
public static string APPLE_ID { get { return RManager.GetValue("Fruits",
"APPLE_ID"); } }
public static string PEAR_ID { get { return RManager.GetValue("Fruits",
"PEAR_ID"); } }
}
}
To simplify the generation of resxml file and static class, I developed a free tool, called RMTool. This tool displays the data triplets (id (Name), value (Text), description) in a table of the selected resxml file.
The Interface helps to manage, generate and translate your resource files.
Using the Code
I created a new project, and I put a Label in the Form1.
After the RManager.cs, Fruit.cs files were added to the project, and the RMTool and FruitsNm namespace were referred in the Form1 file, I was able to assign APPLE_ID language dependent string value to the Text
property of label1
.
The source code, the constructor of Form1:
public Form1()
{
InitializeComponent();
string AppPath =
Path.GetDirectoryName(Application.ExecutablePath);
RManager.GetFiles(AppPath, "");
label1.Text = Fruits.APPLE_ID
}
and the result:
Summary
I presented an alternative way to the solution of the localization problem. In this way your project size and the compilation time won’t increase with new language resources, since they are stored in different XML files (resxml), which are dynamically loaded during the execution of the application. I also provided a tool to facilitate the development of resource files.