small_flagsUntil a couple of years ago, most software applications were released in English. Unfortunately for us the developers, nowadays, many customers require that that the product they purchase, will be localized to a specific language (other than English). I know, for instance, that there is a European law which requires healthcare products to be localized to the European market (starting from 2009 or 2010). Because this article is about how to localize your application using string tables, I recommend you to first read about a free tool which helps you extract hard coded strings to string tables. Don’t go any further before you also read about how to generate public properties for string tables, you must read it.

Setting up a String Table

I assume that you already have some user interface which needs to be localized, I will demonstrate this process with Windows Forms but it doesn’t matter what UI framework is being used. The first thing to do is to add a string table, you can add it to your UI project or to another project (if it will be used from other assemblies as well). To add a string table, right click on the project (in Visual Studio solution explorer) and choose Add->New Item from the context menu. Then, select the Resources File Template and name it as you wish, I will name it UserMessages.resx. Add entries to the string table, like I did:

localizationstringtable

Using the String Table from the code

Thanks to Visual Studio, a class called UserMessages was auto-generated with some static properties which represent the String Table keys. Lets take a look at how I used this class from the UI code:

public partial class DemoForm : Form
{
    public DemoForm()
    {
        InitializeComponent();
        this.Text = UserMessages.Caption;
        m_label.Text = UserMessages.HelloWorldMessage;
    }
}

The form caption and the label text received their values from the String Table, so this is what we get when running the application:

formwithenglish


Adding another language

To support another language, we need to add another String Table to the same project. It must be named exactly like the existing String Table with a little add-on, the culture shall be appended to it just before the resx extension. If I want to add Spanish support to my demo, I need to add a string table which is named UserMessages.es-ES.resx. Notice to the es-ES, it is the Spanish (Spain) culture. After adding this table, fill it with the values translated to the language you need (Spanish in this demo):

spanish


Making it work with the new language

Well, all you have to do is to add the following line before any other code:

Thread.CurrentThread.CurrentUICulture = new CultureInfo("es-ES");

It will be best, if you add it in your exe project, in the main method. Run the program and there it is:

image

You can now add any language you want, and everything works just fine.

How does the magic works

You may have noticed that after adding another String Table, I only changed the current UI culture to Spanish – No code was changed, UserMessages class is used exactly the same as before. To understand how it works, we need to see the String Table generated code, what is behind those UserMessages properties? Here is the code behind the HelloWorldMessage property:

public static string HelloWorldMessage {
     get {
         return ResourceManager.GetString("HelloWorldMessage", resourceCulture);
     }
 }

There is a call to the ResourceManager.GetString method with the name of the String Table key and the resource culture. When looking at this method code with the Reflector, we can see that this is a long method. But this is what interest us:

  • If the resource culture is null (like in our case), the current UI culture is used.
  • When diving deep into that code, we can see that GetResourceFileName is used to select the correct resource file. Here is its implementation, see how it builds the correct resource name with the correct culture appended:
protected virtual string GetResourceFileName(CultureInfo culture)
{
    StringBuilder builder = new StringBuilder(0xff);
    builder.Append(this.BaseNameField);
    if (!culture.Equals(CultureInfo.InvariantCulture))
    {
        CultureInfo.VerifyCultureName(culture, true);
        builder.Append('.');
        builder.Append(culture.Name);
    }
    builder.Append(".resources");
    return builder.ToString();
}

 

Summary

I think that it is really easy to localize your application. As long as you stick with the idea of getting every string from a String Table, you’re OK. If a support for another language is needed, just add the appropriate tables and without really touching your code, the application is localized. I would recommend to set the UI culture by reading it from a configuration file, so that you won’t need to even change that line. That is it, if you have any question, please fill welcome to leave a comment.

Tags :

8 Responses to “How To Localize Your Application Using String Tables”


  1. christian

    Said on October 21, 2008 :

    You might want to consider using “summary” and not “summery” to conclude your article ;-)

  2. Shahar Y

    Said on October 21, 2008 :

    @ christian

    Thanks, problem fixed.
    I was typing my thought and didn’t notice my typo, but why didn’t Windows Live Writer notice it?? ;)

  3. Yves

    Said on October 23, 2008 :

    Nice, good article, but I have one question regarding Forms design: How can I select a string table entry for a form text, like a window title or a button text, without adding custom code that reads the string table entry and assigns it to the control? I can assign images from files or resources, but how does it work with texts? Does it work at all?

    I know there are several methods to translate texts in a GUI to different languages, of which this is probably the most elegant. But I’m not coding the texts, I’m typing them in the visual designer. And everything that breaks out of this workflow is complicated and tends to be omitted…

  4. Shahar Y

    Said on October 23, 2008 :

    @ Yves

    You don’t have any choice but adding custom code.
    This is VERY simple:
    m_text1.Text = MyStringTable.Text1;
    m_text2.Text = MyStringTable.Text2;
    m_text3.Text = MyStringTable.Text3;

    Add it in the constructor after the InitializeComponents() metod.

    You insert the texts to the string tables and not to the designer.

  5. Gadi

    Said on October 27, 2008 :

    When one deals with applications bigger than “hello world” size, one should use translation tools to handle all languages, rather than manually do it.
    These tools allow you to automatically scan for changes in your string tables, send the delta to the translation agency, detect common translation errors, automatically translate repeating terms, and many more. (Readers may wish to look at Sisulizer, Multilizer, Alchemy CATALYST and other tools)
    The process is much more complex than just externalizing the strings, although this is definitely the basic stage.

  6. Asterix

    Said on March 7, 2009 :

    I have a problem using the resx file. I created it, added some strings in it. It’s listed in resources files (myMessages.resx) in my project, but when I try to use it this way:

    MessageBox::Show(myMessages.Test1);

    it doesn’t work. Compiler says “.\XXX.cpp(21) : error C2065: ‘myMessages’ : non declared identifier

    When and how is this auto-generated class created? Which file is it?

    Do I have to #include something? or add a using namespace something?

    Thanks for your help

  7. Shahar Y

    Said on March 8, 2009 :

    @ Asterix

    I understand that you are using C++, while the above solution works for C# or VB.net.
    The generated code contains properies which doesn’t exist in C++, so it can’t work for C++

  8. Dean M

    Said on February 16, 2011 :

    I’m using the refractor tool and method suggested, developing in Visual Studio 2008. It works great on my development machine (Windows Vista, .NET 3.5). However, when I install the app on other machines (XP, .NET 2.0 SP2 & Windows 7, .NET 4.0), the culture is ignored and only the default culture is used. My app is built for .NET 2.0 since my customer’s requirements are that the app be backward compatible.

    What am I missing? I attempted to install the refractor dlls on the other machines, but they won’t install without Visual Studio installed first, which end users won’t have. Any ideas why the built app won’t work for my other machines/OS’s?

Post a Comment