WPF應用程序本地化的最佳方法分享
應用程序本地化有很多種方式,選擇合適的才是最好的。這里只討論一種方式,動態(tài)資源(DynamicResource) 這種方式可是在不重啟應用程序的情況下進行資源的切換,不論是語言切換,還是更上層的主題切換。想要運行時切換不同的資源就必須使用 動態(tài)資源(DynamicResource) 這種方式。
圖片是可以使用資源字典進行動態(tài) binding 的 不要被誤導了
資源文件
確保不同語言環(huán)境中資源 Key 是同一個,且對用的資源類型是相同的。
在資源比較多的情況下,可以通過格式化的命名方式來規(guī)避 Key 沖突。
資源文件的屬性可以設置成:
生成操作:內容
復制到輸出目錄:如果較新則復制
上面的操作可以在不重新編譯程序的情況下編輯語言資源并應用。
如果不想讓別人更改語言資源則
生成操作:頁
復制到輸出目錄:不復制
英文資源文件 en-US.xaml
<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="WindowsTitle">MainWindow</sys:String>
<ImageSource x:Key="icon">pack://SiteOfOrigin:,,,/Icons/en-USicon.png</ImageSource>
<sys:String x:Key="LanguageButton">LanguageButton</sys:String>
<sys:String x:Key="LockTime-OneMinute">1 Minute</sys:String>
<sys:String x:Key="LockTime-FiveMinute">5 Minute</sys:String>
<sys:String x:Key="LockTime-TenMinute">10 Minute</sys:String>
<sys:String x:Key="LockTime-FifteenMinute">15 Minute</sys:String>
<sys:String x:Key="LockTime-ThirtyMinute">30 Minute</sys:String>
<sys:String x:Key="LockTime-OneHour">1 Hour</sys:String>
<sys:String x:Key="LockTime-TwoHour">2 Hour</sys:String>
<sys:String x:Key="LockTime-ThreeHour">3 Hour</sys:String>
<sys:String x:Key="LockTime-Never">Never</sys:String>
</ResourceDictionary>中文資源文件 zh-CN.xaml
<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="WindowsTitle">主窗口</sys:String>
<ImageSource x:Key="icon">pack://SiteOfOrigin:,,,/Icons/zh-CNicon.png</ImageSource>
<sys:String x:Key="LanguageButton">語言按鈕</sys:String>
<sys:String x:Key="LockTime-OneMinute">1 分鐘</sys:String>
<sys:String x:Key="LockTime-FiveMinute">5 分鐘</sys:String>
<sys:String x:Key="LockTime-TenMinute">10 分鐘</sys:String>
<sys:String x:Key="LockTime-FifteenMinute">15 分鐘</sys:String>
<sys:String x:Key="LockTime-ThirtyMinute">30 分鐘</sys:String>
<sys:String x:Key="LockTime-OneHour">1 小時</sys:String>
<sys:String x:Key="LockTime-TwoHour">2 小時</sys:String>
<sys:String x:Key="LockTime-ThreeHour">3 小時</sys:String>
<sys:String x:Key="LockTime-Never">永不</sys:String>
</ResourceDictionary>資源使用
App.xaml
設置默認語言資源,用于在設計階段預覽,并利用 VS 智能提示資源的 Key 防止 Key編寫出錯。
<Application
x:Class="Localization.Core.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Localization.Core"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/I18nResources/en-US.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>主界面布局
<Window
x:Class="Localization.Core.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Localization.Core"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:operate="clr-namespace:Localization.Core.Operates"
Title="{DynamicResource WindowsTitle}"
Width="800"
Height="450"
Icon="{DynamicResource icon}"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<ComboBox
x:Name="comLan"
Width="{Binding SizeToContent.Width}"
Height="{Binding SizeToContent.Width}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
SelectedValuePath="Tag"
SelectionChanged="ComboBox_SelectionChanged">
<ComboBoxItem Content="中文" Tag="zh-CN" />
<ComboBoxItem Content="English" Tag="en-US" />
</ComboBox>
<StackPanel Grid.Row="1">
<Button
Margin="0,50,0,0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="{DynamicResource LanguageButton}" />
<ComboBox
x:Name="cmTime"
Width="120"
Margin="20"
VerticalAlignment="Center">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{operate:ResourceBinding Key}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
</Grid>
</Window>cs代碼
public partial class MainWindow : Window
{
ObservableCollection<KeyValuePair<string, int>>? TimeList { get; set; }
public MainWindow()
{
InitializeComponent();
var lan = Thread.CurrentThread.CurrentCulture.Name;
comLan.SelectedValue = lan;
Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
TimeList = new ObservableCollection<KeyValuePair<string, int>>()
{
new KeyValuePair<string, int>("LockTime-OneMinute", 1),
new KeyValuePair<string, int>("LockTime-FiveMinute", 5),
new KeyValuePair<string, int>("LockTime-TenMinute", 10),
new KeyValuePair<string, int>("LockTime-FifteenMinute", 15),
new KeyValuePair<string, int>("LockTime-ThirtyMinute", 30),
new KeyValuePair<string, int>("LockTime-OneHour", 60),
new KeyValuePair<string, int>("LockTime-TwoHour", 120),
new KeyValuePair<string, int>("LockTime-ThreeHour", 180),
new KeyValuePair<string, int>("LockTime-Never", 0),
};
cmTime.ItemsSource = TimeList;
cmTime.SelectedValue = TimeList[0];
}
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.AddedItems is null) return;
LanguageOperate.SetLanguage((e.AddedItems[0] as ComboBoxItem)!.Tag.ToString()!);
}
}App.config
language 配置項用于保存用戶選擇的語言類型
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="language" value=""/>
</appSettings>
</configuration>輔助類
語言切換操作類
檢查方法入參,有值則切換到指定的資源,無值則讀取配置文件中的值,若配置文件中仍無值,則獲取當前線程運行的語言環(huán)境,切換到與語言環(huán)境相匹配的資源,如果沒有找到與之匹配的資源則不做操作。
切換資源之后,將配置文件中的 language 的 value 改為當前所選的語言保存并刷新配置文件,直到下次更改。
重寫 Application 類的 OnStartup 方法,在 OnStartup 中調用 SetLanguage 方法以實現應用程序啟動對語言環(huán)境的判別。
/// <summary>
/// 語言操作
/// </summary>
public class LanguageOperate
{
private const string KEY_OF_LANGUAGE = "language";
/// <summary>
/// 語言本地化
/// </summary>
/// <param name="language">語言環(huán)境</param>
public static void SetLanguage(string language = "")
{
if (string.IsNullOrWhiteSpace(language))
{
language = ConfigurationManager.AppSettings[KEY_OF_LANGUAGE]!;
if (string.IsNullOrWhiteSpace(language))
language = System.Globalization.CultureInfo.CurrentCulture.ToString();
}
string languagePath = $@"I18nResources\{language}.xaml";
if (!System.IO.File.Exists(languagePath)) return;
var lanRd = Application.LoadComponent(new Uri(languagePath, UriKind.RelativeOrAbsolute)) as ResourceDictionary;
var old = Application.Current.Resources.MergedDictionaries.FirstOrDefault(o => o.Contains("WindowsTitle"));
if (old != null)
Application.Current.Resources.MergedDictionaries.Remove(old);
Application.Current.Resources.MergedDictionaries.Add(lanRd);
Configuration configuration = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
configuration.AppSettings.Settings[KEY_OF_LANGUAGE].Value = language;
configuration.Save();
ConfigurationManager.RefreshSection("AppSettings");
var culture = new System.Globalization.CultureInfo(language);
System.Globalization.CultureInfo.CurrentCulture = culture;
System.Globalization.CultureInfo.CurrentUICulture = culture;
}
}資源 binding 解析類
ComBox 控件通常是通過 ItemSource 進行綁定,默認情況下是無法對綁定的資源進行翻譯的。
通過繼承 MarkupExtension 類 重寫 ProvideValue 方法來實現,item 綁定 資源的 Key 的解析。
/// <summary>
/// markup extension to allow binding to resourceKey in general case
/// </summary>
public class ResourceBinding : MarkupExtension
{
#region properties
private static DependencyObject resourceBindingKey;
public static DependencyObject ResourceBindingKey
{
get => resourceBindingKey;
set => resourceBindingKey = value;
}
// Using a DependencyProperty as the backing store for ResourceBindingKeyHelper. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ResourceBindingKeyHelperProperty =
DependencyProperty.RegisterAttached(nameof(ResourceBindingKey),
typeof(object),
typeof(ResourceBinding),
new PropertyMetadata(null, ResourceKeyChanged));
static void ResourceKeyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(d is FrameworkElement target) || !(e.NewValue is Tuple<object, DependencyProperty> newVal)) return;
var dp = newVal.Item2;
if (newVal.Item1 == null)
{
target.SetValue(dp, dp.GetMetadata(target).DefaultValue);
return;
}
target.SetResourceReference(dp, newVal.Item1);
}
#endregion
public ResourceBinding() { }
public ResourceBinding(string path) => Path = new PropertyPath(path);
public override object ProvideValue(IServiceProvider serviceProvider)
{
if ((IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget)) == null) return null;
if (((IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget))).TargetObject != null && ((IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget))).TargetObject.GetType().FullName is "System.Windows.SharedDp") return this;
if (!(((IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget))).TargetObject is FrameworkElement targetObject) || !(((IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget))).TargetProperty is DependencyProperty targetProperty)) return null;
#region binding
Binding binding = new Binding
{
Path = Path,
XPath = XPath,
Mode = Mode,
UpdateSourceTrigger = UpdateSourceTrigger,
Converter = Converter,
ConverterParameter = ConverterParameter,
ConverterCulture = ConverterCulture,
FallbackValue = FallbackValue
};
if (RelativeSource != null)
binding.RelativeSource = RelativeSource;
if (ElementName != null)
binding.ElementName = ElementName;
if (Source != null)
binding.Source = Source;
#endregion
var multiBinding = new MultiBinding
{
Converter = HelperConverter.Current,
ConverterParameter = targetProperty
};
multiBinding.Bindings.Add(binding);
multiBinding.NotifyOnSourceUpdated = true;
targetObject.SetBinding(ResourceBindingKeyHelperProperty, multiBinding);
return null;
}
#region Binding Members
/// <summary>
/// The source path (for CLR bindings).
/// </summary>
public object Source { get; set; }
/// <summary>
/// The source path (for CLR bindings).
/// </summary>
public PropertyPath Path { get; set; }
/// <summary>
/// The XPath path (for XML bindings).
/// </summary>
[DefaultValue(null)]
public string XPath { get; set; }
/// <summary>
/// Binding mode
/// </summary>
[DefaultValue(BindingMode.Default)]
public BindingMode Mode { get; set; }
/// <summary>
/// Update type
/// </summary>
[DefaultValue(UpdateSourceTrigger.Default)]
public UpdateSourceTrigger UpdateSourceTrigger { get; set; }
/// <summary>
/// The Converter to apply
/// </summary>
[DefaultValue(null)]
public IValueConverter Converter { get; set; }
/// <summary>
/// The parameter to pass to converter.
/// </summary>
/// <value></value>
[DefaultValue(null)]
public object ConverterParameter { get; set; }
/// <summary>
/// Culture in which to evaluate the converter
/// </summary>
[DefaultValue(null)]
[TypeConverter(typeof(System.Windows.CultureInfoIetfLanguageTagConverter))]
public CultureInfo ConverterCulture { get; set; }
/// <summary>
/// Description of the object to use as the source, relative to the target element.
/// </summary>
[DefaultValue(null)]
public RelativeSource RelativeSource { get; set; }
/// <summary>
/// Name of the element to use as the source
/// </summary>
[DefaultValue(null)]
public string ElementName { get; set; }
#endregion
#region BindingBase Members
/// <summary>
/// Value to use when source cannot provide a value
/// </summary>
/// <remarks>
/// Initialized to DependencyProperty.UnsetValue; if FallbackValue is not set, BindingExpression
/// will return target property's default when Binding cannot get a real value.
/// </remarks>
public object FallbackValue { get; set; }
#endregion
#region Nested types
private class HelperConverter : IMultiValueConverter
{
public static readonly HelperConverter Current = new HelperConverter();
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return Tuple.Create(values[0], (DependencyProperty)parameter);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
#endregion
}實現效果

到此這篇關于WPF應用程序本地化的最佳方法分享的文章就介紹到這了,更多相關WPF本地化內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
C#中的Task.WhenAll和Task.WhenAny方法介紹
這篇文章介紹了C#中的Task.WhenAll和Task.WhenAny方法,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-04-04
C#使用FileStream循環(huán)讀取大文件數據的方法示例
這篇文章主要介紹了C#使用FileStream循環(huán)讀取大文件數據的方法,結合實例形式分析了FileStream文件流的形式循環(huán)讀取大文件的相關操作技巧,需要的朋友可以參考下2017-05-05
C#實現JWT無狀態(tài)驗證的實戰(zhàn)應用解析
這篇文章主要介紹了C#實現JWT無狀態(tài)驗證的實戰(zhàn)應用解析,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-03-03

