How to localize Silverlight Applications the smart way

Introduction:
A very common task when developing business applications is to localize them for english, german and other languages. The most common approach is to use ResX files in order to create resource classes which can be used to bind the silverlight controls to. This approach is perfectly described by Sly (that is his nickname). Due to a bug in the resource generator from Microsoft, you always have to change the created resource class from internal to public (look at point 17 on his blog entry), which is very annoying … That’s why I was searching for a better solution to localize silverlight applications – and I think I found a really smart way to do that task.

Silverlight Localization (the smart way):
Step1:
ResourceFilesFirst of all you need a silverlight application;) If you already have one – fine. If not, create a new example application. After that you have to add a new resource file. First of all, add a fallback resource file which can be used as an alternativ, if the string can’t be found in one of the language dependend resource files. After that add a resource file for every language you want to add (and call it like the neutral resource file with the postfix of your designated language).

Step2:
Because we go the smart way, it’s important to disable the code generation. In order to do that, open the neutral resource file with a double click in Visual Studio and switch the Access Modifier to “No code generation”.
AccessModifier

Step3:
Now add some string resources to the resources files …

Step4:
Microsoft does not really very much to support the localization task, because by default, the resource files won’t be included in the created XAP file. To include the language depended resources into the XAP file, you have to open the project file (.csproj) in your favored text editor (e.g. Notepad++) and add the languages comma separated to the <SupportedCultures> Tag.

<SupportedCultures>de,en</SupportedCultures>

Step5: (We get it soon)
Now we can implement a new class that reads the resources and returns the localized strings.

public class ApplicationResources : IValueConverter
{
    /// <summary>
    /// The Resource Manager loads the resources out of the executing assembly (and the XAP File where there are embedded)
    /// </summary>
    private static readonly ResourceManager resourceManager =
    new ResourceManager("SilverlightLocalization.Resources.Strings",
    Assembly.GetExecutingAssembly());
    
    /// <summary>
    /// Use this property to specify the culture
    /// </summary>
    private static CultureInfo uiCulture = Thread.CurrentThread.CurrentUICulture;
    public static CultureInfo UiCulture
    {
        get { return uiCulture; }
        set { uiCulture = value; }
    }
    
    /// <summary>
    /// This method returns the localized string of the given resource.
    /// </summary>
    public string Get(string resource)
    {
        return resourceManager.GetString(resource, UiCulture);
    }
    
    #region IValueConverter Members
    
    /// <summary>
    /// This method is used to bind the silverlight component to the resource.
    /// </summary>
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var reader = (ApplicationResources)value;
        return reader.Get((string)parameter);
    }
    
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        // ConvertBack is not used, because the Localization is only a One Way binding
        throw new NotImplementedException();
    }
    
    #endregion
}

Step6:
Now we have to publish the ApplicationResources class as a Application Resource in order to use it for our binding. To to this, open the App.xaml and extend the Application.Resources.

<Application.Resources>
<SilverlightLocalization:ApplicationResources x:Key="Localization"/>
</Application.Resources>

Step7 (last step):
Yeahh – we can bind the silverlight components now. Therefore replace the content of the Text attribute of the component to localize with the following generic replacement: {Binding ConverterParameter=Page_label01, Converter={StaticResource Localization}, Source={StaticResource Localization}}

e.g. “Page_label01″ is the name of the label specified for that component within the resource file.

The complete example could look like this:

<TextBlock
FontSize="32"
TextAlignment="Center"
Margin = "0,200,0,0"
Text="{Binding ConverterParameter=Page_label01, Converter={StaticResource Localization}, Source={StaticResource Localization}}"
MouseLeftButtonUp="OnClick"
/>

Advantages of this solution:
First of all, I think this solution is very smart, because you don’t need to update the class modifier from internal to public, everytime you change the resource strings.
A second advantage is, that the XAP file is not as big, as if you would use code generation. Why? Because you simply don’t have to create tons of resource access properties. Every resource is loaded within the ApplicationResource class.

Example:
With the following link you can try a working example: Click me Example Application
And here’s the example source code to download: Smart Silverlight Localization (You have to set SilverlightLocalizationTestPage.aspx as the Start Page)

Hope you enjoy this solution.
Cheers
- Gerhard

kick it on DotNetKicks.com

About these ads
Posted in .NET, C#. Tags: . 18 Comments »

18 Responses to “How to localize Silverlight Applications the smart way”

  1. Attila Says:

    Hi Stephan!

    By using Dmytro’s ResXFileCodeGeneratorEx (http://dmytro.kryvko.googlepages.com/) generator the only thing you’ve to take care is adding the supported cultures to the csproj file.

    What is a big win with his codegenerator is that you get formatting methods as well for resource strings which contains formatting parameters.

  2. GEB Says:

    The only way I have found to access the strings from code-behind is something like:

    var reader = new ApplicationResources();
    this.MyTextBlock.Text = reader.Get(“Page_label01″);

    Is there any way for ApplicationResources to be static so that it never has to get allocated?

  3. Thierry Says:

    Hi,

    Really cool code and actually works both in .NET 2008 & Blend which is great. Just a quick question… How would you handle multiple resource files with different name as this seems to be hardcode in your class i.e. SilverlightLocalization.Resources.Strings

    In my scenario, I have numerous resources files but these weren’t added to a dictionary when originally created and this is giving me serious grief in MS Blend.

    Thanks.

    Thierry

  4. Danilo Says:

    Geb, to be static you must change

    public string Get(string resource)
    by,
    public static string Get(string resource)

    and replace the contents of method Convert(xxx…)
    by
    return ApplicationResources.Get((string)parameter);

  5. Danilo Says:

    Full Code:

    private static readonly ResourceManager resourceManager = new ResourceManager(“APPNAME.Resources.Strings”, Assembly.GetExecutingAssembly());

    public static string Get(string resource)
    {
    return resourceManager.GetString(resource, Thread.CurrentThread.CurrentUICulture);
    }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
    return ApplicationResources.Get((string)parameter);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
    throw new NotImplementedException();
    }

  6. Michal Says:

    Hi!
    Nice work just one question though.
    I am currently using RIA Services to generate an application and your code works great but it doesn’t localize the [Description] attribute that can be placed on class members.

    Any idea on how this can be done?

    Cheers

  7. Gerhard Says:

    The [Description] on class members has nothing to do with localization of applications, but to answer your question: You can use the WCF Extras Library (http://wcfextras.codeplex.com/) to export Service Method descriptions.

    Cheers
    - Gerhard

  8. Michael Says:

    Hi,

    This looks really nice. One thought I have is that different laguages might create buttons of different sizes – and that is fine (in my case), but changing the language, might change the layout of the page in the blink of an eye.

    I would like to add some kind of animation, like a 3D flip-over effect, so you
    1) flip the page 90 degrees (so you only see the thin side of the paper)
    2) do the LayoutRoot clear and instantiate a new page.
    3) finish the flip, and the user sees a new page with the new language.

    Being new to SilverLight I really don’t know how to do this – can you help adding a little GUI-coolness to this otherwise very smart way of doing localization?

    Cheers
    Michael

  9. Alexander Says:

    I’ve “en”,”de” and “ru” localization resourses.
    “en” is setup as a neutral language by attribute
    [assembly: NeutralResourcesLanguageAttribute("en")] in AssemblyInfo.cs
    But When I setting up Japan language in Application_Startup in App.xaml.cs (Thread.CurrentThread.CurrentCulture = new CultureInfo(“ja-JP”);
    Thread.CurrentThread.CurrentUICulture = new CultureInfo(“ja-JP”);), converter always get “ru” strings insted “en” as default.

    Why it is happened?

  10. Miguel Madero Says:

    Gerhard,

    Nice post and I agree this is somewhat better, however you loose some designer support. If you create the binding expression in Blend/VS10, you need to know the name of your resource. If you use a ResourceWrapper for the generated class, then you can just bind to a property known at design time. I would look into an approach that can have a bit of both, the class for design time, but then ignore this class for the Release build.

    Not that I’m a big fan of creating my BindingExpressions using the designer, but some of my customers are really concern about this and some developers are used to this UX when coming from WinForms

    Another thing that concerns me is how to load this resources from stallite assemblies (or satellite XAPs on demand). This would really reduce the size of the download because in your example, although you got rid of the generated code, you’re still embedding all those resx files as Assembly Resources.

  11. Miguel Madero Says:

    Another drawback of this approach is that you’re already using a converter, which is sort of limiting in case you really need to use a Converter for something else.

  12. arous Says:

    Thanks, very helpful

  13. Dr. Armen Says:

    Sorry guy, it’s not smart at all. It’s more a nice alternative.
    But you mess up with an important thing. Your code can lead to serious errors.

  14. Mariana Says:

    well this sample was working very good on SL3 but now i used it on SL4 using VS 2010 and it’s not working.

    it reads from the resx but it doesn’t swap between languages
    this part is not working
    LayoutRoot.Children.Clear();
    this.LayoutRoot.Children.Add(new MainPage());

  15. rminchev Says:

    Really helpful.
    Thanks

  16. Vu Nguyen Says:

    Yes, really helpful.
    Thanks
    Vu

  17. John Says:

    Your sample links are broken.
    Can you fix them?
    Thanks!


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 106 other followers

%d bloggers like this: