Using the LocBaml sample application that Microsoft provides with the Windows SDK for Windows Presentation Foundation can be very frustrating. By default it seems to pick up a lot of things that clutter the resulting CSV file. If your application contains a lot of image resources, those will get duplicated in the localized resource dlls as well, increasing the space that your application occupies on the hard drive. And finally, getting an XBAP application to be deployable after localization with LocBaml requires opening up the application manifest and adding all the localized resource files to it. Otherwise a deployment error will bite you.
So with all those problems, we decided to take a different approach for our WPF XBAP applications. We put all our strings into a standalone XAML file, producing a ResourceDictionary. This way our application can pick up strings using Text=”{StaticResource strXYZ}” for XAML markup and/or use Application.Current.FindResource(“strXYZ”) for codebehind. We name the file “Strings_en-US.xaml”. We can localize this file nicely, and name the resulting set appropriately: “Strings_de-DE.xaml”, “Strings_fr-FR.xaml” and so on. These files can then be included as resources in the application. They can also be added as “loose” XAML files, so we can add languages or make corrections after compilation.
The string resource file looks something like this:
<?xml version="1.0" encoding="utf-8" ?>
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
>
<sys:String x:Key="strButtonOK">OK</sys>
<sys:String x:Key="strButtonCancel">Cancel</sys>
</ResourceDictionary>
At application load time, we look at the CurrentCulture and attempt to load the ResourceDictionary object like this:
ResourceDictionary rd = null;
System.Globalization.CultureInfo ci =
System.Globalization.CultureInfo.CurrentCulture;
string langCountry = ci.Name;
string languageFile =
string.Format("/Resources/Strings/Strings_{0}.xaml", langCountry);
try
{
rd = (ResourceDictionary)Application.LoadComponent(
new Uri(languageFile, UriKind.RelativeOrAbsolute)
);
}
You can imagine this with some catch {} fallbacks for when a specific language-country combination doesn’t exist. We’ll fall back to a file that’s only language specific, and if that doesn’t work out, we’ll fall back to en-US.
We’re not using CurrentUICulture, since that seems to be tied to the UI language of Vista, and we want our apps to follow what the user sets in Control Panel. CurrentCulture seems to work for that.
To load the strings into the global resource dictionary, we just do
Application.Current.Resources.MergedDictionaries.Add(rd);
To load loose XAML files that are not part of the application’s resources, we do something like this:
ResourceDictionary rd1 = null;
string languageFileLoose = string.Format(
"pack://siteoforigin:,,,/Resources/Strings/Strings_{0}.xaml",
langCountry
);
Uri uri = new Uri(languageFileLoose, UriKind.Absolute);
System.Windows.Resources.StreamResourceInfo info;
System.Windows.Markup.XamlReader reader;
try {
info = Application.GetRemoteStream(uri);
reader = new System.Windows.Markup.XamlReader();
rd1 = (ResourceDictionary)reader.LoadAsync(info.Stream);
}
Again, we extend this with catch {}Â blocks to fall back to a file that’s only language specific. We do the loose XAML loading after we’ve loaded embedded resources, so we can rely on a basic set of languages and strings, but can add languages, fix string translation errors or make improvements in the loose XAML after compiling things.
It may work for you as well, but ymmv.
Update: Changed the first Uri to UriKind.RelativeOrAbsolute
Al
Rick
GeekTieGuy
Tim
Chad
GeekTieGuy
J3nonk
GeekTieGuy
Chris Barth