Introduction
Once I were to write a globalized application. I had to consider two approaches, one proposed by Visual Studio's Localized attribute of Windows Form, and one based on directly reading strings from resources. I chose second one because it allows the translator to use some simpler tools than Visual Studio (i.e. WinRes or any XML editor), and gives much more control over translation of strings by an application. However using this approach is a little bit mundane for the programmer, because with every thing he wants to globalize, he has to write appropriate lines of code, and has to keep an eye on adding or deleting controls from a form. He also has to watch (or set) names for resource entries. Every time you add or delete a control, you have to make changes to the globalization code. This is something difficult to avoid, but can be made a little bit declarative. Here is where my framework comes to play.
Implementation issues
GlobIt framework uses reflection for probing a form against some attributes that could be translated. At first I wanted to use fields of a specific type, i.e. System.Windows.Forms.Form
, to translate their properties using their names for querying resources. Unfortunately, by default, all fields are made private. This in my opinion shouldn't be changed. Since C# language has no friend functionality, this approach couldn't be used. Secondly I leaned towards properties. When using fields, framework could only guess what field's attribute to translate (Text
, or Width
, or ...). Using properties, it is upto the programmer what attribute he wants to change with specific property. Programmer also has to write a property for every attribute he wants to translate, but, as we will see, that's all he has to worry about.
Its time I showed you how it actually works, so lets get down to business. I prefer the way the code speaks for itself, with my little support. So we will just follow the flow of the application.
The code
For every attribute you want to be translated, you should create appropriate public property. For example, for a button1
field:
private System.Windows.Forms.Button button1;
We create two properties, one that sets its Text
attribute, and another one, that sets its Width
. Read possibilities are not necessary.
public String button1_Text {
get {return button1.Text;}
set {button1.Text = value;}
}
public Int32 button1_Width {
get {return button1.Width;}
set {button1.Width = value;}
}
Probably you have noticed, that second property sets Width
, which is Int32
. With GlobIt framework, it is also possible to set non-string values. One thing you have to do is setting appropriate type in the resource entry. Resource file is an XML file, below is an example entry:
<data name="GlobItTesterApp.Form1.button1_Width" type="System.Int32">
<value>250</value>
</data>
And here is where GlobIt does its job. After InitializeComponent()
, create Globalizator
object and pass this
as an argument:
InitializeComponent();
Globalizator gbl = new Globalizator(this);
gbl.Translate(this.GetType().Namespace + '.' + "GlobItTest");
Globalizator
will store this
as an object on which behalf translation will be done. Then we just call Translate()
method of Globalizator
object, passing a string composed of form's namespace appended by '.' and base file name for resource files. For example, for English (neutral culture), it would be named GlobItTest.resx, while the Polish one (my mother tongue) would be called GlobItTest.pl-PL.resx. I hope you now see what resource base file name is. Now we are moving to the core of the framework. At first, we have to create ResourceManager
object. We are doing this using the code below:
Type t = obj.GetType();
String str = t.FullName;
ResourceManager rm = new ResourceManager(baseName, t.Assembly);
where obj
is this
reference passed to Globalizator
constructor. Now, that we want to enumerate all public properties of a form, we have to obtain the collection of obj
properties. The code below does this:
PropertyInfo [] pi =
t.GetProperties (BindingFlags.Instance | BindingFlags.Public);
We have all we need, so what to wait for? Let's just iterate over the collection we have just obtained:
foreach (PropertyInfo p in pi)
{
string resName = t.FullName + '.' + p.Name;
Object resObj = rm.GetObject(resName);
if (resObj != null)
if (p.CanWrite)
t.GetProperty(p.Name).SetValue(obj, resObj, null);
}
For each public property GlobIt framework queries resource file against specific property, it uses fully qualified property name, which is namespace + class name + property name. If it doesn't exist, nothing happens, so the code doesn't compel you to do anything, unless you want to. If name that was queried against was found, matching object is passed to the SetValue()
method of specific property. Here is where the translation occurs. As you can see, the framework has quite a little code.
Overall view
GlobIt framework is fairly simple, but does its job well. It is not suited for all the localization issues, like date formats, sorting rules, etc., but provides an easy way for semi-declarative approach for globalizing applications. Main disadvantage is that it cannot be used with obfuscators, which become more and more popular recently. I intend to provide an alternate solution for obfuscated projects as well, as soon as I will have some spare time. GlobIt uses only public properties, so it doesn't need to be granted ReflectionPermission
permission. GlobIt framework doesn't include using System.Globalization
so it may also be used in other areas, serving as a resource framework, but its primarily target is globalization.
At the moment I'm a student at Warsaw University of Technology in Poland.
My programming langugage of choice is C++.
I also like C# and Java.