Home > Localization > Localized Value Formatting in WPF

Localized Value Formatting in WPF


When I introduced the WPFGlue localization components, I concentrated mainly on displaying localized strings and other static information in the user interface. Today I want to complete the WPFGlue localization section by discussing localized value formatting.

The Problem

Number and date formats vary from culture to culture. 30/12/2009, 12/30/2009 and 30.12.2009 all refer to the same day, and 12.345,67 and 12,345.67 are actually the same number, but will turn out very different if the computer doesn’t know which format the user assumed. Since the early days of computers, this problem has been solved by defining the culture under which the system runs as a system property. Then there would be a set of predefined formats for each given culture which implement the usual standards in that culture. Applications would take number and date formats from these system settings, so they could use the local standards even though they were developed in a different language.

A special case of this problem are enumerated lists. Usually, there is a short text that describes each option. In a localized application, this text should be translated according to the current culture, and it would also be necessary to sort the list differently in different languages in order to keep up an alphabetic order of the items.

Specifying the Formatting Culture in WPF

In WPF, value formatting is part of data binding: if a control needs to display a bound value, it converts it into a string and displays the string. The culture which is used can come from three sources:

  • Default: if you don’t do anything special to tell the system from which culture it should derive its number and date formats, it will just take standard US English. Makes sense to define one culture so that by default the application will look the same on whatever system it runs. But actually, in the rest of Dot.NET the rule is that the application should take the installed system culture as default for these purposes, so WPF breaks the expectations of especially us poor Non-Americans…
  • The Language property or xml:lang attribute: WPF FrameworkElements have a Language property, which can accept a IETF language tag like en-US or de-DE, and will set the date and number formats according to this language and culture’s properties. The definitions for the formats come from a set of preinstalled cultures which are part of the Dot.Net framework. The Language property is inherited, so if it is specified on top of a page, it will take effect on the whole page.
  • The ConverterCulture property on the Binding: This property accepts a Globalization.CultureInfo object. This object might come from any source. Especially, if the user changed the standard number and date formats on the system using the Language and Regional Settings in Control Panel, these changes will be visible in the CurrentCulture object of the application, while they won’t be visible if one just sets the Language property.

So, setting the ConverterCulture on the bindings would be the preferred way to respect the system’s and user’s formatting preferences. However, there are two drawbacks: first of all there is no built-in way to get at the system culture in XAML, and second one would have to specify the culture on each and every binding, since there is no way to apply this setting in a way that is global to the application.

In order to solve this problem, I added another MarkupExtension to the WPFGlue localization namespace: Binding. This is actually nothing more than a normal Binding with the ConverterCulture preset to the CurrentCulture of the system:

Namespace Localization
    Public Class Binding
        Inherits System.Windows.Data.Binding

        Public Sub New()
            MyBase.New()
            ConverterCulture = System.Globalization.CultureInfo.CurrentCulture
        End Sub
        Public Sub New(ByVal path As String)
            MyBase.New(path)
            ConverterCulture = System.Globalization.CultureInfo.CurrentCulture
        End Sub
    End Class
End Namespace

 

So, one can use it everywhere instead of the default Binding, and get date and number formats that behave according to the system settings. I’m not especially proud of this solution, but for the time being it seems to be the best compromise.

The LocalizationExample which you find in the WPFGluePublished solution on the Downloads page demonstrates this behaviour. This XAML code:

    Title="LocalizedPage" Language="{l:UILanguage}" FlowDirection="{l:FlowDirection}">
    <Page.Resources>
        <s:DateTime x:Key="Value">#12/30/2009#</s:DateTime>
    </Page.Resources>
    <Grid>
        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" >
            <TextBlock Text="{l:String TestString}" />
            <TextBlock Text="{Binding Source={StaticResource Value}}"/>
            <TextBlock Text="{l:Binding Source={StaticResource Value}}"/>
            <Image Stretch="None" Source="{l:BitmapImage FlagURI}"/>
        </StackPanel>
    </Grid>
</Page>

 

produces the following output:

image

 

Notice that setting the page’s Language property to the UILanguage (German in my case) causes the first binding to display its value in the default German date format, while using the localized binding causes the same value to be displayed in the format YYYY-MM-DD, which is what I specified in Control Panel as the standard short date format.

Localized Lists

In Dot.NET, sets of options can and should be summarized in enumeration types, with a named constant for each available option. The Dot.NET formatting engine, which is called through the String.Format method, supports converting the numeric enumeration values to strings and vice versa using the constant names that are defined for the enumeration in code. However, this option is only useful for debug output. If the values must be displayed to users, they must be translated into their languages, and actually this is not the job of the code which defines the enumeration.

A user interface which wants to use enumeration values needs the following functions:

  • translate numerical values to string descriptions (for read-only display of values).
  • translate string descriptions into numerical values (for cases where the user can enter text).
  • create a mapped list of values and descriptions (for ComboBoxes and the like where the user just picks a value from a list).

The LocalizedList class provides these functions. It works both as a collection of value / description pairs and as a IValueConverter that converts the descriptions forwards and backwards.

In order to define a LocalizedList, you have several options:

  • Just set the ValueEnum to an enumeration type and let the LocalizedList generate the entries from the enumeration’s definition (actually, this is not localizing anything yet…)
  • Set the LocalizedList’s Definition property to a string in the format “value1=name1,value2=name2…”, where the values are the constant names or values from the enum definition, and the names the localized display names. Typically, the definition string would itself be a localized string resource.
  • Define Entry objects directly in the XAML, which map string keys to arbitrary typed values.

The definition string format is actually quite flexible. So, you can change the default list and value separator characters (“,” and “=”). You could also leave out the values and just specify the enumeration type, so that the LocalizedList assumes that you specified the descriptions for the numeric values in their natural order. On the other hand, you could specify both names and values, but change the order, or use a subset of the available values, only. And finally, you could leave out the ValueEnum altogether and use String codes instead, as it is done in the language selection list in the example:

<l:LocalizedList x:Key="languageList" Definition="{l:String LanguageList}"/>

where e.g. the English version of the referenced string resource is

de-DE=German,en-US=English,ar-SA=Arabic

The other LocalizedList in the example maps String keys directly to objects:

<l:LocalizedList x:Key="assemblies">
    <l:Entry Key="{l:String InternalLabel}" Value="{x:Null}"/>
    <l:Entry Key="{l:String ExternalLabel}" Value="{x:Type r:Dummy}"/>
</l:LocalizedList>

 

Using this list, it is possible to change the resource assembly of the example purely in XAML, with no code behind:

<ComboBox x:Name="SelectAssembly" ItemsSource="{StaticResource assemblies}"
          DisplayMemberPath="Key" SelectedValuePath="Value"
          SelectedValue="{Binding RelativeSource={RelativeSource
          Mode=FindAncestor, AncestorType={x:Type NavigationWindow}},
          Path=(l:Localization.ResourceReferenceType)}"/>

 

Conclusion

Using localized resources on the one hand and providing localized value formatting on the other hand are about all that comes to my mind under the heading of localization. I’ll be glad to react to feedback or criticism…

Categories: Localization Tags: , ,
  1. John Lagonikas
    December 22, 2010 at 16:08

    It seems that adding this tag

    Language=”{Binding Source={x:Static glob:CultureInfo.CurrentCulture}, Path=Name, ConverterCulture={x:Static glob:CultureInfo.CurrentCulture}}”

    to your root element will set the culture to the system default (glob is a mapping to the System.Globalization namespace).

    • John Lagonikas
      December 22, 2010 at 16:08

      (I meant attribute, not tag)

      • December 24, 2010 at 11:10

        Hi,

        yes, that would probably work… In the first case, the TypeConverter of the Language property would convert the name of the current culture to a XmlLanguage object, in the second case, the ConverterCulture would be set to the system culture…

        So, in fact there is a built-in way to access the system culture, thanks for pointing this out.

        Anyway, I would have preferred if Language and ConverterCulture used the same type, and the syntax was more concise.

  1. No trackbacks yet.

Leave a reply to John Lagonikas Cancel reply