August 07, 2008

Localization and Globalization

Using Localization from Code

ASP.NET provides all the Localization you need by just adding .RESX files to your Web App in the right location but sometimes you want to invoke the Localization yourself. To do this you'll use the System.Resources.ResourceManager class. This allows you to get resources by simply supplying the name of the resource and the Culture but to setup the resource itself is a bit trickier (in Visual Studio 2005) as some of the files required have been removed from Visual Studio's Templates. Because of this you'll need to use some tools to create the Resource (and optionally the Assembly to store it know as Resource Assemblies or Satellite Assemblies).
There are 3 steps to creating resources for use with ResourceManager:
1. Write a text file or .RESX file to hold your resources.
2. Compile the resources in step 1 into a .RESOURCES file.
3. Compile the .RESOURCES file in step 2 into a Satellite Assembly.
4. Locate the Satellite Assembly from step 3 into a location which is recognised by the Localisation mechanism.

1. If your resources are all strings then the easiest thing to do is add them to a .TXT file. If your resources are more than strings e.g. images then you should probably use a .RESX file. .RESX files are the usual location for resources. These are added to your Class Library Project in VS by selecting the project and clicking the Add File and then choosing Resource File.
If you wish to use a .TXT file then here's how you enter the resources:
myFirstResource = here is my first resources value
mySecondResource = here is my second resources value
(Note that the .TXT file must be saved with UTF-8 support, you'll probably find this in your text editor save options somethere, notepad has it, this is required to support some culture specific characters like accents).

2. The .TXT file or .RESX file is not enough for the ResourceManager, this .RESX file has to be converted to a .RESOURCES file before it can be used, this can be done with the tool, ResGen.exe,
resgen strings.en.txt strings.resources
resgen strings.es.txt strings.es.resources.

for more see http://msdn.microsoft.com/en-us/library/xbx3z216(VS.80).aspx

If you wish to use these bare .RESOURCES files then you can by following this http://msdn.microsoft.com/en-us/library/khyt7e7y.aspx but note that there are issues with Concurrent access on IIS so you may want to embed the resource in an Assembly (next step).

3. This .RESOURCES file can then be embedded in your DLL for later use by the ResourceManager but I found some little caveats here which cost me at least 1 day to figure out. This caveat is that the naming of the .RESOURCES files is also used as well as the DLL.
What I found it that for other cultures the .RESOURCES file must be named with that Culture as it does get used e.g.
The Default culture is English (en), the other culture used is Spanish (es).

You create 2 .TXT files to store the resources
strings.txt and strings.es.txt.

You then generate 2 .RESOURCES files from these with, it is these files that must have the correct naming i.e. the Spanish version must have the ".es" in the name before the extension.
resgen strings.en.txt strings.resources
resgen strings.es.txt strings.es.resources.

Now you use a Linker tool call AL.exe to embed each resource into it's own dll, note that you supply the culture here at the command line and also you must name the DLLS for the Cultures other than the default to have the ".resources" before the extentsion:
al /embed:strings.resources /culture:en /out:strings.dll
al /embed:strings.es.resources /culture:es /out:strings.resources.dll

You now copy only the .dll into the correct directories i.e. if it's aWebsite the root directory is the "bin" directory and it it's a standalone app then just copy into the same dir as the .exe.
Place the default assembly. strings.dll into the root and them create a subdirectory "es" and place the Spanish DLL in there
e.g.
/bin/strings.dll
/bin/es/strings.resources.dll

for more see http://msdn.microsoft.com/en-us/library/21a15yht(VS.71).aspx

4. If you use the bare .RESOURCES file then you can use the method described here http://msdn.microsoft.com/en-us/library/khyt7e7y.aspx. All you need to do is name the files uniquely using the pattern described, e.g. strings.es.resources for Spanish. The use the ResourceManager _rm = ResourceManager.CreateFileBasedResourceManager("strings",Path.Combine( AppDomain.CurrentDomain.BaseDirectory, "Resources" ), null);
CultureInfo cInfo = new CultureInfo(culture);
string result = _rm.GetString(resourceStringName, cInfo);

If you use an Assembly to store your Resources then you cannot use the CreateFileBasedResourceManager instead you must use the ResourceManager's constructor.
Assembly stringsAssembly = Assembly.Load("strings");//the default assemblies name see step 3.
ResourceManager rm = new ResourceManager("strings", stringsAssembly );//"Strings" is the resource name of the default Culture i.e. strings.resource that you embedded earlier in the DLL.
string result = _rm.GetString("myFirstResource", new CultureInfo("es"));//get me the resource from the Spanish Culture DLL.

2 comments:

Jems Nichole said...

The information that you provided was thorough and helpful. I will have to share your article with others
TechWhirl

Parov Hefe said...

If you're interested in .resx translation management tools, you should have a look at the software localization platform POEditor, because it has a very nice and flexible interface that you can use to translate strings collaboratively.