Perspective : a light process for Silverlight localization

Perspective : a light process for Silverlight localization

Principle and implementation

Silverlight 2.0 doesn't provide an high-level localization mechanism.

So I developed in Perspective for Silverlight a technique using classic .NET string. resources for all textual properties. The standard satellite assembly mechanism is used to deploy the resources for a given culture.

This technique is based on the one proposed in Perspective for WPF . But it is not possible to develop custom markup extensions in Silverlight 2.0. The solution is to define the localization information into a collection associated with the container of the controls to translate, through an attached property. This principle is used to manage the states by the VisualStateManager class.

Implementation :

  • Add in the project a .resx resource file for each XAML file, in the base language of the application, and create a resource for each text to be localized. I.e., for a given XAML file Page2.xaml, create a Page2.resx file in a subfolder named Strings (subfolder of the folder where the XAML file resides). In the Access modifier field, choose No code generation.

Visual Studio : Default resources (English language)

  • Add in the project a .resx resource file for each targeted culture, i.e. Strings\Page2.fr.resx, and translate the resources.

Visual Studio : French language resources

  • Add a reference in the project on the Perspective.Wpf assembly (from Perspective for Silverlight).
  • In the XAML file (Page2.xaml in our case), add an XML namespace referencing this assembly and the .NET namespace Perspective.Wpf.ResourceStrings, and then declare the ResourceStringManager.Groups attached property.
<UserControl x:Class="PerspectiveDemo.UI.Pages.Page2"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:prs="clr-namespace:Perspective.Wpf.ResourceStrings;assembly=Perspective.Wpf">
    <Grid x:Name="LayoutRoot" Background="White">
        <prs:ResourceStringManager.Groups>
...
        </prs:ResourceStringManager.Groups>
        <StackPanel
            HorizontalAlignment="Center"
            VerticalAlignment="Center">
            <TextBlock 
                Name="tbMain"
                FontSize="36" />
            <Button Name="b1" Background="Red"/>
        </StackPanel>
    </Grid>
</UserControl>
  • Insert a ResourceStringGroup in the ResourceStringManager.Groups property.
  • Indicate in the AssemblyName property the name of the assembly, without extension, and in the BaseName property the name of the .resx file, without extension and prefixed with the path in dot notation, i.e. :
<prs:ResourceStringManager.Groups>
    <prs:ResourceStringGroup AssemblyName="PerspectiveDemo.UI.Pages" BaseName="Strings.Page2">
...
    </prs:ResourceStringGroup>
</prs:ResourceStringManager.Groups>
  • For each property to localize, insert in the ResourceStringGroup a ResourceStringLink element, and define the TargetName (taget element), TargetProperty and ResourceName (name of the resource in the .resx file) properties.
<prs:ResourceStringManager.Groups>
    <prs:ResourceStringGroup AssemblyName="PerspectiveDemo.UI.Pages" BaseName="Strings.Page2">
        <prs:ResourceStringLink ResourceName="MainText" TargetName="tbMain" TargetProperty="Text" />
        <prs:ResourceStringLink ResourceName="ButtonText" TargetName="b1" TargetProperty="Content" />
    </prs:ResourceStringGroup>
</prs:ResourceStringManager.Groups>
  • To make the system operational, it is necessary to register the assembly in the internal static dictionary of the ResourceStringManager class, with its RegisterAssembly() method. The Perspective example application calls this method in the page constructor, before XAML loading :
public Page2()
{
    ResourceStringManager.RegisterAssembly(this.GetType().Assembly);
    InitializeComponent();
}

By default, satellite assemblies produced by the compilation are not embedded in the .xap file. To embedd the satellite assembly for a given culture, it must be specified in the .csproj file. It is therefore advisable to create a compilation configuration by culture, for example Debug FR:

<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug FR|AnyCPU' ">
...
  <SupportedCultures>fr</SupportedCultures>
</PropertyGroup>

At runtime, the resources corresponding to the current culture (Page2.fr.resx for the French language) are automatically loaded by .NET.

Execution : French language resources

If the satellite assembly is not found for the current culture, default resources are loaded (Page2.resx).

Execution : Default resources (English language)

To force the loading of a given culture resources, it is possible to specify the uiculture parameter in the URL :

http://localhost:49658/PerspectiveDemoTestPage.aspx?uiculture=fr

It is also possible to specify the uiculture parameter in the HTML object element :

<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%">
    <param name="uiculture" value="fr" />
...
</object>

It is also possible to specify this parameter in the InitParameters property of the Silverlight ASP.NET server control :

<asp:Silverlight InitParameters="uiculture=fr" ...>

The parameter is processed in the Application.Startup event :

private void Application_Startup(object sender, StartupEventArgs e)
{
...
    ParametersHelper.LoadInitParametersFrom(e.InitParams);
    string uiCulture = ParametersHelper.UICultureValue;
    if (!uiCulture.Equals(Thread.CurrentThread.CurrentUICulture.Name))
    {
        Thread.CurrentThread.CurrentUICulture = new CultureInfo(uiCulture);
    }
...
}

The culture change at run time can be done by reloading a package containing resources for the targeted culture. This avoids embedding the resources of many cultures in one big package, which might be too slow to download.

About this article

Author : Olivier Dewit.

History :

  • January 26, 2009 : 1st publication (Perspective for Silverlight, version 0.9)