|
|||||||||||||||||||||
|
|||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
Update: This article has been rewritten after a helpful comment by Kingsley Moore below made the solution much, much simpler and easier to add to an application. ContentsIntroductionThis article is yet another attempt to address the problem of localizing WPF applications in a simple and maintainable fashion.
The example application has a few localized The original implementation presented in this article required a wrapper class around the set of properties, which was a hassle to either manually or auto-generate. This update removes the need for this wrapper, making this much easier to use. BackgroundThere have been a number of other articles on this subject, including Localizing WPF Applications using Locbaml which covered different methods of localizing XAML files, each with their own advantages and disadvantages. The first approach in that article (Targeted Localization Without Using LocBaml.exe) got me thinking, but I had to diverge from that approach in order to trigger runtime auto-updating (for all elements, even on existing Windows). Another earlier project is WPF Multi-Lingual at Runtime, which does provide a solution for runtime auto-updating, but to my mind introduces complexity in the management of the language resource files. By maintaining support for existing *.resx files in this article, we can still use existing applications to easily translate applications to new I thoroughly recommend reading and understanding both these articles, there is a lot of information there, as well as useful tricks that have helped me a lot in other projects. Using the CodeAutomatic Updating with ObjectDataProviderIn order to get automatic updating when we change the current "This class is also useful when you want to replace your current binding source object with another object and have all the associated bindings updated." So all we need to do is replace (or refresh) the Here is where this solution has been improved. While it is possible to directly bind to the properties of the auto-generated RESX designer class (Resources.Designer.cs), we also need to get an instance of this class for the ODP. All <ObjectDataProvider x:Key="Resources"
ObjectType="{x:Type cultures:CultureResources}" MethodName="GetResourceInstance"/>
... with the method: public Properties.Resources GetResourceInstance()
{
return new Properties.Resources();
}
Use of One constraint for this to work is that the public static void ChangeCulture(CultureInfo culture)
{
//remain on the current culture if the desired culture cannot be found
// - otherwise it would revert to the default resources set,
// which may or may not be desired.
if (pSupportedCultures.Contains(culture))
{
Properties.Resources.Culture = culture;
ResourceProvider.Refresh();
}
else
Debug.WriteLine("Culture [{0}] not available", culture);
}
Updating the current Design-time SupportAt design-time, Adding Localized StringsAll We can then add a <Label x:Name="labelCultureName"
Content="{Binding Path=LabelCultureName, Source={StaticResource Resources}}"/>
And if we have added this resource If you see the case that a Adding More CulturesA simple way to add another Enumerating Available CulturesWith a number of Debug.WriteLine("Get Installed cultures:");
CultureInfo tCulture = new CultureInfo("");
foreach (string dir in Directory.GetDirectories(Application.StartupPath))
{
try
{
//see if this directory corresponds to a valid culture name
DirectoryInfo dirinfo = new DirectoryInfo(dir);
tCulture = CultureInfo.GetCultureInfo(dirinfo.Name);
//determine if a resources DLL exists in this directory that
//matches the executable name
if (dirinfo.GetFiles(Path.GetFileNameWithoutExtension
(Application.ExecutablePath) + ".resources.dll").Length > 0)
{
pSupportedCultures.Add(tCulture);
Debug.WriteLine(string.Format(" Found Culture: {0} [{1}]",
tCulture.DisplayName, tCulture.Name));
}
}
//ignore any ArgumentExceptions generated for non-culture
//directories in the bin folder
catch { }
}
The above is a relatively quick way of checking the applications bin directory for any folders that match Design-Time Solution for UserControlsA problem arose for me in the veiled form of the <UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="WPFLocalize.UserControl2"
x:Name="UserControl">
<Grid x:Name="LayoutRoot">
<Label x:Name="labelUserControl2"
Content="{Binding Path=LabelCultureName,
Source={StaticResource Resources}}"/>
</Grid>
</UserControl>
When you add a I understand that the problem when loading a public UserControl2()
{
#if DEBUG
//only perform the following fix if we are in the designer
// - the default ctor is not executed when editing the usercontrol,
// but is executed when usercontrol has been added to a window/page
// NB. The Visual Studio designer might return null for Application.Current
// http://msdn.microsoft.com/en-us/library/bb546934.aspx
if (DesignerProperties.GetIsInDesignMode(this) && Application.Current != null)
{
Uri resourceLocater =
new System.Uri("/WPFLocalize;component/ResourceDictionary1.xaml",
UriKind.Relative);
ResourceDictionary dictionary =
(ResourceDictionary)Application.LoadComponent(resourceLocater);
//add the resourcedictionary containing our Resources ODP to
//App.Current (which is the Designer / Blend)
if (!Application.Current.Resources.MergedDictionaries.Contains(dictionary))
Application.Current.Resources.MergedDictionaries.Add(dictionary);
}
#endif
this.InitializeComponent();
}
Using the LimitationsIn this example I am using WPF ResourceCultureProvider.DataChanged +=
new EventHandler(ResourceCultureProvider_DataChanged);
void ResourceCultureProvider_DataChanged(object sender, EventArgs e)
{
Debug.WriteLine
("ObjectDataProvider.DataChanged event. New culture [{0}]",
CurrentResourceCulture.LabelCultureName));
}
History
| ||||||||||||||||||||