cmiles – blog

Charles Miles – Tucson Hiking, Outside and Adventures, Excel, .net, Life

Generating WPF with T4 Templates for Localization

Recently I had the opportunity to explore setting up a small WPF application for globalization/localization. I had no previous experience with this process, so I started by doing some searching and reading. I thought the links below were particularly interesting:

WPF Localization Guidance – Included in this project is The WPF Localization Guidance PDF by Rick Strahl and Michele Leroux Bustamante – I highly recommend reading this! Great details, several approaches are examined.

Creating an Internationalized Wizard in WPF by Josh Smith, Karl Shifflett. Very nice walk thru of building an Internationalized Wizard style app with resource files. This is a very approachable place to start.

WPF Multi-Lingual at Runtime by Andrew Wood – A XmlDataProvider based solution.

Localizing WPF Applications using Locbaml by brunzefb – This link is notable for its comparison of several different approaches.

WPF Localization by Sacha Barber – Interesting because of the third method shown in the article that uses ResourceDictionaries. For more information on using Resource Dictionaries see the answer to this question on Stack Overflow by  Ray Burns.

 

One interesting thing about many of the approaches above is that the focus seems to on localizing strings/text, and to a lesser degree settings, images and other resources. In the context of WPF I was surprised not to find more information about customizing the layout of the UI as part of the translation into another language. I think the structure of XAML encourages a UI composed of many elements that will need more than string substitution to be ideal in another language.

For example, the XAML below and UI it generates seems typical of the kind of composition and ‘richness’ encouraged by WPF/XAML’s structure and tools:

<UserControl
		xml:lang="en"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="basic_styles.xaml" />
                <ResourceDictionary Source="equations.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </UserControl.Resources>

	<WrapPanel Style="{StaticResource description_wrap_panel_style}">

		<TextBlock	x:Name="c_textblock"
					Style="{StaticResource description_textblock_style}"
					AutomationProperties.Name = "Enter a Constant, C, that satisfies the following equation: the standard error of the estimate is equal to the constant C over the square root of the sample size">
			Enter a constant, <Italic>C</Italic>, that satisfies
    	<InlineUIContainer Style='{StaticResource image_container_style}'>
    		<Image x:Name='formula_11'
					Source='{StaticResource equation_11}'
					Style='{StaticResource image_style}'>
                <Image.Height>
                    <MultiBinding Converter='{StaticResource image_size}'>
                        <Binding Mode='OneWay'
								ElementName='formula_11'
								Path='Tag'/>
                        <Binding Mode='OneWay'
								ElementName='c_textblock'
								Path='FontSize'/>
                    </MultiBinding>
                </Image.Height>
            </Image>
    	</InlineUIContainer>
	    </TextBlock>

		<TextBlock 	Style="{StaticResource description_textblock_style}"
					KeyboardNavigation.TabIndex="1">
			(<Hyperlink AutomationProperties.Name='More information about the constant C' x:Name='c_hyperlink'>more info</Hyperlink>)
		</TextBlock>

		<TextBox 	Style="{StaticResource entry_textbox_style}"
					AutomationProperties.LabeledBy="{Binding ElementName=c_textblock}"
					KeyboardNavigation.TabIndex="0">
		</TextBox>

	</WrapPanel>
</UserControl>

Certainly changing out all of the strings to translate this example is possible (slightly painful because of the number of string to change out…). In French or Spanish this would work fine:

 

 

However, here is the same UI translated into Japanese – notice that the translator did not keep the elements in the same position.

 

To accommodate a flexible layout in this application I decided to use T4 templates to generate ‘loose’ XAML files. These files are included in the output and parsed at runtime based on the CurrentCulture.

The code below is the contents of the file base_block.tt – this t4 template holds the common elements of the XAML files and will be ‘imported’ by the language specific t4 templates. The language specific templates will provide values for the variables introduced in base_block.tt. One important detail is the use of encoding="Unicode" – I assumed “Utf-8” would work but the XamlParser would error on some characters when the template specified Utf-8, apparently because of the BOM setting…

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ output extension=".xaml" encoding="Unicode"#>

<UserControl
		xml:lang="<#= this.xml_lang #>"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="basic_styles.xaml" />
                <ResourceDictionary Source="equations.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </UserControl.Resources>

	<WrapPanel Style="{StaticResource description_wrap_panel_style}">

		<TextBlock	x:Name="c_textblock"
					Style="{StaticResource description_textblock_style}"
					AutomationProperties.Name = "<#= this.textblock_automation_name #>">
			<#= this.textblock_constant_C_contents #>
	    </TextBlock>

		<TextBlock 	Style="{StaticResource description_textblock_style}"
					KeyboardNavigation.TabIndex="1">
			<#= this.hyperlink_textblock_contents #>
		</TextBlock>

		<TextBox 	Style="{StaticResource entry_textbox_style}"
					AutomationProperties.LabeledBy="{Binding ElementName=c_textblock}"
					KeyboardNavigation.TabIndex="0">
		</TextBox>

	</WrapPanel>
</UserControl>

<#+

	private string xml_lang = @"";
	private string textblock_constant_C_contents = @"";
	private string textblock_automation_name = @"";
	private string hyperlink_textblock_contents = @"";

#>

Note that the variables hold all of the information that I wanted to be able to manipulate – not just the string portions of the UI. The file for English, en.tt:

<# 
	
	xml_lang = @"en";

	textblock_constant_C_contents = 
		@"Enter a constant, <Italic>C</Italic>, that satisfies
    	<InlineUIContainer Style='{StaticResource image_container_style}'>
    		<Image x:Name='formula_11' 
					Source='{StaticResource equation_11}' 
					Style='{StaticResource image_style}'>
                <Image.Height>
                    <MultiBinding Converter='{StaticResource image_size}'>
                        <Binding Mode='OneWay' 
								ElementName='formula_11' 
								Path='Tag'/>                        
                        <Binding Mode='OneWay' 
								ElementName='c_textblock' 
								Path='FontSize'/>
                    </MultiBinding>
                </Image.Height>
            </Image>
    	</InlineUIContainer>";

	textblock_automation_name = @"Enter a Constant, C, that satisfies the following equation: the standard error of the estimate is equal to the constant C over the square root of the sample size";

	hyperlink_textblock_contents = @"(<Hyperlink AutomationProperties.Name='More information about the constant C' x:Name='c_hyperlink'>more info</Hyperlink>)";

#>

<#@ include file="base_block.tt" #>

The file for the Spanish version:

    <#
	
        xml_lang = @"es";

        textblock_constant_C_contents =
            @"Ingrese una constante, <Italic>C</Italic>, para resolver
            <InlineUIContainer Style='{StaticResource image_container_style}'>
                <Image x:Name='formula_11'
                        Source='{StaticResource equation_11}'
                        Style='{StaticResource image_style}'>
                    <Image.Height>
                        <MultiBinding Converter='{StaticResource image_size}'>
                            <Binding Mode='OneWay'
                                    ElementName='formula_11'
                                    Path='Tag'/>
                            <Binding Mode='OneWay'
                                    ElementName='c_textblock'
                                    Path='FontSize'/>
                        </MultiBinding>
                    </Image.Height>
                </Image>
            </InlineUIContainer>";

        textblock_automation_name = @"Ingrese una constante, C, que satisface la siguiente ecuación: el error estándar de la estimación es igual a la constante C sobre la raíz cuadrada del tamaño de la muestra.";

        hyperlink_textblock_contents = @"(<Hyperlink AutomationProperties.Name='Más información acerca de la constante C'>más información</Hyperlink>)";

    #>

    <#@ include file="base_block.tt" #>

Each of the XAML files has a Build Action of ‘Content’ so that it is included in the output for the project.  At runtime I look at the CurrentCulture, compare that to the names of the generated XAML files that are available, feed the file to XamlReader.Load() and add the resulting UserControl into the UI as needed. A small sample app demonstrating this is a available here.

 

I do not have enough experience with globalization/localization to be confident this approach would work in all situations, but for this application it did work and I enjoyed that it: easily allows flexible UI layout, generated XAML files that could be viewed in the VS editor and is simple to use. I seems to me that the biggest downsides to this approach are that it does not allow translators to work on a simple value pair style file and the loose XAML does not allow for a code-behind (which is usually fine but occasionally awkward – at least for me). I would love to hear any comments or feedback on this approach!

 

Special thanks to Lance and Kent for translating for me!! And extra thanks to Lance for pointing out how to cleanly post source code on WordPress.com

 

Enjoy!

CM

Filed under: .net, , , , , ,

twitter -> twitterings

  • Just saw the new Resharper 6.1 Early Access version has Async CTP support listed - downloading now... 6 months ago
  • Quick post about FeedDemon/Pinboard/Save to Pinboard on Android as my choice for Google Reader Sharing cmiles.info/2011/11/05/goo… 6 months ago
  • FeedDemon+Custom Sharing XML->Pinboard; NewsRob + Save to Pinboard on Android - replacing Google Reader sharing, Reader now just for sync... 7 months ago
  • Doing a few Max - cycling74.com - tutorials - what fun, first time in a decade I have played with this... 7 months ago

RSS pinboard -> links

  • Gmap4 - Share Enhanced Google Maps
    A very cool tool - free for non-commercial use - that can produce very nice (and very usable) topo maps that can have data (GPX, KML,...) overlaid on it very easily!
  • Vistoso Hiking Club Hikes Database
    Short descriptions of hikes near Tucson with a few off trail and interesting destinations - no doubt there are more informative sources but sometimes the simplest thing is nice...
  • Bill Hill - Pictures
    I don't stumble on that many well organized great sets of Tucson hiking photos so saving this link!
  • [WPF] How to bind to data when the DataContext is not inherited
    This solved a problem for me with control of DataGrid Column visibility via Data Binding. Did not test/research/profile this extensively, but for my problem this worked like a charm.

RSS cmiles-consuming -> posts

  • Kafka on the Shore and The Wind-Up Bird Chronicle, Haruki Murakami 2012 June 1
    Kafka on the Shore and The Wind-Up Bird Chronicle by Haruki Murakami are favorites of mine. To combine them into one blog post seems slightly odd – they are not part of a series and are complex enough that reducing them down to a few sentences seems like a disservice. But what these have in [...]
  • Hyperion and The Fall of Hyperion, Dan Simmons 2012 May 16
    I first read Dan Simmons‘s Hyperion in the early 1990s – the Hyperion Cantos is quite famous and I would occasionally see Hyperion listed in Amazon recommendations and ‘top’ lists (see the ISFDB Top 100 Lists and Your Picks: Top 100 Science-Fiction, Fantasy Books from npr books (note that you may get some amusement out [...]
  • Wool Omnibus (Wool 1-5), Hugh Howey 2012 May 1
    I don’t usually pay much attention to Amazon’s recommendations – but after finishing 1Q84 I wanted something new and, for whatever reason, the Wool Omnibus by Hugh Howey jumped out at me on Amazon’s list. Wool takes place in a bleak future where the characters live in self sufficient underground silos with only a vague [...]
  • 1Q84, Haruki Murakami 2012 April 15
    I am a fan of Haruki Murakami, so I was excited when the English version of 1Q84 became available. I picked up and put down this book several times while reading it and I have to admit that I just really don’t know what to say about it – Long/Interesting/What?/I am glad I read it/Why?/1984/hmmm… [...]
  • Ride the Divide 2012 April 2
    Ride the Divide has great scenery, interesting people, injuries, struggles and riding along the Great Divide Mountain Bike Route that goes from Banff, Canada to the Mexican border! While I am familiar with some of the long distance hiking trails in the US I had no idea this route existed and it was fascinating to [...]
  • Forks over Knives 2012 March 20
    Forks over Knives (official website), directed by Lee Fulkerson, is a rather long documentary with the singular message that plant based diets are better than meat based diets (it may be slightly more subtle than that, but I think that is a pretty fair summary). I watched the film days after seeing Tabloid and could [...]
  • Tabloid, The Thin Blue Line 2012 March 15
    Tabloid – by Errol Morris – is the story of Joyce McKinney. McKinney was involved in a widely publicized case/scandal/incident in England in the late 1970s sometimes given the colorful name ‘The Case of the Manacled Mormon’. I had no knowledge of the events prior to the film – which might have added to the [...]
  • Jig 2012 March 1
    Jig follows a number of dancers leading up to their participation in the 40th Irish Dancing World Championships (2010). This film is a friendly, short and entertaining glimpse into a world that I did not know even existed – great fun. Simple, good! Rating: 4 of 5 First Watch Date: December 2011 CM
email: charles@cmiles.info

flickr -> pictures

1205 Looking down Pontatoc Canyon

1205 Looking up canyon from the end of the Pontatoc Canyon Trail

1205 Pontatoc Canyon End of Trail Sign

1205 Looking up at the rocks above the end of the Pontatoc Canyon Trail

1205 Catepillar

1205 Maybe from the fire fighting

1205 Looking back up Sabino Canyon from Brinkley Point

1205 View towards Sabino Canyon from Brinkley Point

1205 Charles and Alison

1205 View from Brinkley Point

More Photos
Follow

Get every new post delivered to your Inbox.