Post on 14-Dec-2015
How To Achieve World(-Ready) Domination In Silverlight
Guy Smith-Ferrier guy@guysmithferrier.com Blog: http://www.guysmithferrier.com
About… Author of .NET
Internationalization Visit http://www.dotneti18n.com to
download the complete source code
The .NET Developer Network http://www.dotnetdevnet.com Free user group for .NET
developers, architects and IT Pros based in Bristol
DDD South West http://www.dddsouthwest.com Taunton, Saturday 23rd May 2009
Agenda
Localizing Silverlight Using .resx Files Downloading Localized Resources On Demand Silverlight Installation User Experience Silverlight UI Localization Silverlight Fonts and Font Management Silverlight Globalization
Localizing Silverlight Using .resx Files
Using Visual Studio 2008 create a new Silverlight app
Add a button
Add a Resources File (In Solution Explorer, right click WpfApplication1, select Add | New Item, select Resources File) and call it PageResources.resx Add a new resource entry called "Button_1" with a
value of "Hello World" Set the Access Modifier to Public (using the combo
box) Change the constructor to public in
PageResources.Designer.cs
<Button Content="Hello World"/>
Localizing Silverlight Using .resx Files
(continued) In Page.xaml add a "Resources"
namespace, a static resource and change the button to use a static resource
<UserControl x:Class="SilverlightApplication1.Page" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Resources="clr-namespace:SilverlightApplication1" Width="400" Height="300"> <UserControl.Resources> <Resources:PageResources x:Name="PageResources"/> </UserControl.Resources> <Grid x:Name="LayoutRoot" Background="White"> <Button Content="{Binding Button_1, Source={StaticResource PageResources}}"/> </Grid></UserControl>
Localizing Silverlight Using .resx Files
(continued) In Visual Studio copy PageResources.resx
to PageResources.fr-FR.resx Change the "Button_1" resource value to
"Bonjour Le Monde" Open SilverlightApplication1.csproj using
NotePad, locate the SupportedCultures element and set it to fr-FR
<SupportedCultures>fr-FR</SupportedCultures>
Localizing Silverlight Using .resx Files
(continued) Open SilverlightApplication1TestPage.aspx
and add InitParameters to the Silverlight control
<asp:Silverlight ID="Xaml1" runat="server" Source="~/ClientBin/SilverlightApplication1.xap" MinimumVersion="2.0.31005.0" Width="100%" Height="100%" InitParameters="UICulture=fr-FR" />
Localizing Silverlight Using .resx Files
(continued) In App.xaml.cs change the
Application_Startup method
Run the application
private void Application_Startup( object sender, StartupEventArgs e){ string cultureName = e.InitParams["UICulture"].ToString(); if (! String.IsNullOrEmpty(cultureName)) Thread.CurrentThread.CurrentUICulture = new CultureInfo(cultureName);
this.RootVisual = new Page();}
Solutions To The Internal Constructor
1. Use a different Strongly Typed Resource Class code generator PublicResourceCodeGenerator
http://www.dotneti18n.com/Downloads/ResourceProviderCodeGenerators.zip
ResXFileCodeGeneratorEx http://dmytro.kryvko.googlepages.com
Solutions To The Internal Constructor (continued)
2. Write a wrapper for the Strongly Typed Resource Class and bind to the wrapper
<UserControl.Resources> <Resources:PublicPageResources x:Name="PageResources"/></UserControl.Resources>
public class PublicPageResources{ private static PageResources resources = new PageResources(); public PageResources { get { return resources; } }}
Setting The Silverlight Culture From ASP.NET
Add the following directives to the ASP.NET Page attribute:-
Add the following script to the ASP.NET page:-
UICulture="auto" Culture="auto"
<script runat="server"> protected void Page_Load(object sender, EventArgs e) { Xaml1.InitParameters = "UICulture=" + System. Threading.Thread.CurrentThread.CurrentUICulture.Name; }</script>
Setting The Silverlight Culture From ASP.NET (continued)
Delete the InitParameters from the Silverlight control
Add a Spanish(Spain) .resx file for PageResources
Change the SupportedCultures in the .csproj file<SupportedCultures>fr-FR,es-ES</SupportedCultures>
Download On DemandCreating The Resource XAP File
Manually Copy bin\debug\AppManifest.xaml to bin\debug\fr-FR Ensure that the only AssemblyPart is:-
Create a ZIP file, fr-FR.zip, containing:- AppManifest.xaml with no path information SilverlightApplication1.resources.dll with fr-FR path
Rename fr-FR.zip to fr-FR.xap Copy fr-FR.xap to SilverlightApplication1.Web\
ClientBin
<AssemblyPart Source= "fr-FR/SilverlightApplication1.resources.dll" />
Download On DemandCreating The Resource Xap File
Automatically Download the XapResourcePackager task
from http://www.guysmithferrier.com Create an msbuild project to use the
XapResourcePackager task:-
<UsingTask TaskName="XapResourcePackager" AssemblyFile="Silverlight.Build.Tasks.dll"/>
<Target Name="BuildXap"> <XapResourcePackager XapResourceFilename="$(XapResourceFilename)" AssemblyName="$(AssemblyName)" SourceFiles="@(SourceFiles)" XapResourceFileCultureName="$(XapResourceFileCultureName)"/></Target>
Download On DemandChanging The Silverlight
Application Empty the SupportedCultures in
the .csproj file
Change the Application_Startup event to:-
<SupportedCultures></SupportedCultures>
private void Application_Startup(object sender, StartupEventArgs e){ string cultureName = e.InitParams["UICulture"]; if (!String.IsNullOrEmpty(cultureName)) { Thread.CurrentThread.CurrentUICulture = new CultureInfo(cultureName); XapResourceLoader.Load(); } else this.RootVisual = new Page();}
Download On DemandChanging The Silverlight
Application Add the following lines to the App
constructor
Add the following method to the App class
XapResourceLoader.AssemblyFilename = App.Current.GetType().Assembly.FullName.Split(',')[0];
XapResourceLoader.OpenReadCompleted += new OpenReadCompletedEventHandler( XapResourceLoader_OpenReadCompleted);
private void XapResourceLoader_OpenReadCompleted( object sender, OpenReadCompletedEventArgs e){ this.RootVisual = new Page();}
Download On DemandXapResourceLoader Class
public static event OpenReadCompletedEventHandler OpenReadCompleted;public static string AssemblyFilename { get; set; }
public static void Load(){ WebClient webClient = new WebClient(); webClient.OpenReadCompleted += new OpenReadCompletedEventHandler(InternalOpenReadCompleted); if (OpenReadCompleted != null) { webClient.OpenReadCompleted += OpenReadCompleted; }
Uri cultureUri = new Uri(String.Format("{0}.xap", Thread.CurrentThread.CurrentUICulture.Name), UriKind.Relative);
webClient.OpenReadAsync(cultureUri);}
Download On DemandXapResourceLoader Class
private static void InternalOpenReadCompleted( object sender, OpenReadCompletedEventArgs e){ if (e.Error == null && !e.Cancelled) { Stream xapStream = e.Result; Uri resourcesAssemblyUri = new Uri(GetResourcesAssemblyPath(), UriKind.Relative); StreamResourceInfo xapStreamResourceInfo = new StreamResourceInfo(xapStream, null); StreamResourceInfo resourcesAssemblyStreamResourceInfo = Application.GetResourceStream( xapStreamResourceInfo, resourcesAssemblyUri); if (resourcesAssemblyStreamResourceInfo != null) { AssemblyPart assemblyPart = new AssemblyPart(); assemblyPart.Load(resourcesAssemblyStreamResourceInfo.Stream); } }}
Download On DemandXapResourceLoader Class
private static string GetResourcesAssemblyPath(){ // e.g. "fr-FR/SilverlightApplication1.resources.dll" return Thread.CurrentThread.CurrentUICulture.Name + "/" + AssemblyFilename + ".resources.dll";}
Silverlight Installation User Experience
The Silverlight install image is retrieved from http://go.microsoft.com/fwlink/?LinkId=108181 This reads the browser's HTTP accept language
setting and returns an image for that culture English
French
Spanish
Japanese
Silverlight Installation User Experience
(continued) The Silverlight add-in is installed using a
Windows application The Windows application uses the operating
system's culture (e.g. en-US) to determine the localized resources to display
If you set your browser's language to a different language to your operating you will see a schizophrenic installation experience
Silverlight Installation Testing Tip
To save from having to uninstall and reinstall Silverlight to test the installation experience disable Silverlight:- In Internet Explorer select Tools | Manage Add-
ons | Enable or Disable Add-ons Select Microsoft Silverlight and click on the
Disable radio button
Silverlight Runtime Culture Support
Silverlight is a single runtime that includes all supported languages:- English (en-US) French (fr) German (de) Italian (it) Japanese (jp) Korean (ko) Spanish (es) Chinese Simplified (zh-Hans) Chinese Traditional (zh-Hant)
Silverlight Configuration Dialog
The Config dialog's UI is dictated by the operating system (only)
Defaults to English
Silverlight Message & File Dialogs(MessageBox, OpenFileDialog, SaveFileDialog)
These dialogs' UIs are dictated by the operating system (only)
Silverlight Globalized Controls
The DatePicker and Calendar controls respect the Thread.CurrentCulture setting
en-GB fr-FR nb-NO ja-JP
Localization And Globalization Sources Summary
UI Element Origin Controlled By
.aspx/.ascx pages/controls
Your application CurrentUICulture(& CurrentCulture)
Silverlight install image Microsoft's server Browser's HTTP ACCEPT LANGUAGE
Silverlight install dialogs
Silverlight install package
OS's UI Culture
Silverlight application Your application CurrentUICulture(& CurrentCulture)
Silverlight config dialog Silverlight runtime OS's UI Culture
Silverlight file dialogs Client's operating system
OS's UI Culture
Silverlight controls Silverlight runtime CurrentUICulture(& CurrentCulture)
Silverlight Fonts
Silverlight supports 9 fonts on all platforms
Font Fallback
If you use a font that is not in the list Silverlight asks the operating system for the font
The operating system uses Font Fallback to find the best match for unknown fonts Lucida Sans Unicode on Windows Vista
Ultimate
Lucida Sans Unicode on Windows XP SP2 (East Asian support not installed)
Silverlight Default Font
Silverlight uses the "Portable User Interface" font by default
The "Portable User Interface" font is an alias for Lucida Grande
Using Non-Silverlight Fonts
Create a new Silverlight project Add a folder called Fonts Add a font, Oz.ttf, to the Fonts folder
Ensure that Build Action is set to Resource Add a button to Page.xaml:-
<Button FontFamily="Fonts/Oz.ttf#Australian" Content="Hello World"/>
Downloading Fonts Asynchronously
Delete the FontFamily from the Button control Select Oz.ttf in the Fonts folder, set Build
Action to None Create a Fonts folder in the web project and
add the font file to the folder Add a click event with the following code:-private void Button_Click(object sender, RoutedEventArgs e){ WebClient webClient = new WebClient(); webClient.OpenReadCompleted += new OpenReadCompletedEventHandler(OpenReadCompleted); webClient.OpenReadAsync( new Uri("/Fonts/Oz.ttf", UriKind.Relative));}
Downloading Fonts Asynchronously
(continued) Add a TextBlock to the page Add the following code:-
Only TextBlock and TextBox have a FontSource property
private void OpenReadCompleted(object sender, OpenReadCompletedEventArgs e){ this.TextBlock1.FontSource = new FontSource(e.Result); this.TextBlock1.FontFamily = new FontFamily("Australian"); this.TextBlock1.Text = "The font has landed";}
Font Subsetting
Open a Silverlight project in Blend 2 or above
Add a font Tools | Font Manager Select Mongolian Baiti
(349K) Check the "Auto fill"
checkbox Add a TextBlock, set the
Font Family to the new font and set the Content to "Hello World"
Build the project
Font Subsetting (continued)
Open the subsetted font (obj\Debug\Fonts\monbaiti0.subset.ttf, 101K)
Font SubsettingMSBuild Tasks
When you add a Font to your project Blend does the following:- Adds "SubsetFont.targets" to the project root
folder Adds "SubsetFontTask.dll" to the project root
folder Adds the following line to the .csproj file:-<Import Project="SubsetFont.targets" />
Silverlight Globalization Namespace
Comparison between the .NET Framework and the Silverlight Framework:- http://www.guysmithferrier.com/
downloads/SilverlightGlobalizationClassComparison.pdf
No support for locale IDs (LCIDs) No lunisolar calendars No Windows-specific features
CultureInfo.ThreeLetterWindowsLanguageName
RegionInfo.GeoId No code page support (Unicode only)
Custom Cultures
The CultureAndRegionInfoBuilder class is not supported in Silverlight
Silverlight does read custom cultures if they are present on the client's operating system
Right To Left Support
No built in support in Silverlight 2 and 3 FrameworkElement.FlowDirection is not
supported Expected for Silverlight 4 Almost all workarounds require the creation of
a separate RTL version of the Silverlight application
Silverlight 2.0 Hebrew & Arabic Language Support
A library of Silverlight RTL controls http://
silverlightrtl.codeplex.com
Other WPF I18N Features Not Available In Silverlight
Static markup extension is not supported Silverlight's MarkupExtension is not public
See "Differences Between WPF and Silverlight" http://wpfslguidance.codeplex.com
Silverlight On Windows vs. Silverlight On Mac OS X
Culture data varies across operating systems
Sort, casing and comparison results vary across operating systems
Summary
Localization is possible through .resx files Bind to the strongly typed resource classes
If you have more than one culture you should consider "download on demand"
Avoiding a schizophrenic user interface can be a challenge
Globalization support is dependent upon the operating system