Historically, Windows has supported number substitution by allowing the representation of different cultural shapes for the same digits while keeping the internal storage of these digits unified among different locales, for example numbers are stored in their well known hexadecimal values, 0x40, 0x41, but displayed according to the selected language.
This has allowed applications to process numerical values without the need to convert them from one language to another, for example a user can open an Microsoft Excel spreadsheet in a localized Arabic Windows and see the numbers shaped in Arabic, but open it in a European version of Windows and see European representation of the same numbers. This is also necessary for other symbols such as comma separators and percentage symbol because they usually accompany numbers in the same document.
Windows Presentation Foundation (WPF) continues the same tradition, and adds further support for this feature that allows more user control over when and how substitution is used. While this feature is designed for any language, it is particularly useful in bidirectional content where shaping digits for a specific language is usually a challenge for application developers because of the various cultures an application might run on.
The core property controlling how number substitution works in Windows Presentation Foundation (WPF) is the Substitution dependency property. The NumberSubstitution class specifies how numbers in text are to be displayed. It has three public properties that define its behavior. Following is a summary of each of the properties.
CultureSource:
This property specifies how the culture for numbers is determined. It takes one of three NumberCultureSource enumeration values.
CultureOverride:
The CultureOverride property is used only if the CultureSource property is set to Override and is ignored otherwise. It specifies the number culture. A value of null, the default value, is interpreted as en-US.
Substitution:
This property specifies the type of number substitution to perform. It takes one of the following NumberSubstitutionMethod enumeration values.
AsCulture: The substitution method is determined based on the number culture's NumberFormatInfo.DigitSubstitution property. This is the default.
Context: If the number culture is an Arabic or Farsi culture, it specifies that the digits depend on the context.
European: Numbers are always rendered as European digits.
NativeNational: Numbers are rendered using the national digits for the number culture, as specified by the culture's NumberFormat.
Traditional: Numbers are rendered using the traditional digits for the number culture. For most cultures, this is the same as NativeNational. However, NativeNational results in Latin digits for some Arabic cultures, whereas this value results in Arabic digits for all Arabic cultures.
What do those values mean for a bidirectional content developer? In most cases, the developer might need only to define FlowDirection and the language of each textual UI element, for example Language="ar-SA" and the NumberSubstitution logic takes care of displaying the numbers according to the correct UI. The following example demonstrates using Arabic and English numbers in a Windows Presentation Foundation (WPF) application running in an Arabic version of Windows.
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<StackPanel>
<TextBlock Background="LightGreen" FontSize="32"
Language="ar-SA" FlowDirection="RightToLeft">1+2=3</TextBlock>
<TextBox Background="LightGreen" FontSize="32"
Language="ar-SA" FlowDirection="RightToLeft">1+2=3</TextBox>
<TextBlock Background="LightBlue" FontSize="32">1+2=3</TextBlock>
<TextBox Background="LightBlue" FontSize="32">1+2=3</TextBox>
</StackPanel>
</Page>
The following graphic shows the output of the previous sample if you are running in an Arabic version of Windows.
Graphic That Shows Arabic and English Numbers Displayed
The FlowDirection was important in this case because setting the FlowDirection to LeftToRight instead would have yielded European digits. The following sections discuss how to have a unified display of digits throughout your document. If this example is not running on Arabic Windows, all the digits display as European digits.
Defining Substitution Rules
In a real application you might need to set the Language programmatically. For example, you want to set the xml:lang attribute to be the same as the one used by the system’s UI, or maybe change the language depending on the application state.
If you want to make changes based on the application's state, make use of other features provided by Windows Presentation Foundation (WPF).
First, set the application component’s NumberSubstitution.CultureSource="Text". Using this setting makes sure that the settings do not come from the UI for text elements that have "User" as the default, such as TextBlock.
For example:
<TextBlock Name="text1" NumberSubstitution.CultureSource="Text"> 1234+5679=6913 </TextBlock> |
In the corresponding C# code, set the Language property for example, to "ar-SA".
text1.Language = System.Windows.Markup.XmlLanguage.GetLanguage("ar-SA"); |
If you need to set the Language property to the current user’s UI language use the following code.
text1.Language = System.Windows.Markup.XmlLanguage.GetLanguage( System.Globalization.CultureInfo.CurrentUICulture.IetfLanguageTag); |
CurrentCulture represents the current culture used by the current thread at run time.
Your final XAML example should be similar to the following example.
<Page x:Class="WindowsApplication.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Code Sample" Height="300" Width="300"
>
<StackPanel>
<TextBlock Language="ar-SA"
FlowDirection="RightToLeft">عربى: 1+2=3
</TextBlock>
<TextBlock Language="ar-SA"
FlowDirection="RightToLeft"
NumberSubstitution.Substitution="European">عربى: 1+2=3
</TextBlock>
</StackPanel>
</Page>
Your final C# example should be similar to the following.
namespace BidiTest
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
string currentLanguage =
System.Globalization.CultureInfo.CurrentCulture.IetfLanguageTag;
text1.Language = System.Windows.Markup.XmlLanguage.GetLanguage(currentLanguage);
if (currentLanguage.ToLower().StartsWith("ar"))
{
text1.FlowDirection = FlowDirection.RightToLeft;
}
else
{
text1.FlowDirection = FlowDirection.LeftToRight;
}
}
}
}
The following graphic shows what the window looks like for either programming language.
Graphic That Displays Arabic Numbers
Using the Substitution Property
The way number substitution works in Windows Presentation Foundation (WPF) depends on both the Language of the text element and itsFlowDirection. If the FlowDirection is left to right, then European digits are rendered. However if it is preceded by Arabic text, or has the language set to "ar" and the FlowDirection is RightToLeft, Arabic digits are rendered instead.
In some cases, however, you might want to create a unified application, for example European digits for all users. Or Arabic digits in Table cells with a specific Style. One easy way to do that is using the Substitution property.
In the following example, the first TextBlock does not have the Substitution property set, so the algorithm displays Arabic digits as expected. However in the second TextBlock, the substitution is set to European overriding the default substitution for Arabic numbers, and European digits are displayed.
<Page x:Class="WindowsApplication.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Code Sample" Height="300" Width="300"
>
<StackPanel>
<TextBlock Language="ar-SA"
FlowDirection="RightToLeft">عربى: 1+2=3
</TextBlock>
<TextBlock Language="ar-SA"
FlowDirection="RightToLeft"
NumberSubstitution.Substitution="European">عربى: 1+2=3
</TextBlock>
</StackPanel>
</Page>