Globalization and Localization 프로그래밍/WPF2016. 11. 5. 14:17
Globalization and Localization
WPF Globalization and Localization Overview
When you limit your product's availability to only one language, you limit your potential customer base to a fraction of our world’s 6.5 billion population. If you want your applications to reach a global audience, cost-effective localization of your product is one of the best and most economical ways to reach more customers.
This overview introduces globalization and localization in Windows Presentation Foundation (WPF). Globalization is the design and development of applications that perform in multiple locations. For example, globalization supports localized user interfaces and regional data for users in different cultures. WPF provides globalized design features, including automatic layout, satellite assemblies, and localized attributes and commenting .
Localization is the translation of application resources into localized versions for the specific cultures that the application supports. When you localize in WPF, you use the APIs in the System.Windows.Markup.Localizer namespace. These APIs power the LocBaml Tool Sample command-line tool. For information about how to build and use LocBaml, see How to: Localize an Application.
Best Practices for Globalization and Localization in WPF
You can make the most of the globalization and localization functionality that is built into WPF by following the UI design and localization-related tips that this section provides.
Best Practices for WPF UI Design
When you design a WPF–based UI, consider implementing these best practices:
Write your UI in XAML; avoid creating UI in code. When you create your UI by using XAML, you expose it through built-in localization APIs.
Avoid using absolute positions and fixed sizes to lay out content; instead, use relative or automatic sizing .
Use SizeToContent; and keep widths and heights set to Auto.
Avoid using Canvas to lay out UIs.
Use Grid and its size-sharing feature.
Provide extra space in margins because localized text often requires more space. Extra space allows for possible overhanging characters.
Enable TextWrapping on TextBlock to avoid clipping .
Set the xml:langattribute . This attribute describes the culture of a specific element and its child elements. The value of this property changes the behavior of several features in WPF. For example, it changes the behavior of hyphenation, spell checking, number substitution, complex script shaping, and font fallback. See Globalization for WPF for more information about setting the xml:lang Handling in XAML.
Create a customized composite font to obtain better control of fonts that are used for different languages . By default, WPF uses the GlobalUserInterface.composite font in your Windows\Fonts directory.
When you create navigation applications that may be localized in a culture that presents text in a right-to-left format, explicitly set theFlowDirection of every page to ensure the page does not inherit FlowDirection from the NavigationWindow.
When you create stand-alone navigation applications that are hosted outside a browser, set the StartupUri for your initial application to aNavigationWindow instead of to a page (for example, <Application StartupUri="NavigationWindow.xaml">). This design enables you to change the FlowDirection of the Window and the navigation bar. For more information and an example, see Globalization Homepage Sample.
Best Practices for WPF Localization
When you localize WPF–based applications, consider implementing these best practices:
Use local ization comments to provide extra context for localizers.
Use localization attributes to control localization instead of selectively omitting Uid properties on elements. See Localization Attributes and Comments for more information.
Use msbuild /t:updateuid and /t:checkuid to add and check Uid properties in your XAML. Use Uid properties to track changes between development and localization. Uid properties help you localize new development changes. If you manually add Uid properties to a UI, the task is typically tedious and less accurate.
Do not edit or change Uid properties after you begin localization.
Do not use duplicate Uid properties (remember this tip when you use the copy-and-paste command).
Set the UltimateResourceFallback location in AssemblyInfo.* to specify the appropriate language for fallback (for example,[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]).
If you decide to include your source language in the main assembly by omitting the <UICulture> tag in your project file, set theUltimateResourceFallback location as the main assembly instead of the satellite (for example, [assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.MainAssembly)]).
Localize a WPF Application
When you localize a WPF application, you have several options. For example, you can bind the localizable resources in your application to an XML file, store localizable text in resx tables, or have your localizer use Extensible Application Markup Language (XAML) files . This section describes a localization workflow that uses the BAML form of XAML , which provides several benefits:
You can localize after you build .
You can update to a newer version of the BAML form of XAMLwith localizations from an older version of the BAML form of XAML so that you can localize at the same time that you develop.
You can validate original source elements and semantics at compile time because the BAML form of XAML is the compiled form of XAML.
Localization Build Process
When you develop a WPF application, the build process for localization is as follows:
The developer creates and globalizes the WPF application. In the project file the developer sets <UICulture>en-US</UICulture> so that when the application is compiled, a language-neutral main assembly is generated. This assembly has a satellite .resources.dll file that contains all the localizable resources. Optionally, you can keep the source language in the main assembly because our localization APIs support extraction from the main assembly.
When the file is compiled into the build, the XAML is converted to the BAML form of XAML. The culturally neutral MyDialog.exe and the culturally dependent (English) MyDialog.resources.dll files are released to the English-speaking customer.
Localization Workflow
The localization process begins after the unlocalized MyDialog.resources.dll file is built. The UI elements and properties in your original XAML are extracted from the the BAML form of XAML into key-value pairs by using the APIs under System.Windows.Markup.Localizer. Localizers use the key-value pairs to localize the application. You can generate a new .resource.dll from the new values after localization is complete.
The keys of the key-value pairs are x:Uid values that are placed by the developer in the original XAML. These x:Uid values enable the API to track and merge changes that happen between the developer and the localizer during localization. For example, if the developer changes the UI after the localizer begins localizing, you can merge the development change with the already completed localization work so that minimal translation work is lost.
The following graphic shows a typical localization workflow that is based on the BAML form of XAML. This diagram assumes the developer writes the application in English . The developer creates and globalizes the WPF application. In the project file the developer sets <UICulture>en-US</UICulture> so that on build, a language neutral main assembly gets generated with a satellite .resources.dll containing all localizable resources. Alternately, one could keep the source language in the main assembly because WPF localization APIs support extraction from the main assembly. After the build process, the XAML get compiled into BAML. The culturally neutral MyDialog.exe.resources.dll get shipped to the English speaking customer.
Examples of WPF Localization
This section contains examples of localized applications to help you understand how to build and localize WPF applications.
Run Dialog Box Example
The following graphics show the output of the Run dialog box sample.
English:
German:
Designing a Global Run Dialog Box
This example produces a Run dialog box by using WPF and XAML. This dialog box is equivalent to the Run dialog box that is available from the Microsoft Windows Start menu.
Some highlights for making global dialog boxes are:
Automatic Layout
In Window1.xaml:
<Window SizeToContent="WidthAndHeight">
The previous Window property automatically resizes the window according to the size of the content. This property prevents the window from cutting off content that increases in size after localization; it also removes unneeded space when content decreases in size after localization.
<Grid x:Uid="Grid_1">
Uid properties are needed in order for WPF localization APIs to work correctly.
They are used by WPF localization APIs to track changes between the development and localization of the user interface (UI). Uid properties enable you to merge a newer version of the UI with an older localization of the UI. You add a Uid property by running msbuild /t:updateuid RunDialog.csproj in a command shell. This is the recommended method of adding Uid properties because manually adding them is typically time-consuming and less accurate. You can check that Uid properties are correctly set by running msbuild /t:checkuid RunDialog.csproj.
The UI is structured by using the Grid control, which is a useful control for taking advantage of the automatic layout in WPF. Note that the dialog box is split into three rows and five columns. Not one of the row and column definitions has a fixed size; hence, the UI elements that are positioned in each cell can adapt to increases and decreases in size during localization.
<Grid.ColumnDefinitions> <ColumnDefinition x:Uid="ColumnDefinition_1" /> <ColumnDefinition x:Uid="ColumnDefinition_2" />
The first two columns where the Open: label and ComboBox are placed use 10 percent of the UI total width.
<ColumnDefinition x:Uid="ColumnDefinition_3" SharedSizeGroup="Buttons" /> <ColumnDefinition x:Uid="ColumnDefinition_4" SharedSizeGroup="Buttons" /> <ColumnDefinition x:Uid="ColumnDefinition_5" SharedSizeGroup="Buttons" /> </Grid.ColumnDefinitions>
Note that of the example uses the shared-sizing feature of Grid. The last three columns take advantage of this by placing themselves in the sameSharedSizeGroup. As one would expect from the name of the property, this allows the columns to share the same size. So when the "Browse…" gets localized to the longer string "Durchsuchen…", all buttons grow in width instead of having a small "OK" button and a disproportionately large "Durchsuchen…" button.
Xml:lang
Xml:lang="en-US"
Notice the xml:lang Handling in XAML placed at the root element of the UI. This property describes the culture of a given element and its children. This value is used by several features in WPF and should be changed appropriately during localization. This value changes what language dictionary is use to hyphenate and spell check words. It also affects the display of digits and how the font fallback system selects which font to use. Finally, the property affects the way numbers are displayed and the way texts written in complex scripts are shaped. The default value is "en-US".
Building a Satellite Resource Assembly
In .csproj:
<UICulture>en-US</UICulture>
Notice the addition of a UICulture value. When this is set to a valid CultureInfo value such as en-US, building the project will generate a satellite assembly with all localizable resources in it.
<Resource Include="RunIcon.JPG">
<Localizable>False</Localizable>
</Resource>
The RunIcon.JPG does not need to be localized because it should appear the same for all cultures. Localizable is set to false so that it remains in the language neutral main assembly instead of the satellite assembly. The default value of all noncompilable resources is Localizable set to true.
Localizing the Run Dialog
Parse
After building the application, the first step in localizing it is parsing the localizable resources out of the satellite assembly. For the purposes of this topic, use the sample LocBaml tool which can be found at LocBaml Tool Sample. Note that LocBaml is only a sample tool meant to help you get started in building a localization tool that fits into your localization process. Using LocBaml, run the following to parse: LocBaml /parse RunDialog.resources.dll /out: to generate a "RunDialog.resources.dll.CSV" file.
Localize
Use your favorite CSV editor that supports Unicode to edit this file. Filter out all entries with a localization category of "None". You should see the following entries:
Resource Key | Localization Category | Value |
Button_1:System.Windows.Controls.Button.$Content | Button | OK |
Button_2:System.Windows.Controls.Button.$Content | Button | Cancel |
Button_3:System.Windows.Controls.Button.$Content | Button | Browse... |
ComboBox_1:System.Windows.Controls.ComboBox.$Content | ComboBox | |
TextBlock_1:System.Windows.Controls.TextBlock.$Content | Text | Type the name of a program, folder, document, or Internet resource, and Windows will open it for you. |
TextBlock_2:System.Windows.Controls.TextBlock.$Content | Text | Open: |
Window_1:System.Windows.Window.Title | Title | Run |
Localizing the application to German would require the following translations:
Resource Key | Localization Category | Value |
Button_1:System.Windows.Controls.Button.$Content | Button | OK |
Button_2:System.Windows.Controls.Button.$Content | Button | Abbrechen |
Button_3:System.Windows.Controls.Button.$Content | Button | Durchsuchen… |
ComboBox_1:System.Windows.Controls.ComboBox.$Content | ComboBox | |
TextBlock_1:System.Windows.Controls.TextBlock.$Content | Text | Geben Sie den Namen eines Programms, Ordners, Dokuments oder einer Internetresource an. |
TextBlock_2:System.Windows.Controls.TextBlock.$Content | Text | Öffnen: |
Window_1:System.Windows.Window.Title | Title | Run |
Generate
The last step of localization involves creating the newly localized satellite assembly. This can be accomplished with the following LocBaml command:
LocBaml.exe /generate RunDialog.resources.dll /trans:RunDialog.resources.dll.CSV /out: . /cul:de-DE
On German Windows, if this resources.dll is placed in a de-DE folder next to the main assembly, this resource will automatically load instead of the one in the en-US folder. If you do not have a German version of Windows to test this, set the culture to whatever culture of Windows you are using (i.e. en-US), and replace the original resources.dll.
Satellite Resource Loading
MyDialog.exe | en-US\MyDialog.resources.dll | de-DE\MyDialog.resources.dll |
---|---|---|
Code | Original English BAML | Localized BAML |
Culturally neutral resources | Other resources in English | Other resources localized to German |
The .NET framework automatically chooses which satellite resources assembly to load based on the application’sThread.CurrentThread.CurrentUICulture. This defaults to the culture of your Windows OS. So if you are using German Windows, the de-DE\MyDialog.resources.dll loads, if you are using English Windows, the en-US\MyDialog.resources.dll loads. You can set the ultimate fallback resource for your application by specifying the NeutralResourcesLanguage in your project’s AssemblyInfo.*. For example if you specify:
[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
then the en-US\MyDialog.resources.dll will be used with German Windows if a de-DE\MyDialog.resources.dll or de\MyDialog.resources.dll are both unavailable.
Microsoft Saudi Arabia Homepage
The following graphics show an English and Arabic Homepage. For the complete sample that produces these graphics see Globalization Homepage Sample.
English:
Arabic:
Designing a Global Microsoft Homepage
This mock up of the Microsoft Saudi Arabia web site illustrates the globalization features provided for RightToLeft languages. Languages such as Hebrew and Arabic have a right-to-left reading order so the layout of UI must often be laid out quite differently than it would be in left-to-right languages such as English. Localizing from a left-to-right language to a right-to-left language or vice versa can be quite challenging. WPF has been designed to make such localizations much easier.
FlowDirection
Homepage.xaml:
<Page x:Uid="Page_1" x:Class="MicrosoftSaudiArabiaHomepage.Homepage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" FlowDirection="LeftToRight" Localization.Comments="FlowDirection(This FlowDirection controls the actual content of the homepage)" xml:lang="en-US">
Notice the FlowDirection property on Page. Changing this property to RightToLeft will change the FlowDirection of the Page and its children elements so that the layout of this UI is flipped to become right-to-left as an Arabic user would expect. One can override the inheritance behavior by specifying an explicit FlowDirection on any element. The FlowDirection property is available on any FrameworkElement or document related element and has an implicit value of LeftToRight.
Observe that even the background gradient brushes are flipped correctly when the root FlowDirection is changed:
FlowDirection="LeftToRight"
FlowDirection="RightToLeft"
Avoid Using Fixed Dimensions for Panels and Controls
Take a look through Homepage.xaml, notice that aside from the fixed width and height specified for the entire UI on the top DockPanel, there are no other fixed dimensions. Avoid using fixed dimensions to prevent clipping localized text that may be longer than the source text. WPF panels and controls will automatically resize based on the content that they contain. Most controls also have minimum and maximum dimensions that you can set for more control (i.e. MinWidth= "20"). With Grid, you can also set relative widths and heights by using ‘*’ (i.e. Width= "0.25*") or use its cell size sharing feature.
Localization Comments
There are many cases where content may be ambiguous and difficult to translate. The developer or designer has the ability to provide extra context and comments to localizers through localization comments. For example the Localization.Comments below clarifies the usage of the character ‘|’.
<TextBlock x:Uid="TextBlock_2" DockPanel.Dock="Right" Foreground="White" Margin="5,0,5,0" Localization.Comments="$Content(This character is used as a decorative rule.)"> | </TextBlock>
This comment becomes associated with TextBlock_1’s content and in the case of the LocBaml Tool, ( see How to: Localize an Application), it can be seen in the 6th column of the TextBlock_1 row in the output .csv file:
Resource Key | Category | Readable | Modifiable | Comment | Value |
TextBlock_1:System.Windows.Controls.TextBlock.$Content | Text | TRUE | TRUE | This character is used as a decorative rule. | | |
Comments can be placed on the content or property of any element using the following syntax:
<TextBlock x:Uid="TextBlock_1" DockPanel.Dock="Right" Foreground="White" Margin="5,0,5,0" Localization.Comments="$Content(This is a comment on the TextBlock's content.) Margin(This is a comment on the TextBlock's Margin property.)"> | </TextBlock>
Localization Attributes
Often the developer or localization manager needs control of what localizers can read and modify. For example, you might not want the localizer to translate the name of your company or legal wording. WPF provides attributes that enable you to set the readability, modifiability, and category of an element’s content or property which your localization tool can use to lock, hide, or sort elements. For more information, see Attributes. For the purposes of this sample, the LocBaml Tool just outputs the values of these attributes. WPF controls all have default values for these attributes, but you the can override them. For example, the following example overrides the default localization attributes for TextBlock_1 and sets the content to be readable but unmodifiable for localizers.
<TextBlock x:Uid="TextBlock_1" Localization.Attributes= "$Content(Readable Unmodifiable)"> Microsoft Corporation </TextBlock>
In addition to the readability and modifiability attributes, WPF provides an enumeration of common UI categories ( LocalizationCategory) that can be used to give localizers more context. The WPF default categories for platform controls can be overridden in XAML as well:
<TextBlock x:Uid="TextBlock_2"> <TextBlock.ToolTip> <TextBlock x:Uid="TextBlock_3" Localization.Attributes= "$Content(ToolTip Readable Unmodifiable)"> Microsoft Corporation </TextBlock> </TextBlock.ToolTip> Windows Vista </TextBlock>
The default localization attributes that WPF provides can also be overridden through code, so you can correctly set the right default values for custom controls. For example:
[Localizability(Readability = Readability.Readable, Modifiability=Modifiability.Unmodifiable, LocalizationCategory.None)]
public class CorporateLogo: TextBlock
{
…
..
.
}
The per instance attributes set in XAML will take precedence over the values set in code on custom controls. For more information on attributes and comments, see Localization Attributes and Comments.
Font Fallback and Composite Fonts
If you specify a font that does not support a given codepoint range, WPF will automatically fallback to one that does by using the Global User Interface.compositefont that is located in your Windows\Fonts directory. Composite fonts work just as any other font and can be used explicitly by setting an element’s FontFamily (i.e. FontFamily= "Global User Interface"). You can specify your own font fallback preference by creating your own composite font and specifying what font to use for specific codepoint ranges and languages.
For more information on composite fonts see FontFamily.
Localizing the Microsoft Homepage
You can follow the same steps as the Run Dialog example to localize this application. The localized .csv file for Arabic is available for you in theGlobalization Homepage Sample.
Globalization for WPF
This topic introduces issues that you should be aware of when writing Windows Presentation Foundation (WPF) applications for the global market. The globalization programming elements are defined in Microsoft .NET in System.Globalization.
XAML Globalization
Extensible Application Markup Language (XAML) is based on XML and takes advantage of the globalization support defined in the XML specification. The following sections describe some XAML features that you should be aware of.
Character References
A character reference gives the number of the particular Unicode character it represents, in either decimal or hexadecimal. The following example shows a decimal character reference.
Ϩ
This example shows a hexadecimal character reference. Notice that it has an x in front of the hexadecimal number.
Ϩ
Encoding
The encoding supported by XAML are ASCII, Unicode UTF-16, and UTF-8. The encoding statement is at the beginning of XAML document. If no encoding attribute exists and there is no byte-order, the parser defaults to UTF-8. UTF-8 and UTF-16 are the preferred encodings. UTF-7 is not supported. The following example demonstrates how to specify a UTF-8 encoding in a XAML file.
?xml encoding="UTF-8"?
Language Attribute
XAML uses xml:lang to represent the language attribute of an element. To take advantage of the CultureInfo class, the language attribute value needs to be one of the culture names predefined by CultureInfo. xml:lang is inheritable in the element tree (by XML rules, not necessarily because of dependency property inheritance) and its default value is an empty string if it is not assigned explicitly.
The language attribute is very useful for specifying dialects. For example, French has different spelling, vocabulary, and pronunciation in France, Quebec, Belgium, and Switzerland. Also Chinese, Japanese, and Korean share code points in Unicode, but the ideographic shapes are different and they use totally different fonts.
The following Extensible Application Markup Language (XAML) example uses the fr-CA language attribute to specify Canadian French.
<TextBlock xml:lang="fr-CA">Découvrir la France</TextBlock>
Unicode
XAML supports all Unicode features including surrogates. As long as the character set can be mapped to Unicode, it is supported. For example, GB18030 introduces some characters that are mapped to the Chinese, Japanese, and Korean (CFK) extension A and B and surrogate pairs, therefore it is fully supported. A WPF application can use StringInfo to manipulate strings without understanding whether they have surrogate pairs or combining characters.
Designing an International User Interface with XAML
This section describes user interface (UI) features that you should consider when writing an application.
International Text
WPF includes built-in processing for all Microsoft .NET Framework supported writing systems.
The following scripts are currently supported:
Arabic
Bengali
Devanagari
Cyrillic
Greek
Gujarati
Gurmukhi
Hebrew
Ideographic scripts
Kannada
Lao
Latin
Malayalam
Mongolian
Odia
Syriac
Tamil
Telugu
Thaana
Thai*
Tibetan
*In this release the display and editing of Thai text is supported; word breaking is not.
The following scripts are not currently supported:
Khmer
Korean Old Hangul
Myanmar
Sinhala
All the writing system engines support OpenType fonts. OpenType fonts can include the OpenType layout tables that enable font creators to design better international and high-end typographic fonts. The OpenType font layout tables contain information about glyph substitutions, glyph positioning, justification, and baseline positioning, enabling text-processing applications to improve text layout.
OpenType fonts allow the handling of large glyph sets using Unicode encoding. Such encoding enables broad international support as well as for typographic glyph variants.
WPF text rendering is powered by Microsoft ClearType sub-pixel technology that supports resolution independence. This significantly improves legibility and provides the ability to support high quality magazine style documents for all scripts.
International Layout
WPF provides a very convenient way to support horizontal, bidirectional, and vertical layouts. In presentation framework the FlowDirectionproperty can be used to define layout. The flow direction patterns are:
LeftToRight - horizontal layout for Latin, East Asian and so forth.
RightToLeft - bidirectional for Arabic, Hebrew and so forth.
Developing Localizable Applications
When you write an application for global consumption you should keep in mind that the application must be localizable. The following topics point out things to consider.
Multilingual User Interface
Multilingual User Interfaces (MUI) is a Microsoft support for switching UIs from one language to another. A WPF application uses the assembly model to support MUI. One application contains language-neutral assemblies as well as language-dependent satellite resource assemblies. The entry point is a managed .EXE in the main assembly. WPF resource loader takes advantage of the Framework's resource manager to support resource lookup and fallback. Multiple language satellite assemblies work with the same main assembly. The resource assembly that is loaded depends on the CurrentUICulture of the current thread.
Localizable User Interface
WPF applications use XAML to define their UI. XAML allows developers to specify a hierarchy of objects with a set of properties and logic. The primary use of XAML is to develop WPF applications but it can be used to specify a hierarchy of any common language runtime (CLR) objects. Most developers use XAML to specify their application's UI and use a programming language such as C# to react to user interaction.
From a resource point of view, a XAML file designed to describe a language-dependent UI is a resource element and therefore its final distribution format must be localizable to support international languages. Because XAML cannot handle events many XAML applications contain blocks of code to do this. For more information, see XAML Overview (WPF). Code is stripped out and compiled into different binaries when a XAML file is tokenized into the BAML form of XAML. The BAML form of XAML files, images, and other types of managed resource objects are embedded in the satellite resource assembly, which can be localized into other languages, or the main assembly when localization is not required.
Note |
---|
WPF applications support all the FrameworkCLR resources including string tables, images, and so forth. |
Building Localizable Applications
Localization means to adapt a UI to different cultures. To make a WPF application localizable, developers need to build all the localizable resources into a resource assembly. The resource assembly is localized into different languages, and the code-behind uses resource management API to load. One of the files required for a WPF application is a project file (.proj). All resources that you use in your application should be included in the project file. The following example from a .csproj file shows how to do this.
<Resource Include="data\picture1.jpg"/> <EmbeddedResource Include="data\stringtable.en-US.restext"/>
To use a resource in your application instantiate a ResourceManager and load the resource you want to use. The following example demonstrates how to do this.
void OnClick(object sender, RoutedEventArgs e) { ResourceManager rm = new ResourceManager ("MySampleApp.data.stringtable", Assembly.GetExecutingAssembly()); Text1.Text = rm.GetString("Message"); }
Using ClickOnce with Localized Applications
ClickOnce is a new Windows Forms deployment technology that will ship with Microsoft Visual Studio 2005. It enables application installation and upgrading of Web applications. When an application that was deployed with ClickOnce is localized it can only be viewed on the localized culture. For example, if a deployed application is localized to Japanese it can only be viewed on Japanese Microsoft Windows not on English Windows. This presents a problem because it is a common scenario for Japanese users to run an English version of Windows.
The solution to this problem is setting the neutral language fallback attribute. An application developer can optionally remove resources from the main assembly and specify that the resources can be found in a satellite assembly corresponding to a specific culture. To control this process use theNeutralResourcesLanguageAttribute. The constructor of the NeutralResourcesLanguageAttribute class has two signatures, one that takes anUltimateResourceFallbackLocation parameter to specify the location where the ResourceManager should extract the fallback resources: main assembly or satellite assembly. The following example shows how to use the attribute. For the ultimate fallback location, the code causes theResourceManager to look for the resources in the "de" subdirectory of the directory of the currently executing assembly.
[assembly: NeutralResourcesLanguageAttribute( "de" , UltimateResourceFallbackLocation.Satellite)]
Use Automatic Layout Overview
This topic introduces guidelines for developers on how to write Windows Presentation Foundation (WPF) applications with localizable user interfaces (UIs). In the past, localization of a UI was a time consuming process. Each language that the UI was adapted for required a pixel by pixel adjustment. Today with the right design and right coding standards, UIs can be constructed so that localizers have less resizing and repositioning to do. The approach to writing applications that can be more easily resized and repositioned is called automatic layout, and can be achieved by using WPF application design.
This topic contains the following sections.
Advantages of Using Automatic Layout
Because the WPF presentation system is powerful and flexible, it provides the ability to layout elements in an application that can be adjusted to fit the requirements of different languages. The following list points out some of the advantages of automatic layout.
UI displays well in any language.
Reduces the need to readjust position and size of controls after text is translated.
Reduces the need to readjust window size.
UI layout renders properly in any language.
Localization can be reduced to the point that it is little more than string translation.
Automatic Layout and Controls
Automatic layout enables an application to adjust the size of a control automatically. For example, a control can change to accommodate the length of a string. This capability enables localizers to translate the string; they no longer need to resize the control to fit the translated text. The following example creates a button with English content.
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="ButtonLoc.Pane1" Name="myWindow" SizeToContent="WidthAndHeight" > <DockPanel> <Button FontSize="28" Height="50">My name is Hope.</Button> </DockPanel> </Window>
In the example, all you have to do to make a Spanish button is change the text. For example,
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="ButtonLoc.Pane1" Name="myWindow" SizeToContent="WidthAndHeight" > <DockPanel> <Button FontSize="28" Height="50">Me llamo Esperanza.</Button> </DockPanel> </Window>
The following graphic shows the output of the code samples.
Automatic Layout and Coding Standards
Using the automatic layout approach requires a set of coding and design standards and rules to produce a fully localizable UI. The following guidelines will aid your automatic layout coding.
Coding Standards | Description |
---|---|
Do not use absolute positions. |
|
Do not set a fixed size for a window. |
<StackPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="GridLoc.Pane1" > |
Add a FlowDirection. |
|
Use composite fonts instead of physical fonts. |
|
Add xml:lang. |
|
Automatic Layout and Grids
The Grid element, is useful for automatic layout because it enables a developer to position elements. A Grid control is capable of distributing the available space among its child elements, using a column and row arrangement. The UI elements can span multiple cells, and it is possible to have grids within grids. Grids are useful because they enable you to create and position complex UI. The following example demonstrates using a grid to position some buttons and text. Notice that the height and width of the cells are set to Auto; therefore, the cell that contains the button with an image adjusts to fit the image.
<Grid Name="grid" ShowGridLines ="false"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <TextBlock Margin="10, 10, 5, 5" Grid.Column="0" Grid.Row="0" FontSize="24">Grid </TextBlock> <TextBlock Margin="10, 10, 5, 5" Grid.Column="0" Grid.Row="1" FontSize="12" Grid.ColumnSpan="2">The following buttons and text are positioned using a Grid. </TextBlock> <Button Margin="10, 10, 5, 5" Grid.Column="0" Grid.Row="2" Background="Pink" BorderBrush="Black" BorderThickness="10">Button 1 </Button> <TextBlock Margin="10, 10, 5, 5" Grid.Column="1" Grid.Row="2" FontSize="12" VerticalAlignment="Center" TextWrapping="WrapWithOverflow">Sets the background color. </TextBlock> <Button Margin="10, 10, 5, 5" Grid.Column="0" Grid.Row="3" Foreground="Red"> Button 2 </Button> <TextBlock Margin="10, 10, 5, 5" Grid.Column="1" Grid.Row="3" FontSize="12" VerticalAlignment="Center" TextWrapping="WrapWithOverflow">Sets the foreground color. </TextBlock> <Button Margin="10, 10, 5, 5" Grid.Column="0" Grid.Row="4"> <Image Source="data\flower.jpg"></Image> </Button> <TextBlock Margin="10, 10, 5, 5" Grid.Column="1" Grid.Row="4" FontSize="12" VerticalAlignment="Center" TextWrapping="WrapWithOverflow">Adds an image as the button's content. </TextBlock> </Grid>
The following graphic shows the grid produced by the previous code.
Automatic Layout and Grids Using the IsSharedSizeScope Property
A Grid element is useful in localizable applications to create controls that adjust to fit content. However, at times you want controls to maintain a particular size regardless of content. For example, if you have "OK", "Cancel" and "Browse" buttons you probably do not want the buttons sized to fit the content. In this case the Grid.IsSharedSizeScope attached property is useful for sharing the same sizing among multiple grid elements. The following example demonstrates how to share column and row sizing data between multiple Grid elements.
<StackPanel Orientation="Horizontal" DockPanel.Dock="Top"> <Button Click="setTrue" Margin="0,0,10,10">Set IsSharedSizeScope="True"</Button> <Button Click="setFalse" Margin="0,0,10,10">Set IsSharedSizeScope="False"</Button> </StackPanel> <StackPanel Orientation="Horizontal" DockPanel.Dock="Top"> <Grid ShowGridLines="True" Margin="0,0,10,0"> <Grid.ColumnDefinitions> <ColumnDefinition SharedSizeGroup="FirstColumn"/> <ColumnDefinition SharedSizeGroup="SecondColumn"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto" SharedSizeGroup="FirstRow"/> </Grid.RowDefinitions> <Rectangle Fill="Silver" Grid.Column="0" Grid.Row="0" Width="200" Height="100"/> <Rectangle Fill="Blue" Grid.Column="1" Grid.Row="0" Width="150" Height="100"/> <TextBlock Grid.Column="0" Grid.Row="0" FontWeight="Bold">First Column</TextBlock> <TextBlock Grid.Column="1" Grid.Row="0" FontWeight="Bold">Second Column</TextBlock> </Grid> <Grid ShowGridLines="True"> <Grid.ColumnDefinitions> <ColumnDefinition SharedSizeGroup="FirstColumn"/> <ColumnDefinition SharedSizeGroup="SecondColumn"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto" SharedSizeGroup="FirstRow"/> </Grid.RowDefinitions> <Rectangle Fill="Silver" Grid.Column="0" Grid.Row="0"/> <Rectangle Fill="Blue" Grid.Column="1" Grid.Row="0"/> <TextBlock Grid.Column="0" Grid.Row="0" FontWeight="Bold">First Column</TextBlock> <TextBlock Grid.Column="1" Grid.Row="0" FontWeight="Bold">Second Column</TextBlock> </Grid> </StackPanel> <TextBlock Margin="10" DockPanel.Dock="Top" Name="txt1"/>
Note For the complete code sample, see How to: Share Sizing Properties Between Grids
Localization Attributes and Comments
Windows Presentation Foundation (WPF) localization comments are properties, inside XAML source code, supplied by developers to provide rules and hints for localization. Windows Presentation Foundation (WPF) localization comments contain two sets of information: localizability attributes and free-form localization comments. Localizability attributes are used by the WPF Localization API to indicate which resources are to be localized. Free-form comments are any information that the application author wants to include.
Localization Comments
If markup application authors have requirements for specific elements in XAML, such as constraints on text length, font family, or font size, they can convey this information to localizers with comments in the XAML code. The process for adding comments to source code is as follows:
Application developer adds localization comments to XAML source code.
During the build process, you can specify in the .proj file whether to leave the free-form localization comments in the assembly, strip out part of the comments, or strip out all the comments. The stripped-out comments are placed in a separate file. You specify your option using a LocalizationDirectivesToLocFile tag, eg:
<LocalizationDirectivesToLocFile>value</LocalizationDirectivesToLocFile>
The values that can be assigned are:
None - Both comments and attributes stay inside the assembly and no separate file is generated.
CommentsOnly - Strips only the comments from the assembly and places them in the separate LocFile.
All - Strips both the comments and the attributes from the assembly and places them both in a separate LocFile.
When localizable resources are extracted from BAML, the localizability attributes are respected by the BAML Localization API.
Localization comment files, containing only free-form comments, are incorporated into the localization process at a later time.
The following example shows how to add localization comments to a XAML file.
<TextBlock x:Id = "text01"
FontFamily = "Microsoft Sans Serif"
FontSize = "12"
Localization.Attributes = "$Content (Unmodifiable Readable Text)
FontFamily (Unmodifiable Readable)"
Localization.Comments = "$Content (Trademark)
FontSize (Trademark font size)" >
Microsoft
</TextBlock>
In the previous sample the Localization.Attributes section contains the localization attributes and the Localization.Comments section the free-form comments. The following tables show the attributes and comments and their meaning to the localizer.
Localization attributes | Meaning |
---|---|
$Content (Unmodifiable Readable Text) | Contents of the TextBlock element cannot be modified. Localizers cannot change the word "Microsoft". The content is visible (Readable) to the localizer. The category of the content is text. |
FontFamily (Unmodifiable Readable) | The font family property of the TextBlock element cannot be changed but it is visible to the localizer. |
Localization free-form comments | Meaning |
---|---|
$Content (Trademark) | The application author tells the localizer that the content in the TextBlock element is a trademark. |
FontSize (Trademark font size) | The application author indicates that the font size property should follow the standard trademark size. |
Localizability Attributes
The information in Localization.Attributes contains a list of pairs: the targeted value name and the associated localizability values. The target name can be a property name or the special $Content name. If it is a property name, the targeted value is the value of the property. If it is $Content, the target value is the content of the element.
There are three types of attributes:
Category. This specifies whether a value should be modifiable from a localizer tool. See Category.
Readability. This specifies whether a localizer tool should read (and display) a value. See Readability.
Modifiability. This specifies whether a localizer tool allows a value to be modified. See Modifiability.
These attributes can be specified in any order delimited by a space. In case duplicate attributes are specified, the last attribute will override former ones. For example, Localization.Attributes = "Unmodifiable Modifiable" sets Modifiability to Modifiable because it is the last value.
Modifiability and Readability are self-explanatory. The Category attribute provides predefined categories that help the localizer when translating text. Categories, such as, Text, Label, and Title give the localizer information about how to translate the text. There are also special categories: None, Inherit, Ignore, and NeverLocalize.
The following table shows the meaning of the special categories.
Category | Meaning |
---|---|
None | Targeted value has no defined category. |
Inherit | Targeted value inherits its category from its parent. |
Ignore | Targeted value is ignored in the localization process. Ignore affects only the current value. It will not affect child nodes. |
NeverLocalize | Current value cannot be localized. This category is inherited by the children of an element. |
Localization.Comments contains free-form strings concerning the targeted value. Application developers can add information to give localizers hints about how the applications text should be translated. The format of the comments can be any string surrounded by "()". Use '\' to escape characters.
Bidirectional Features in WPF Overview
Unlike any other development platform, WPF has many features that support rapid development of bidirectional content, for example, mixed left to right and right to left data in the same document. At the same time, WPF creates an excellent experience for users who require bidirectional features such as Arabic and Hebrew speaking users.
The following sections explain many bidirectional features together with examples illustrating how to achieve the best display of bidirectional content. Most of the samples use XAML, though you can easily apply the concepts to C# or Microsoft Visual Basic code.
FlowDirection
The basic property that defines the content flow direction in a WPF application is FlowDirection. This property can be set to one of two enumeration values, LeftToRight or RightToLeft. The property is available to all WPF elements that inherit from FrameworkElement.
The following examples set the flow direction of a TextBox element.
Left-to-right flow direction
<TextBlock Background="DarkBlue" Foreground="LightBlue" FontSize="20" FlowDirection="LeftToRight"> This is a left-to-right TextBlock </TextBlock>
Right-to-left flow direction
<TextBlock Background="LightBlue" Foreground="DarkBlue" FontSize="20" FlowDirection="RightToLeft"> This is a right-to-left TextBlock </TextBlock>
The following graphic shows how the previous code renders.
Graphic That Illustrates FlowDirection
An element within a user interface (UI) tree will inherit the FlowDirection from its container. In the following example, the TextBlock is inside a Grid, which resides in a Window. Setting the FlowDirection for the Window implies setting it for the Grid and TextBlock as well.
The following example demonstrates setting FlowDirection.
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="FlowDirectionApp.Window1" Title="BidiFeatures" Height="200" Width="700" FlowDirection="RightToLeft"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition/> <ColumnDefinition/> </Grid.ColumnDefinitions> <TextBlock Grid.Column="0" > This is a right-to-left TextBlock </TextBlock> <TextBlock Grid.Column="1" FlowDirection="LeftToRight"> This is a left-to-right TextBlock </TextBlock> </Grid> </Window>
The top level Window has a RightToLeftFlowDirection, so all elements contained within it also inherit the same FlowDirection. For an element to override a specified FlowDirection it must add an explicit direction change such as the second TextBlock in the previous example which changes toLeftToRight. When no FlowDirection is defined, the default LeftToRight applies.
The following graphic shows the output of the previous example.
Graphic That Illustrates Explicitly Assigned FlowDirection
FlowDocument
Many development platforms such as HTML, Win32 and Java provide special support for bidirectional content development. Markup languages such as HTML give content writers the necessary markup to display text in any required direction, for example the HTML 4.0 tag, "dir" that takes "rtl" or "ltr" as values. This tag is similar to the FlowDirection property, but the FlowDirection property works in a more advanced way to layout textual content and can be used for content other than text.
In WPF, a FlowDocument is a versatile UI element that can host a combination of text, tables, images and other elements. The samples in the following sections use this element.
Adding text to a FlowDocument can be done in more that one way. A simple way to do so is through a Paragraph which is a block-level element used to group content such as text. To add text to inline-level elements the samples use Span and Run. Span is an inline-level flow content element used for grouping other inline elements, while a Run is an inline-level flow content element intended to contain a run of unformatted text. A Spancan contain multiple Run elements.
The first document example contains a document that has a number of network share names; for example \\server1\folder\file.ext. Whether you have this network link in an Arabic or English document, you always want it to appear in the same way. The following graphic shows the link in an Arabic RightToLeft document.
Graphic That Illustrates Using the Span Element
Because the text is RightToLeft, all special characters, such as the "\", separate the text in a right to left order. That results in the link not being shown in the correct order, therefore to solve the problem, the text must be embedded to preserve a separate Run flowing LeftToRight. Instead of having a separate Run for each language, a better way to solve the problem is to embed the less frequently used English text into a larger Arabic Span.
The following graphic illustrates this.
Graphic That Illustrates Using the Run Element Embedded in a Span Element
The following example demonstrates using Run and Span elements in documents.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" FlowDirection="RightToLeft"> <FlowDocument> <Paragraph> <Span FlowDirection="RightToLeft" > ستجد الملف هنا: <Run FlowDirection="LeftToRight"> \\server1\filename\filename1.txt</Run> ثم باقى النص! </Span> </Paragraph> </FlowDocument> </Page>
Span Elements
The Span element works as a boundary separator between texts with different flow directions. Even Span elements with the same flow direction are considered to have different bidirectional scopes which means that the Span elements are ordered in the container’s FlowDirection, only the content within the Span element follows the FlowDirection of the Span.
The following graphic shows the flow direction of several TextBlock elements.
Graphic That Illustrates FlowDirection in Several TextBlock Elements
The following example shows how to use the Span and Run elements to produce the results shown in the previous graphic.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <StackPanel > <TextBlock FontSize="20" FlowDirection="RightToLeft"> <Run FlowDirection="LeftToRight">العالم</Run> <Run FlowDirection="LeftToRight" Foreground="Red" >فى سلام</Run> </TextBlock> <TextBlock FontSize="20" FlowDirection="LeftToRight"> <Run FlowDirection="RightToLeft">العالم</Run> <Run FlowDirection="RightToLeft" Foreground="Red" >فى سلام</Run> </TextBlock> <TextBlock FontSize="20" Foreground="Blue">العالم فى سلام</TextBlock> <Separator/> <TextBlock FontSize="20" FlowDirection="RightToLeft"> <Span Foreground="Red" FlowDirection="LeftToRight">Hello</Span> <Span FlowDirection="LeftToRight">World</Span> </TextBlock> <TextBlock FontSize="20" FlowDirection="LeftToRight"> <Span Foreground="Red" FlowDirection="RightToLeft">Hello</Span> <Span FlowDirection="RightToLeft">World</Span> </TextBlock> <TextBlock FontSize="20" Foreground="Blue">Hello World</TextBlock> </StackPanel> </Page>
In the TextBlock elements in the sample, the Span elements are laid out according to the FlowDirection of their parents, but the text within each Spanelement flows according to its own FlowDirection. This is applicable to Latin and Arabic – or any other language.
Adding xml:lang
The following graphic shows another example that uses numbers and arithmetic expressions, such as "200.0+21.4=221.4". Notice that only theFlowDirection is set.
Graphic That Displays Numbers Using Only FlowDirection
Users of this application will be disappointed by the output, even though the FlowDirection is correct the numbers are not shaped as Arabic numbers should be shaped.
XAML elements can include an XML attribute ( xml:lang) that defines the language of each element. XAML also supports a XML language principle whereby xml:lang values applied to parent elements in the tree are used by child elements. In the previous example, because a language was not defined for the Run element or any of its top level elements, the default xml:lang was used, which is en-US for XAML. The internal number shaping algorithm of Windows Presentation Foundation (WPF) selects numbers in the corresponding language – in this case English. To make the Arabic numbers render correctly xml:lang needs to be set.
The following graphic shows the example with xml:lang added.
Graphic That Illustrates Using the xml:lang Attribute
The following example adds xml:lang to the application.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" FlowDirection="RightToLeft"> <FlowDocument> <Paragraph> <Span FlowDirection="RightToLeft" Language="ar-SA"> العملية الحسابية: "200.0+21.4=221.4" </Span> </Paragraph> </FlowDocument> </Page>
Be aware that many languages have different xml:lang values depending on the targeted region, for example, "ar-SA" and "ar-EG" represent two variations of Arabic. The previous examples illustrate that you need to define both the xml:lang and FlowDirection values.
FlowDirection with Non-text Elements
FlowDirection defines not only how text flows in a textual element but also the flow direction of almost every other UI element. The following graphic shows a ToolBar that uses a horizontal LinearGradientBrush to draw its background.
Graphic That Shows a ToolBar with a Left to Right Gradient
After setting the FlowDirection to RightToLeft, not only the ToolBar buttons are arranged from right to left, but even the LinearGradientBrush realigns its offsets to flow from right to left.
The following graphic shows the realignment of the LinearGradientBrush.
Graphic That Shows a ToolBar with a Right to Left Gradient
The following example draws a RightToLeftToolBar. (To draw it left to right, remove the FlowDirection attribute on the ToolBar.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <ToolBar FlowDirection="RightToLeft" Height="50" DockPanel.Dock="Top"> <ToolBar.Background> <LinearGradientBrush StartPoint="0,0.5" EndPoint="1,1"> <LinearGradientBrush.GradientStops> <GradientStop Color="DarkRed" Offset="0" /> <GradientStop Color="DarkBlue" Offset="0.3" /> <GradientStop Color="LightBlue" Offset="0.6" /> <GradientStop Color="White" Offset="1" /> </LinearGradientBrush.GradientStops> </LinearGradientBrush> </ToolBar.Background> <Button FontSize="12" Foreground="White">Button1</Button> <Rectangle Width="20"/> <Button FontSize="12" Foreground="White">Button2</Button> <Rectangle Width="20"/> <Button FontSize="12" Foreground="White">Button3</Button> <Rectangle Width="20"/> <Button FontSize="12" Foreground="White">Button4</Button> <Rectangle Width="20"/> </ToolBar> </Page>
FlowDirection Exceptions
There are a few cases where FlowDirection does not behave as expected. This section covers two of these exceptions.
Image
An Image represents a control that displays an image. In XAML it can be used with a Source property that defines the uniform resource identifier (URI) of the Image to display.
Unlike other UI elements, an Image does not inherit the FlowDirection from the container. However, if the FlowDirection is set explicitly toRightToLeft, an Image is displayed flipped horizontally. This is implemented as a convenient feature for developers of bidirectional content; because in some cases, horizontally flipping the image produces the desired effect.
The following graphic shows a flipped Image.
Graphic That Illustrates a Flipped Image
The following example demonstrates that the Image fails to inherit the FlowDirection from the StackPanel that contains it. Note You must have a file named ms_logo.jpg on your C:\ drive to run this example.
<StackPanel xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' FlowDirection="RightToLeft"> <Image Source="file://c:/ms_logo.jpg" Width="147" Height="50"/> <Separator Height="10"/> <Image Source="file://c:/ms_logo.jpg" Width="147" Height="50" FlowDirection="LeftToRight" /> <Separator Height="10"/> <Image Source="file://c:/ms_logo.jpg" Width="147" Height="50" FlowDirection="RightToLeft"/> </StackPanel>
Note Included in the download files is an ms_logo.jpg file. The code assumes that the .jpg file is not inside your project but somewhere on the C:\ drive. You must copy the .jpg from the project files to your C:\ drive or change the code to look for the file inside the project. To do this changeSource="file://c:/ms_logo.jpg" to Source="ms_logo.jpg".
Paths
In addition to an Image, another interesting element is Path. A Path is an object that can draw a series of connected lines and curves. It behaves in a manner similar to an Image regarding its FlowDirection; for example its RightToLeftFlowDirection is a horizontal mirror of its LeftToRight one. However, unlike an Image, Path inherits its FlowDirection from the container and one does not need to specify it explicitly.
The following example draws a simple arrow using 3 lines. The first arrow inherits the RightToLeft flow direction from the StackPanel so that its start and end points are measured from a root on the right side. The second arrow which has an explicit RightToLeftFlowDirection also starts on the right side. However, the third arrow has its starting root on the left side. For more information on drawing see LineGeometry andGeometryGroup.
<StackPanel xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' FlowDirection="RightToLeft"> <Path Stroke="Blue" StrokeThickness="4"> <Path.Data> <GeometryGroup > <LineGeometry StartPoint="300,10" EndPoint="350,30" /> <LineGeometry StartPoint="10,30" EndPoint="352,30" /> <LineGeometry StartPoint="300,50" EndPoint="350,30" /> </GeometryGroup> </Path.Data> </Path> <Path Stroke="Red" StrokeThickness="4" FlowDirection="RightToLeft"> <Path.Data> <GeometryGroup > <LineGeometry StartPoint="300,10" EndPoint="350,30" /> <LineGeometry StartPoint="10,30" EndPoint="352,30" /> <LineGeometry StartPoint="300,50" EndPoint="350,30" /> </GeometryGroup> </Path.Data> </Path> <Path Stroke="Green" StrokeThickness="4" FlowDirection="LeftToRight"> <Path.Data> <GeometryGroup > <LineGeometry StartPoint="300,10" EndPoint="350,30" /> <LineGeometry StartPoint="10,30" EndPoint="352,30" /> <LineGeometry StartPoint="300,50" EndPoint="350,30" /> </GeometryGroup> </Path.Data> </Path> </StackPanel>
The following graphic shows the output of the previous example.
Graphic That Illustrates Arrows Drawn Using the Path Element
The Image and Path are two examples of a how Windows Presentation Foundation (WPF) uses FlowDirection. Beside laying out UI elements in a specific direction within a container, FlowDirection can be used with elements such as InkPresenter which renders ink on a surface,LinearGradientBrush, RadialGradientBrush. Whenever you need a right to left behavior for your content that mimics a left to right behavior, or vice versa, Windows Presentation Foundation (WPF) provides that capability.
Number Substitution
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.
Override: Number culture is that of CultureOverride property.
Text: Number culture is the culture of the text run. In markup, this would be xml:lang, or its alias Language property ( Language orLanguage). Also, it is the default for classes deriving from FrameworkContentElement. Such classes includeSystem.Windows.Documents.Paragraph, System.Windows.Documents.Table, System.Windows.Documents.TableCell and so forth.
User: Number culture is the culture of the current thread. This property is the default for all subclasses of FrameworkElement such as Page,Window and TextBlock.
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>
Globalization and Localization How-to Topics
How to: Localize an Application
This tutorial explains how to create a localized application by using the LocBaml tool.
Note |
---|
The LocBaml tool is not a production-ready application. It is presented as a sample that uses some of the localization APIs and illustrates how you might write a localization tool. |
Overview
This discussion gives you a step-by-step approach to localizing an application. First, you will prepare your application so that the text that will be translated can be extracted. After the text is translated, you will merge the translated text into a new copy of the original application.
Requirements
Over the course of this discussion, you will use Microsoft build engine (MSBuild), which is a compiler that runs from the command line.
Also, you will be instructed to use a project file. For instructions on how to use MSBuild and project files, see Building and Deploying WPF Applications.
All the examples in this discussion use en-US (English-US) as the culture. This enables you to work through the steps of the examples without installing a different language.
Create a Sample Application
In this step, you will prepare your application for localization. In the Windows Presentation Foundation (WPF) samples, a HelloApp sample is supplied that will be used for the code examples in this discussion. If you would like to use this sample, download the Extensible Application Markup Language (XAML) files from the LocBaml Tool Sample.
Develop your application to the point where you want to start localization.
Specify the development language in the project file so that MSBuild generates a main assembly and a satellite assembly (a file with the .resources.dll extension) to contain the neutral language resources. The project file in the HelloApp sample is HelloApp.csproj. In that file, you will find the development language identified as follows:
<UICulture>en-US</UICulture>
Add Uids to your XAML files. Uids are used to keep track of changes to files and to identify items that must be translated. To add Uids to your files, run updateuid on your project file:
msbuild /t:updateuid helloapp.csproj
To verify that you have no missing or duplicate Uids, run checkuid:
msbuild /t:checkuid helloapp.csproj
After running updateuid, your files should contain Uids. For example, in the Pane1.xaml file of HelloApp, you should find the following:
<StackPanel x:Uid="StackPanel_1">
<TextBlock x:Uid="TextBlock_1">Hello World</TextBlock>
<TextBlock x:Uid="TextBlock_2">Goodbye World</TextBlock>
</StackPanel>
Create the Neutral Language Resources Satellite Assembly
After the application is configured to generate a neutral language resources satellite assembly, you build the application. This generates the main application assembly, as well as the neutral language resources satellite assembly that is required by LocBaml for localization. To build the application:
Compile HelloApp to create a dynamic-link library (DLL):
msbuild helloapp.csproj
The newly created main application assembly, HelloApp.exe, is created in the following folder:
C:\HelloApp\Bin\Debug\
The newly created neutral language resources satellite assembly, HelloApp.resources.dll, is created in the following folder:
C:\HelloApp\Bin\Debug\en-US\
Build the LocBaml Tool
All the files necessary to build LocBaml are located in the WPF samples. Download the C# files from the LocBaml Tool Sample.
From the command line, run the project file (locbaml.csproj) to build the tool:
msbuild locbaml.csproj
Go to the Bin\Release directory to find the newly created executable file (locbaml.exe). Example:C:\LocBaml\Bin\Release\locbaml.exe.
The options that you can specify when you run LocBaml are as follows:
parse or -p: Parses Baml, resources, or DLL files to generate a .csv or .txt file.
generate or -g: Generates a localized binary file by using a translated file.
out or -o [ filedirectory] : Output file name.
culture or -cul [ culture] : Locale of output assemblies.
translation or -trans [ translation.csv] : Translated or localized file.
asmpath or -asmpath: [ filedirectory] : If your XAML code contains custom controls, you must supply the asmpath to the custom control assembly.
nologo: Displays no logo or copyright information.
verbose: Displays verbose mode information.
Note If you need a list of the options when you are running the tool, type LocBaml.exe and press ENTER.
Use LocBaml to Parse a File
Now that you have created the LocBaml tool, you are ready to use it to parse HelloApp.resources.dll to extract the text content that will be localized.
Copy LocBaml.exe to your application's bin\debug folder, where the main application assembly was created.
To parse the satellite assembly file and store the output as a .csv file, use the following command:
LocBaml.exe /parse HelloApp.resources.dll /out:Hello.csv
Note If the input file, HelloApp.resources.dll, is not in the same directory as LocBaml.exe move one of the files so that both files are in the same directory.
When you run LocBaml to parse files, the output consists of seven fields delimited by commas (.csv files) or tabs (.txt files). The following shows the parsed .csv file for the HelloApp.resources.dll:
HelloApp.g.en-US.resources:window1.baml,Stack1:System.Windows.Controls.StackPanel.$Content,Ignore,FALSE, FALSE,,#Text1;#Text2;
HelloApp.g.en-US.resources:window1.baml,Text1:System.Windows.Controls.TextBlock.$Content,None,TRUE, TRUE,,Hello World
HelloApp.g.en-US.resources:window1.baml,Text2:System.Windows.Controls.TextBlock.$Content,None,TRUE, TRUE,,Goodbye World
The seven fields are:
BAML Name. The name of the BAML resource with respect to the source language satellite assembly.
Resource Key. The localized resource identifier.
Category. The value type. See Localization Attributes and Comments.
Readability. Whether the value can be read by a localizer. See Localization Attributes and Comments.
Modifiability. Whether the value can be modified by a localizer. See Localization Attributes and Comments.
Comments. Additional description of the value to help determine how a value is localized. See Localization Attributes and Comments.
Value. The text value to translate to the desired culture.
The following table shows how these fields map to the delimited values of the .csv file:
BAML name
Resource key
Category
Readability
Modifiability
Comments
Value
HelloApp.g.en-US.resources:window1.baml
Stack1:System.Windows.Controls.StackPanel.$Content
Ignore
FALSE
FALSE
#Text1;#Text2
HelloApp.g.en-US.resources:window1.baml
Text1:System.Windows.Controls.TextBlock.$Content
None
TRUE
TRUE
Hello World
HelloApp.g.en-US.resources:window1.baml
Text2:System.Windows.Controls.TextBlock.$Content
None
TRUE
TRUE
Goodbye World
Notice that all the values for the Comments field contain no values; if a field doesn't have a value, it is empty. Also notice that the item in the first row is neither readable nor modifiable, and has "Ignore" as its Category value, all of which indicates that the value is not localizable.
To facilitate discovery of localizable items in parsed files, particularly in large files, you can sort or filter the items by Category, Readability, and Modifiability. For example, you can filter out unreadable and unmodifiable values.
Translate the Localizable Content
Use any tool that you have available to translate the extracted content. A good way to do this is to write the resources to a .csv file and view them in Microsoft Excel, making translation changes to the last column (value).
Use LocBaml to Generate a New .resources.dll File
The content that was identified by parsing HelloApp.resources.dll with LocBaml has been translated and must be merged back into the original application. Use the generate or -g option to generate a new .resources.dll file.
Use the following syntax to generate a new HelloApp.resources.dll file. Mark the culture as en-US (/cul:en-US).
LocBaml.exe /generate HelloApp.resources.dll /trans:Hello.csv /out:c:\ /cul:en-US
Note If the input file, Hello.csv, is not in the same directory as the executable, LocBaml.exe, move one of the files so that both files are in the same directory.
Replace the old HelloApp.resources.dll file in the C:\HelloApp\Bin\Debug\en-US\HelloApp.resources.dll directory with your newly created HelloApp.resources.dll file.
"Hello World" and "Goodbye World" should now be translated in your application.
To translate to a different culture, use the culture of the language that you are translating to. The following example shows how to translate to French-Canadian:
LocBaml.exe /generate HelloApp.resources.dll /trans:Hellofr-CA.csv /out:c:\ /cul:fr-CA
In the same assembly as the main application assembly, create a new culture-specific folder to house the new satellite assembly. For French-Canadian, the folder would be fr-CA.
Copy the generated satellite assembly to the new folder.
To test the new satellite assembly, you need to change the culture under which your application will run. You can do this in one of two ways:
Change your operating system's regional settings ( Start | Control Panel | Regional and Language Options).
In your application, add the following code to App.xaml.cs:
<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="SDKSample.App" x:Uid="Application_1" StartupUri="Window1.xaml"> </Application>
using System.Windows; // Application using System.Globalization; // CultureInfo using System.Threading; // Thread namespace SDKSample { public partial class App : Application { public App() { // Change culture under which this application runs CultureInfo ci = new CultureInfo("fr-CA"); Thread.CurrentThread.CurrentCulture = ci; Thread.CurrentThread.CurrentUICulture = ci; } } }
Some Tips for Using LocBaml
All dependent assemblies that define custom controls must be copied into the local directory of LocBaml or installed into the GAC. This is necessary because the localization API must have access to the dependent assemblies when it reads the binary XAML (BAML).
If the main assembly is signed, the generated resource DLL must also be signed in order for it to be loaded.
The version of the localized resource DLL needs to be synchronized with the main assembly.
You should now have a basic understanding of how to use the LocBaml tool. You should be able to make a file that contains Uids. By using the LocBaml tool, you should be able to parse a file to extract the localizable content, and after the content is translated, you should be able to generate a .resources.dll file that merges the translated content. This topic does not include every possible detail, but you now have the knowledge necessary to use LocBaml for localizing your applications.
How to: Use Automatic Layout to Create a Button
This example describes how to use the automatic layout approach to create a button in a localizable application.
Localization of a user interface (UI) can be a time consuming process. Often localizers need to resize and reposition elements in addition to translating text. In the past each language that a UI was adapted for required adjustment. Now with the capabilities of Windows Presentation Foundation (WPF) you can design elements that reduce the need for adjustment. The approach to writing applications that can be more easily resized and repositioned is called automatic layout.
The following two Extensible Application Markup Language (XAML) examples create applications that instantiate a button; one with English text and one with Spanish text. Notice that the code is the same except for the text; the button adjusts to fit the text.
Example
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="ButtonLoc.Pane1" Name="myWindow" SizeToContent="WidthAndHeight" > <DockPanel> <Button FontSize="28" Height="50">My name is Hope.</Button> </DockPanel> </Window>
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="ButtonLoc.Pane1" Name="myWindow" SizeToContent="WidthAndHeight" > <DockPanel> <Button FontSize="28" Height="50">Me llamo Esperanza.</Button> </DockPanel> </Window>
The following graphic shows the output of the code samples.
Auto Resizable Button
How to: Use a Grid for Automatic Layout
This example describes how to use a grid in the automatic layout approach to creating a localizable application.
Localization of a user interface (UI) can be a time consuming process. Often localizers need to re-size and reposition elements in addition to translating text. In the past each language that a UI was adapted for required adjustment. Now with the capabilities of Windows Presentation Foundation (WPF) you can design elements that reduce the need for adjustment. The approach to writing applications that can be more easily re-sized and repositioned is called auto layout.
The following Extensible Application Markup Language (XAML) example demonstrates using a grid to position some buttons and text. Notice that the height and width of the cells are set to Auto; therefore the cell that contains the button with an image adjusts to fit the image. Because the Gridelement can adjust to its content it can be useful when taking the automatic layout approach to designing applications that can be localized.
Example
The following example shows how to use a grid.
<Grid Name="grid" ShowGridLines ="false"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <TextBlock Margin="10, 10, 5, 5" Grid.Column="0" Grid.Row="0" FontSize="24">Grid </TextBlock> <TextBlock Margin="10, 10, 5, 5" Grid.Column="0" Grid.Row="1" FontSize="12" Grid.ColumnSpan="2">The following buttons and text are positioned using a Grid. </TextBlock> <Button Margin="10, 10, 5, 5" Grid.Column="0" Grid.Row="2" Background="Pink" BorderBrush="Black" BorderThickness="10">Button 1 </Button> <TextBlock Margin="10, 10, 5, 5" Grid.Column="1" Grid.Row="2" FontSize="12" VerticalAlignment="Center" TextWrapping="WrapWithOverflow">Sets the background color. </TextBlock> <Button Margin="10, 10, 5, 5" Grid.Column="0" Grid.Row="3" Foreground="Red"> Button 2 </Button> <TextBlock Margin="10, 10, 5, 5" Grid.Column="1" Grid.Row="3" FontSize="12" VerticalAlignment="Center" TextWrapping="WrapWithOverflow">Sets the foreground color. </TextBlock> <Button Margin="10, 10, 5, 5" Grid.Column="0" Grid.Row="4"> <Image Source="data\flower.jpg"></Image> </Button> <TextBlock Margin="10, 10, 5, 5" Grid.Column="1" Grid.Row="4" FontSize="12" VerticalAlignment="Center" TextWrapping="WrapWithOverflow">Adds an image as the button's content. </TextBlock> </Grid>
The following graphic shows the output of the code sample.
Grid
How to: Use a ResourceDictionary to Manage Localizable String Resources
This example shows how to use a ResourceDictionary to package localizable string resources for Windows Presentation Foundation (WPF) applications.
To use a ResourceDictionary to manage localizable string resources
Create a ResourceDictionary that contains the strings you would like to localize. The following code shows an example.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:system="clr-namespace:System;assembly=mscorlib"> <!-- String resource that can be localized --> <system:String x:Key="localizedMessage">en-US Message</system:String> </ResourceDictionary>
This code defines a string resource, localizedMessage, of type String, from the System namespace in mscorlib.dll.
Add the ResourceDictionary to your application, using the following code.
<Application.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="StringResources.xaml" /> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Application.Resources>
Use the string resource from markup, using Extensible Application Markup Language (XAML) like the following.
<!-- Declarative use of string resource from StringResources.xaml resource dictionary --> <TextBox DockPanel.Dock="Top" Text="{StaticResource localizedMessage}" />
Localize the application. For more information, see How to: Localize an Application.
How to: Use Resources in Localizable Applications
Localization means to adapt a UI to different cultures. To do this, text such as titles, captions, list box items and so forth have to be translated. To make translation easier the items to be translated are collected into resource files. See How to: Localize an Application for information on how to create a resource file for localization. To make a WPF application localizable, developers need to build all the localizable resources into a resource assembly. The resource assembly is localized into different languages, and the code-behind uses resource management API to load. One of the files required for a WPF application is a project file (.proj). All resources that you use in your application should be included in the project file. The following code example shows this.
Example
XAML
<Resource Include="data\picture1.jpg"/>
<EmbeddedResource Include="data\stringtable.en-US.restext"/>
To use a resource in your application, you instantiate ResourceManager and load the resource you want to use. The following demonstrates how to do this.
void OnClick(object sender, RoutedEventArgs e) { ResourceManager rm = new ResourceManager ("MySampleApp.data.stringtable", Assembly.GetExecutingAssembly()); Text1.Text = rm.GetString("Message"); }