Can you recommend a good way to implement a Multilanguage system for a WPF app? The method I'm using right now involves XML, classes and a xaml extension. It Works fine in most of cases, but when I have to deal with dynamic labels or dynamic text in general it require some extra effort. I would like to let the programmer working only in the main problem and forgot the lang issues.
问题:
回答1:
I am using the WPF Localization Extension. It is a really easy way to localize any type of DependencyProperty
on DependencyObject
s.
- is in a real stable state
- supports binding-like writing style like
Text = {LocText ResAssembly:ResFile:ResKey}
- works with the .resx-fallback mechanism (e.g. en-us -> en -> independent culture)
- supports culture forcing (e.g. "this has to be English all the time")
- works with normal dependency properties
- works with control templates
- can be used in XAML (really :P) without any additional namespaces
- can be used in code behind to bind localized values to dynamic generated controls
- implements
INotifyPropertyChanged
for advanced use - supports string formatting e.g.
"this is the '{0}' value"
- supports prefix and suffix values (currently with
LocText
extension) - is in use in productive systems (like my public relation product)
- switching of the language to runtime affects NO timeslice
- can be used with any resource file (
.resx
) across all assemblies (also the dynamic loaded one at runtime) - doesn't need any initializing process (like "call xyz to register a special localize dictionary")
- is available at design-time (MS Expression Blend, MS Visual Studio 2008 (Normal and SP1)
- change of the chosen language is possible at design-time
- can localize any type of data type, as long as a converter (
TypeConverter
) for it exists (extendsLocalizeExtension
) - has built in support for
Text
, upperText
, lowerText
,Image
s,Brush
es,Double
andThickness
- doesn't affects any memory leaks
- leaves the
UID
property untouched - offers a
SpecificCulture
to use asIFormatProvider
(e.g.(123.20).ToString(LocalizeDictionary.SpecificCulture) = "123.20"
or"123,20"
) - offers some functionality to check and get resource values in code behind
- doesn't alter the culture on
Thread.CurrentCulture
orThread.CurrentUICulture
(can be changed easily)
回答2:
Follow these steps:
1) Place all String
fragments in a separate resource file.
Example: StringResources.xaml
:
<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="All_Vehicles">All Vehicles</system:String>
</ResourceDictionary>
2) Make copies for each language and add them (translated) to the merged dictionaries. Don't forget to add the country's ISO code to make things easier.
Example App.xaml
:
<Application x:Class="WpfStringTables.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="Window1.xaml">
<Application.Resources>
<ResourceDictionary >
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="StringResources.de-DE.xaml" />
<ResourceDictionary Source="StringResources.nl-NL.xaml" />
<ResourceDictionary Source="StringResources.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
The last resource file with strings will be used to replace text parts in code.
3a) Use the text parts from the String
table:
Example Window1.xaml
:
<Window x:Class="WpfStringTables.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<Button Margin="51,82,108,129" Name="AllVehiclesButton"
Content="{StaticResource All_Vehicles}"/>
</Grid>
</Window>
3b) Load the resource from code (Only use this code if you don't want to set via XAML
):
void PageLoad()
{
string str = FindResource("All_Vehicles").ToString();
}
4) Switch to new culture at start of application:
Codesnippet from App.xaml.cs
:
public static void SelectCulture(string culture)
{
if (String.IsNullOrEmpty(culture))
return;
//Copy all MergedDictionarys into a auxiliar list.
var dictionaryList = Application.Current.Resources.MergedDictionaries.ToList();
//Search for the specified culture.
string requestedCulture = string.Format("StringResources.{0}.xaml", culture);
var resourceDictionary = dictionaryList.
FirstOrDefault(d => d.Source.OriginalString == requestedCulture);
if (resourceDictionary == null)
{
//If not found, select our default language.
requestedCulture = "StringResources.xaml";
resourceDictionary = dictionaryList.
FirstOrDefault(d => d.Source.OriginalString == requestedCulture);
}
//If we have the requested resource, remove it from the list and place at the end.
//Then this language will be our string table to use.
if (resourceDictionary != null)
{
Application.Current.Resources.MergedDictionaries.Remove(resourceDictionary);
Application.Current.Resources.MergedDictionaries.Add(resourceDictionary);
}
//Inform the threads of the new culture.
Thread.CurrentThread.CurrentCulture = new CultureInfo(culture);
Thread.CurrentThread.CurrentUICulture = new CultureInfo(culture);
}
回答3:
Josh Smith wrote an in-depth tutorial about his preferred method for this: Creating an Internationalized Wizard in WPF.
It might point you towards a big redesign (it's a MVVM solution), but using MVVM seems worth it for other reasons as well.
回答4:
Using this article I've managed to easily use resource files to handle multilingual WPF windows. http://www.codeproject.com/KB/WPF/WPF_Resx_Localization.aspx You should give it a check because it's really simple and effective.