On an Internet-linked world, is not too hard that our applications travel may be to Madagascar or something. Besides, as the time pass, the software working teams are more heterogeneous as our possible clients.
In this article I describe in a fast way, the basis for the treatment of globalization in our applications.
Lets understand globalization as the fact of allowing an application behave correctly in any culture defined inside an operating system. In general, like in Microsoft(R) Windows(R), we realize that we can set some aspects choosing a culture, using the option Language from the Control Panel.
This culture determines the way in which the numbers are displayed on screen, the date format, the format for negative numbers, the language for some emergent messages, etc.
In general, the cultures' identification has been standardized is such a way that with a single string, each one is well established. This string is formed by two components separated by a hyphen. The first of them identifies the base for a given culture and the second one specifies the possible culture variations derived from the application of such a culture in a given region or country. Thus, some valid culture name examples are:
- "es-CO" - For the culture determined by the Spanish in Colombia
- "fr-FR" - For the culture determined by the French in France
- "fr-CA" - For the culture determined by the French in Canada
- "es" - For the culture determined by the English in General
Three important details:
- Some culture names are only described by one component... this is no more than a generalization for a given name.
- As you can see, the name of the region or country always is written with CAPS and is composed (in general) by the two first letters of it.
- When there is not an explicit specification for a culture inside an application (.NET application), the default culture for it will be: "" (empty string) or "en" which is the same in this case.
On the .NET framework there are two special objects which allow us to globalize our applications:
The main idea is that with the
CultureInfo object we get access to all the information and methods related with cultures in a system and with the
ResourceManager we get access to the resources related to a specific culture.
How is this? For instance, if we are going to make our application available in English as well as in Spanish, it doesn't mean that we have to write two different code list. The idea is having two resources files (one for each language, containing the equivalent strings).
Using the code
Let's get into the code creating the globalized HelloWorld which could divide two numbers (this is in order to see how the multi-language exceptions handling would be).
So, we want our program detect the actual machine configuration (specially the culture settings) and allow to change the language in run time. This is: if Windows(r) setting is configured as Spanish, the program will start in Spanish; nevertheless, at any time we could change the language to English.
Ok, now we start creating a Windows Application Project and add two textboxes and two command buttons to the main form:
The textboxes are intended to write two numbers for the program and divide them (in order to generate a multi-language exception when we try to divide by zero). The first brings up the Hello World message box and the second one makes the division of the numbers.
Now, we're going to add the two files we'll use. One, for the culture "es-CO" and the other one for the default culture: English:
The default source file will be called myRes.resx. The file for the "es-CO" culture MUST be called like this:
<default file name>.<culture>.resx. In this case: myRes.es-CO.resx. In this way if we were to include a resource file for "fr-CA", we would have to add the myRes-fr-CA.resx file.
Once the files have been added to the project, they look like this:
The relevant columns are name and value. For each string we need in our program, we have to add a dictionary pair name-value. For instance, we want the button's
text property change as well as the error messages, etc. The important thing is that same resources name have to be defined in all the files:
We can see that same names (though different values) are included in both tables... values for comment, type and mimetype aren't relevant for this application.
Now, we can add a
culture attribute to our form, in order to handle it easily; but first, let's include the appropriate namespaces to work comfortably:
After this, we can add the attribute I just mentioned, just after the declaration of the visual attributes (buttons, textboxes, etc.):
private CultureInfo culture;
In order to get the initial machine culture configuration just at the beginning of the application, we use the following command when the form is created:
You might want to change the language of the application in runtime... so let's add some more controls:
In this way, when a radio button is checked, the culture must be changed. For example, if the Spanish flag is checked the code would be like this:
Now... how to change the captions and messages??
We create a
ResourceManager object whose constructor receives in this example two parameters: the base name for the resource file (i.e. the namespace of the project containing the resources file and the root of the file name), and the assembly from which the resource is called; it is easy to identify it by using the reserved word
typeof and specifying the name of the form and its
Then, we can use the
rm object to get the required string by telling it the name of the string resource (as we named it in the table), and the culture for which the
ResourceManager have to seek (if our culture object actually is "es-CO",
rm will search in the file myResx.es-co.resx; otherwise, it will search in myResx.resx).
Points of interest
- I used the "division by zero" example to show how to handle exceptions in various languages, but actually, .NET knows how to divide by zero. Thus if you divide by zero, you get as a returned value, "Infinite", which is not an error. Though, in my code, I threw an exception when division by zero is detected.
- When you create some resources files, when building your applications, a DLL called <projectname>.resources.dll is created for each resource file, and stored in the resource/debug folder inside a subfolder called as the culture identification (in this example a folder called es-CO was automatically created inside the release folder, with a file inside it called HelloWorldGlobed.resources.dll). So don't forget to attach that folder when deploying your application!!!
- Globalization is not only for Windows applications... I have created Web Services and ASPX pages with this support; but in ASPX pages you have to be careful and ingenious with the state management in order to communicate the language specification between pages (using query strings) or between roundtrips of the same page (using view state, because security in this case is not an issue, so something like a session variable will be a waste).