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:
First 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”.
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
June 24, 2009 at 12:14 pm
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.
July 28, 2009 at 12:46 am
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?
July 31, 2009 at 12:13 pm
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
August 8, 2009 at 12:13 pm
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);
August 8, 2009 at 12:29 pm
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();
}
September 25, 2009 at 6:13 am
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
September 25, 2009 at 6:40 am
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
October 21, 2009 at 6:37 am
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