Number Substitution

posted 2005-10-26

One of the random things I've learned about while working on WPF/Avalon are the different number systems used through out the world. I wrote a XAML example that shows some the support for number substitution. Here's a screenshot and the markup:

The number 4.39 in English, Portuguese, Arabic, Thai, and Panjabi writing systems

<Border xmlns="http://schemas.microsoft.com/winfx/avalon/2005" xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
        Padding="10" HorizontalAlignment="Center" VerticalAlignment="Center" BorderBrush="#ccc" BorderThickness="1">

  <Grid HorizontalAlignment="Center">
    <Grid.Resources>
      <!-- Common properties for all TextBlock elements -->
      <Style TargetType="{x:Type TextBlock}">
        <Setter Property="FontFamily" Value="Constantia" />
        <Setter Property="FontSize" Value="20" />
        <Setter Property="HorizontalAlignment" Value="Center" />
        <Setter Property="Margin" Value="5" />
      </Style>
      <!-- Properties for the number display, the databinding is done here -->
      <Style x:Key="NumberDisplay" TargetType="{x:Type TextBlock}" BasedOn="{StaticResource {x:Type TextBlock}}">
        <Setter Property="NumberSubstitution.Substitution" Value="Traditional" />
        <Setter Property="Text" Value="{Binding Path=Value, ElementName=NumberSlider}" />
        <Setter Property="Grid.Column" Value="1" />
        <Setter Property="MinWidth" Value="50" />
      </Style>
    </Grid.Resources>

    <Grid.RowDefinitions>
      <RowDefinition Height="Auto" />
      <RowDefinition Height="Auto" />
      <RowDefinition Height="Auto" />
      <RowDefinition Height="Auto" />
      <RowDefinition Height="Auto" />
      <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>

    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="Auto" />
      <ColumnDefinition Width="Auto" />
    </Grid.ColumnDefinitions>

    <!-- Title and Slider -->
    <StackPanel Grid.ColumnSpan="2">
      <TextBlock Grid.ColumnSpan="2" FontWeight="Bold" FontFamily="Candara" FontSize="30">Number Substitution</TextBlock>
      <Slider Grid.ColumnSpan="2" Minimum="0" Maximum="10" Name="NumberSlider" IsSnapToTickEnabled="True" TickFrequency=".01" Width="Auto" MaxWidth="300" Margin="10" />
    </StackPanel>

    <!-- Row Shading -->
    <Rectangle Grid.Row="1" Grid.ColumnSpan="2" Fill="#dec" />
    <Rectangle Grid.Row="2" Grid.ColumnSpan="2" Fill="#ffd" />
    <Rectangle Grid.Row="3" Grid.ColumnSpan="2" Fill="#dec" />
    <Rectangle Grid.Row="4" Grid.ColumnSpan="2" Fill="#ffd" />
    <Rectangle Grid.Row="5" Grid.ColumnSpan="2" Fill="#dec" />

    <!-- Labels -->
    <TextBlock Grid.Row="1">English (en-us)</TextBlock>
    <TextBlock Grid.Row="2">Portuguese (pt-pt)</TextBlock>
    <TextBlock Grid.Row="3">Arabic (ar-sa)</TextBlock>
    <TextBlock Grid.Row="4">Thai (th-th)</TextBlock>
    <TextBlock Grid.Row="5">Panjabi (pa-in)</TextBlock>

    <!-- Numbers -->
    <TextBlock Grid.Row="1" xml:lang="en-us" Style="{StaticResource NumberDisplay}" />
    <TextBlock Grid.Row="2" xml:lang="pt-pt" Style="{StaticResource NumberDisplay}" />
    <TextBlock Grid.Row="3" xml:lang="ar-sa" Style="{StaticResource NumberDisplay}" />
    <TextBlock Grid.Row="4" xml:lang="th-th" Style="{StaticResource NumberDisplay}" />
    <TextBlock Grid.Row="5" xml:lang="pa-in" Style="{StaticResource NumberDisplay}" />
  </Grid>
</Border>

You can copy and paste the code into XamlPad and manipulate the slider in order to see the changing values live (which is pretty neat in a very nerdy way).

The markup may look large, but that's mostly my formatting (because I like to make things look pretty). Here's a simplified excerpt to show the important part:

<TextBlock xml:lang="ar-sa" NumberSubstitution.Substitution="Traditional" />

The snippet above does two things. First it sets the culture of the content being displayed in the TextBlock, in this case Saudi Arabic (ar-sa). In order to activate the number substitution in this sample, we also have to set the NumberSubstitution.Substitution property to Traditional, which tells the system to look at the culture of the current context, instead of the system setting.

I'll close this entry with a second screenshot, showing a different number this time:

The number 7.01 in English, Portuguese, Arabic, Thai, and Panjabi writing systems

Update 10/28: Norris Cheng, a fellow Avalon team member, pointed out something I should clarify/correct in this entry (which I've paraphrased below):
The NumberSubstitution.CultureSource tells the system which CultureInfo it should look at: xml:lang (the default), the user's machine setting, or the NumberSubstitution.CultureOverride property. The reason we set NumberSubstitution.Substitution (which should really be called SubstitutionMethod is that even for cultures like ar-SA, latin digits are used by default -- you need to set NumberSubstitution.Substitution to Traditional to tell the system to use the traditional digits instead.