WPF: Localization Guideline

April 1, 2010

no comments

So, What is new?

We all know that the WPF Application are pro – localization architecture and we can set it by BAML or Resx (I prefer to use the *.resx files). Till now I believe that I’m an old news, but this post is not about how can i do it, but I should I do it.

In this post I will set the localization by *.resx files and use the MVVM design pattern

Main milestones to consider while writing the app:

  • Support languages like Hebrew and Arabic: Left –>Right, Right –> Left: the window should mirror all the controls
  • Support language like Chinese: the letters font are bigger / higher  than the usual
  • Support language like German: statistically has the longest words

When design a window/ page or any UI control, the main goal is that:

  1. Will do what he needs to do (Functionality)
  2. Will be user friendly, the way it build, the size, font, colors . . . 

The first case is on the BLL side, but the second one is UI. So you design the UI by using the best consultants and it Stunning!!!. Then you change the application localization and . . . all the UI distorted.

So, the main idea is to block the area of the controls that will contain the localization value (TextBlock, TextBox, Image and so)

High / Long Languages:

For example:

<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<
Grid>
<
Grid.ColumnDefinitions>
<
ColumnDefinition Width="auto"/>
<
ColumnDefinition Width="*"/>
</
Grid.ColumnDefinitions>

<
TextBlock Grid.Column="0" Grid.Row="0" Text="{Binding nameLbl}"/>
<
TextBox Grid.Column="1" Grid.Row="0" Text="{Binding nameLbl}"/>
</
Grid>
</
Window>

 

On this case the TextBlock has Auto Width, if the text will be longer than the text that used on design time it will come over the TextBox area. Change the text English “Enter the password again:”  to “Geben Sie das Kennwort erneut ein:” in German, and your UI changed

So, border it, design our UI with the worst case scenario.

For example:

<Window 
x:Class="LocalizedLoginForm.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
SizeToContent="WidthAndHeight">
<
Window.Resources>
<
Style x:Key="localizeTextBlock" TargetType="{x:Type TextBlock}">
<
Setter Property="TextWrapping" Value="WrapWithOverflow"/>
<
Setter Property="VerticalAlignment" Value="Top"/>
<
Setter Property="Margin" Value="7,2,7,2"/>
</
Style>
   <Style x:Key="localizeTextBox" TargetType="{x:Type TextBox}">
<
Setter Property="VerticalAlignment" Value="Center"/>
<
Setter Property="Margin" Value="0,2,0,2"/>
</
Style>

</Window.Resources>
<Grid>
<
Grid.ColumnDefinitions>
<
ColumnDefinition Width="140"/>
<
ColumnDefinition Width="170"/>
</Grid.ColumnDefinitions>

<
Grid.RowDefinitions>
<RowDefinition MinHeight="27" MaxHeight="47" Height="auto"/>
</
Grid.RowDefinitions>

<TextBlock x:Name="nameLable" 
Text="{Binding Path=namelbl, Source={StaticResource Resources}}"
Grid.Column="0" Grid.Row="1" Style="{StaticResource localizeTextBlock}"/>
<TextBox x:Name="nameValue" Grid.Column="1" Grid.Row="0" Margin="7,1.5"
Style="{StaticResource localizeTextBox}"/>
      </Grid> 
</Window> 

On this case the TextBlock has 140pt Width, if the text will be longer than the text that used on design time it will wrap, the Row Definition give us space for three text rows in length of 140pt. The Column and Row Definition is up to you, translate the text to any language you want to support, it should not triple the size it take.

Also on this case, we solve the issue with the fonts that has high letters Chinese.

Support Left –>Right, Right –> Left languages:

In this case WPF solve it with one Property ‘FlowDirection’ that should be set on the main UI container (even that it can be done on each control). The idea to set it on the main container and all the contained controls will take the ancestor definition.

Change the Localization during runtime:

First, I don’t believe and like the idea that the application should restart for localization change request, if it supported on runtime do not interfere the User and restart the application!!!

Now, there are two triggers for it:

  1. The User has pool of supported languages and he can switch between them
  2. The application localization will be by the System / Machine display language (or default if not exist)

Which one to choose, it’s up to you!!!. But on both cases the localization values must be bonded to the the controls and should be effected by the Culture change.

How to do it???

  • Create valid *.resx files for all the resxSamplelanguages you want to support

 

 

 

 

  • Create class that will manage the Culture and FlowDirection change and invoke the change to the controls that use the localization

For example:

On the Code side:

   1: public partial class CultureResources : BasicNotifyPropertyChangedObject

   2:     {

   3:         public Properties.Resources GetResourceInstance()

   4:         {

   5:             return new Properties.Resources();

   6:         }

   7:  

   8:         private static ObjectDataProvider resourceProvider;

   9:  

  10:         public static ObjectDataProvider ResourceProvider

  11:         {

  12:             get

  13:             {

  14:                 if (resourceProvider == null)

  15:                     resourceProvider = (ObjectDataProvider)App.Current.FindResource("Resources");

  16:                 return resourceProvider;

  17:             }

  18:         }

  19:  

  20:          private FlowDirection applicationtDirection;

  21:  

  22:          public FlowDirection Direction

  23:          {

  24:              get { return applicationtDirection; }

  25:              set

  26:              {

  27:                  applicationtDirection = value;

  28:                  OnPropertyChanged("Direction");

  29:              }

  30:          }

  31:  

  32:         public static void ChangeCulture(CultureInfo culture)

  33:         {

  34:             Properties.Resources.Culture = culture;

  35:             ObjectDataProvider provider = (ObjectDataProvider)App.Current.FindResource("CultureDirection");

  36:             if (provider != null)

  37:             {

  38:                 CultureResources controller = provider.Data as CultureResources;

  39:                 controller.Direction = Properties.Resources.Culture.TextInfo.IsRightToLeft ? FlowDirection.RightToLeft : FlowDirection.LeftToRight;

  40:             }

  41:             ResourceProvider.Refresh();

  42:         }

  43:     }

On the XAML side (App.xaml):

<Application x:Class="LocalizedLoginForm.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cultures="clr-namespace:LocalizedLoginForm">
<
Application.Resources>
<
ObjectDataProvider x:Key="Resources"
ObjectType="{x:Type cultures:CultureResources}" 
MethodName="GetResourceInstance"/>

<
ObjectDataProvider x:Key="CultureDirection"
ObjectType="{x:Type cultures:CultureResources}"  />

</
Application.Resources>
</
Application>

On the bonded container control, like on Window

 

<Window 
x:Class="LocalizedLoginForm.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
SizeToContent="WidthAndHeight"
FlowDirection="{Binding Path = Direction, Source ={StaticResource CultureDirection}}">
...

On the bonded controls, like on TextBlock control [Text] element

<TextBlock x:Name="surnameLable" 
Text="{Binding Path=surnamelbl, Source={StaticResource Resources}}" ...

Explication:

The control / UIElement [“surnameLable”], his Text property bind to the static resource [“Resources”] that defined on the application resources (App.xaml). The [“Resources”] that get the Resources instance by calling the [“GetResourceInstance()”] method. also this class has static method [“ChangeCulture(…)”] that change the Resource Culture and FlowDirection and trigger the controls to update their text value (with the new cultured value)

 

Conclusion

Localization is easy and quick with WPF architecture, but remain with the same appearance is more difficult!

You can download code sample from here: LocalizedLoginForm.zip

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*