The market for apps is global. For example, the Windows Store is available in 230 international markets and more than 106 languages, but only approximately 7% of apps are localized to more than one language. Consider this:
There are more potential customers for your app who speak other languages than potential customers who speak your language.
By offering your apps in multiple languages and providing support for them, you increase the potential market for your apps. This is a great way to differentiate you from other competitive apps that are not world ready. The Windows 8.1 XAML platform provides some great infrastructure for building world-ready apps. In this post, we introduce you to some of the important factors in localized apps for XAML and how to handle them in your apps.
Non-localized apps should also consider international scenarios when handling user input. For example family members could be communicating using a social app in a different language from the UI of the app. There are some simple techniques you can add to your apps to improve the experience.
I have split this post into two parts:
- Part 1 covers the way that international text is handled, including the language property, fonts & bidirectional text support.
- Part 2 will cover resources and data conversion APIs, which enable your app to have localized and geographic specific strings, images, etc, and to convert dates and numbers into the correct format for the end user.
These posts are intended to pull together and summarize the information required to create great world- aware apps in XAML. Most of the advice and functionality applies to Windows 8 as well as 8.1, unless noted as specific to Windows 8.1 app functionality.
Language and region selection
It’s not uncommon for users to speak and work in multiple languages, and use more languages than Windows itself is localized into. Windows supports the ability for a user to specify their preferred language list, which can contain multiple entries. Adding a language adds support for the keyboard, IME, and localized resources if available.
Multilingual users can specify multiple languages in the Control Panel or new PC Settings App. For example:
The PC settings app showing language selection
Apps can also support multiple languages and regions, with different resources per language or region as appropriate. When the app is started, the system tries to match between the ordered user preferences, and the languages supported by the app, picking the best match between them. If there is no match, and the developer doesn’t add logic to override, the app uses the default language specified in the app manifest as the fallback language:
The Visual Studio manifest editor showing the default language
The ordered list of matching languages can be queried at runtime using:
Windows.Globalization.ApplicationLanguages.Languages;
The language match that’s used as much as possible (depending on how complete the localization of your app is for that language) is ApplicationLanguages.Languages[0]. This is also the language used for resources provided by Windows through globalization APIs, such as date/time formatters.
Even though the app may be presented in one language, the user may be inputting content in a different language, and can change keyboards/IME using Win + [spacebar]. For example somebody may be using Windows in English, but using a social app with family members in Japanese. The current input language/region can be queried using:
string currentInputLanguage = Windows.Globalization.Language.CurrentInputMethodLanguageTag;
In addition to the language, it’s important to consider the region for how dates, times, numbers and currencies are formatted. For example in the US the short form of date is normally month/day/year, whereas in the UK, its usually day/month/year. So even though you may not have localized an app to British English, you may want to consider the region when it comes to formatting dates, times, numbers and currency.
Font selection and font fallback
Default font selection
When displaying text using any of the XAML text controls, eg TextBlock, RichTextBlock, TextBox or RichTextBox, the font can be explicitly specified using the font family property. The default, unless a font is specified on the control or through styling, is “Segoe UI.” The generic.xaml file defines default styles for controls and templates. In the past, this was included in the Windows SDK as a discrete file; in Windows 8.1 Preview, it’s embedded into the XAML framework. The generic.xaml file contains a FontFamily resource named ContentControlThemeFontFamily. This is set to Segoe UI and is used as the font for named TextBlock styles as well as the default font for UI controls.
Font fallback
The fonts supplied with Windows collectively cover a very large proportion of characters defined in Unicode. Individually, though, any given font won’t support every character that might occur in content displayed in your app. If a run of text contains symbols that aren’t defined in the selected font, the XAML text system finds an appropriate font that supports these symbols, and uses that font for those symbols. This system is mostly automatic, however some East Asian languages share the same Unicode character codes for a symbol, but with subtly different renderings. For font fallback to know which font to use for the symbol it needs to know the language being used.
In XAML the UI language is a property that is inherited via the UI tree. Once set on an element, the property applies to all its children unless they explicitly set a language value. In markup the language can be set with the xml:lang attribute, for example in MainPage.xaml:
<Page
x:Class="MyTestApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/XAML/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/XAML"
xmlns:local="using:MyTestApp"
xml:lang="en-us">
…
</Page>
In code the language can be set using the Language property, for example the following code sets the language to be the application language from the matching mechanism described above. For example in app.xaml.cs:
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
…
if (rootFrame == null)
{
// Create a Frame to act as the navigation context and navigate to the first page
rootFrame = new Frame();
//Set the default language
rootFrame.Language = Windows.Globalization.ApplicationLanguages.Languages[0];
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
{
//TODO: Load state from previously suspended application
}
// Place the frame in the current Window
Window.Current.Content = rootFrame;
}
…
}
Recommendation: Set the language either via the API shown above, or in markup via localization using the x:Uid mechanism. I’ll cover more about x:Uid in Part 2.
Language font API
Windows supplies a set of APIs to determine the appropriate fonts for different display purposes in different languages. The LanguageFontGroup object has a set of properties exposing the appropriate fonts for different scenarios. For example the following table shows the font family and weights for a couple of languages.
Note: For Korean and other East Asian languages, it’s less common to use font weight to differentiate categories of text. This is because varying the width of strokes doesn’t work well with the calligraphy, particularly at smaller font sizes.
The LanguageFontGroup API can be used to query for appropriate fonts in XAML, for example the following code updates the ContentControlThemeFontFamily resource with the value from the UIText property of the application’s language. This is done in app.xaml so that it’s performed before the UI is created. Note that I am doing this in OnWindowCreated because in multi-window scenarios, each window is on a different thread, and the resources are specific to each thread.
using Windows.Globalization;
using Windows.Globalization.Fonts;
namespace MyTestApp
{
sealed partial class App : Application
{
public App()
{
this.InitializeComponent();
}
/// <summary>
/// Invoked when a new window is created, and resources are managed on a per window basis
/// </summary>
/// <param name="args"></param>
protected override void OnWindowCreated(WindowCreatedEventArgs args)
{
base.OnWindowCreated(args);
string langTag = ApplicationLanguages.Languages[0];
LanguageFontGroup lfg = new LanguageFontGroup(langTag);
FontFamily UIText = new FontFamily(lfg.UITextFont.FontFamily);
SetResource("ContentControlThemeFontFamily", UIText);
}
void SetResource(object key, object value)
{
if (this.Resources.ContainsKey(key))
this.Resources[key] = value;
else
this.Resources.Add(key, value);
}
}
}
Right-to-left and BiDi
Not all languages use a left-to-right (LTR) writing system like English and other European languages. Arabic and Hebrew use a right-to-left (RTL) direction, and UI should typically be mirrored to use RTL for layout. For example the maps app is presented as:
Left-to-right |
|
Right-to-left |
XAML enables you to set the direction on UI elements that derive from FrameworkElement using the FlowDirection property. The property inherits through the visual tree and applies to most child elements. Exceptions are Image and AppBarButton’s icon.
XAML doesn’t automatically set the FlowDirection based on the language. The app is required to do it either via code or through localization. To dynamically set the FlowDirection based on the app language, you can use the following code in MainPage.xaml or other pages:
using Windows.UI.Xaml.Controls;
using Windows.ApplicationModel.Resources.Core;
using Windows.UI.Xaml;
…
public MainPage()
{
// Set the flow direction based on the current language direction
if (ResourceManager.Current.DefaultContext.QualifierValues["LayoutDirection"] == "RTL")
{
this.FlowDirection = FlowDirection.RightToLeft;
}
this.InitializeComponent();
}
To use localization, you can apply an x:Uid on the Page element and set the direction through a resw resource file. I’ll explain more about x:Uid in Part 2.
BiDi and text
RTL also affects the layout and presentation of text. In RTL languages, the text reads from right-to-left. Within an individual paragraph, the text may contain parts that are LTR, and RTL. The Unicode algorithm specifies the rules for how this text should be displayed.
According to the Unicode specifications, each character is assigned directionality and strength, for example the Latin characters (A-Z) have strongly left direction, Arabic characters are strongly right directional, European numbers are weak, and punctuation and whitespace are neutral.
To display the text, it’s logically broken up into runs of contiguous blocks of the same directionality, and the rules are used to determine how to handle numbers and punctuation etc. Unless explicitly overridden with special marker characters, the text is displayed using its native direction. For example English words are always displayed left-to-right, even on an RTL system.
If the text is bidirectional (BiDi) and has runs of mixed LTR and RTL content, flow direction governs the relative layout of each run of text. In RTL the runs of text are laid out from the right-to-left. For example, consider the following text “Ahmad and Hiba are hiking. “ When the names are translated to Urdu, it becomes:
In memory and on disk, the string persists as:
Memory view of Visual Studio showing the string. Note the Urdu characters are presented in the opposite order to persistence.
For the XAML text controls, the contents of the individual runs of text are laid out according to their direction, and the FlowDirection is used to control the order of the runs. For example, the code and markup:
<Grid>
<StackPanel Orientation="Vertical" Margin="50" HorizontalAlignment="Left">
<TextBlock>LeftToRight:</TextBlock>
<Border Background="#FF3E64AA">
<TextBlock FlowDirection="LeftToRight" x:Name="tb1"/>
</Border>
<TextBlock Margin="0,10,0,0">RightToLeft:</TextBlock>
<Border Background="#FF3E64AA">
<TextBlock FlowDirection="RightToLeft" x:Name="tb2"/>
</Border>
</StackPanel>
<Grid.Resources>
<Style TargetType="TextBlock">
<Setter Property="FontSize" Value="20"/>
</Style>
</Grid.Resources>
</Grid>
public MainPage()
{
this.InitializeComponent();
string text = "احمد and حبا are hiking.";
tb1.Text = text;
tb2.Text = text;
}
Will be rendered as:
Diagram showing the rendering of the text in LTR and RTL modes.
Text display controls
The text display controls render the text using the FlowDirection to determine the reading order for paragraphs. For bilingual users, it’s common to come across content that is of a different direction from the language you have localized the app into. For example in a media player, the title of a track may be in Arabic, and you want to render that correctly. If it’s unidirectional, it will be fine, but if it contains mixed bidirectional text, it may not be what the user expects.
To solve this problem, the TextBlock and RichTextBlock controls have a new property TextReadingOrder, which can have its value set to DetectFromContent. When set, it iterates through the content, looking for the first strong character, and then uses that for the overall direction overriding the flow direction. This property doesn’t affect the horizontal alignment, which still comes from the FlowDirection and HorizontalAlignment properties.
For example:
<Grid>
<StackPanel Orientation="Vertical" Margin="50" HorizontalAlignment="Left">
<TextBlock>Default reading order:</TextBlock>
<Border Background="#FF3E64AA">
<TextBlock x:Name="tb1"/>
</Border>
<TextBlock Margin="0,10,0,0">Detect from content:</TextBlock>
<Border Background="#FF3E64AA">
<TextBlock x:Name="tb2" TextReadingOrder="DetectFromContent"/>
</Border>
</StackPanel>
<Grid.Resources>
<Style TargetType="TextBlock">
<Setter Property="FontSize" Value="20"/>
</Style>
</Grid.Resources>
</Grid>
The code above renders its results as:
This is because the first character with a strong direction is RTL.
Text input controls
If the list of languages the user has added in PC Settings/Control panel includes a Bidi language, after a log off/log on, Windows enables the user to control the writing direction by:
- Keyboard: pressing the Left Ctrl + Shift together for LTR and Right Ctrl + Shift together for RTL input mode.
- On screen keyboard: Pressing Ctrl then the direction control:
An Arabic speaker living in the US may use your app localized to English to communicate with her family, but in Arabic. Even though the app may be LTR, this provides a way for her to control the language of the input.
The RichEditBox control persists the direction for each paragraph as part of the RTF markup. You can query for the direction for a specific range using:
using Windows.UI.Text;
…
FormatEffect fe = RichEditBox1.Document.GetRange(0, 1).ParagraphFormat.RightToLeft;
bool isRTL = (fe == FormatEffect.On);
SideBar: A great way to experiment with the different bidi text options is to use the context menu in notepad to enable visualizing the Unicode control characters and to insert them as required.
Images
Unlike most other elements, Images do not inherit the flow direction from their parent, and so are not flipped. Depending on the purpose of the image, you might want it to flip. For example, if you’re using icons for logical operations like going backward and forward, you might want to flip the images for those scenarios. However, logos or photos that contain text probably shouldn’t be flipped.
You can flip an image by setting FlowDirection=RightToLeft directly on the image tag, via code, or via localization. Another alternative is to use the resource loader to select a different image for RTL scenarios. You do that using the layout direction qualifier such as by having two images: mylogo.jpg and mylogo.layoutdir-RTL.jpg, the latter will be used in RTL scenarios.
Textual glyphs in buttons
If you are creating buttons to match the Windows style, with a circle surrounding an icon, look for these issues:
- Buttons inherit the RTL styling, which can conflict with the spacing for the characters to get the correct overlap. If you use AppBarButton, it won’t use RTL for the glyph area solving this problem
- Icon glyphs are not automatically reflected, so if you’re using directional arrows, you probably want to remap the glyphs to choose the opposing direction as lists should flow from right-to-left in that layout direction.
Wrapping up
By using a couple of properties, you can make your apps function better for the global market place. In part 2 we’ll look at how you can localize resources and use the globalization APIs for formatting.
–Sam Spencer, Senior Program Manager, Windows