WPF 本地化的最佳做法

WPF 本地化的最佳做法

  • 资源文件
    • 英文资源文件 en-US.xaml
    • 中文资源文件 zh-CN.xaml
  • 资源使用
    • App.xaml
    • 主界面布局
    • cs代码
  • App.config
  • 辅助类
    • 语言切换操作类
    • 资源 binding 解析类
  • 实现效果

  应用程序本地化有很多种方式,选择合适的才是最好的。这里只讨论一种方式,动态资源(DynamicResource) 这种方式可是在不重启应用程序的情况下进行资源的切换,不论是语言切换,还是更上层的主题切换。想要运行时切换不同的资源就必须使用 动态资源(DynamicResource) 这种方式。
图片是可以使用资源字典进行动态 binding 的 不要被误导了

资源文件

确保不同语言环境中资源 Key 是同一个,且对用的资源类型是相同的。
在资源比较多的情况下,可以通过格式化的命名方式来规避 Key 冲突。

资源文件的属性可以设置成:
生成操作:内容
复制到输出目录:如果较新则复制

上面的操作可以在不重新编译程序的情况下编辑语言资源并应用。

如果不想让别人更改语言资源则
生成操作:
复制到输出目录:不复制

英文资源文件 en-US.xaml

<ResourceDictionaryxmlns="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

<ResourceDictionaryxmlns="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编写出错。

<Applicationx: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>

主界面布局

<Windowx: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><ComboBoxx: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"><ButtonMargin="0,50,0,0"HorizontalAlignment="Center"VerticalAlignment="Center"Content="{DynamicResource LanguageButton}" /><ComboBoxx: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>

辅助类

语言切换操作类

检查方法入参,有值则切换到指定的资源,无值则读取配置文件中的值,若配置文件中仍无值,则获取当前线程运行的语言环境,切换到与语言环境相匹配的资源,如果没有找到与之匹配的资源则不做操作。

切换资源之后,将配置文件中的 language 的 value 改为当前所选的语言保存并刷新配置文件,直到下次更改。

重写 Application 类的 OnStartup 方法,在 OnStartup 中调用 SetLanguage 方法以实现应用程序启动对语言环境的判别。

/// <summary>
/// 语言操作
/// </summary>
public class LanguageOperate
{private const string KEY_OF_LANGUAGE = "language";/// <summary>/// 语言本地化/// </summary>/// <param name="language">语言环境</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 propertiesprivate 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);}#endregionpublic 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 bindingBinding 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;#endregionvar 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 typesprivate 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
}

实现效果

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/91306.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

医院后勤管理用什么系统好?的修医院报修管理系统有哪些优势?

随着医院后勤工作量的不断增加&#xff0c;需要协调和维护的设备和部门也随之增多。传统的医院后勤管理方式已经显得不够优越&#xff0c;其劣势日益凸显&#xff0c;无法满足实际工作需求。因此&#xff0c;快速推动医院后勤信息化管理已成为当前医院发展的迫切需求。而的修医…

concrt140.dll丢失怎么恢复?教你5种修复方法

首先介绍一下concrt140.dll是什么 concrt140.dll是Microsoft Visual C Redistributable for Visual Studio 2015所需的一个动态链接库文件。它是用于支持C程序运行的重要组件之一。当系统中缺少或丢失concrt140.dll文件时&#xff0c;可能会导致一些程序无法正常运行。 首先&a…

BC117 小乐乐走台阶(附完整代码)

描述 小乐乐上课需要走n阶台阶&#xff0c;因为他腿比较长&#xff0c;所以每次可以选择走一阶或者走两阶&#xff0c;那么他一共有多少种走法&#xff1f; 输入描述 输入包含一个整数n (1 ≤ n ≤ 30) 输出描述 输出一个整数&#xff0c;即小乐乐可以走的方法数。 思路&a…

Ajax及前端工程化

Ajax&#xff1a;异步的js与xml。 作用&#xff1a; 1、通过ajax给服务器发送数据&#xff0c;并获得其响应的数据。 2、可以在不更新整个网页的情况下&#xff0c;与服务器交换数据并更新部分网页的技术。 一、同步与异步 二、原生Ajax 1、准备数据地址 2、创建XMLHttpReq…

时序预测 | MATLAB实现基于BP神经网络的时间序列预测-递归预测未来(多指标评价)

时序预测 | MATLAB实现基于BP神经网络的时间序列预测-递归预测未来(多指标评价) 目录 时序预测 | MATLAB实现基于BP神经网络的时间序列预测-递归预测未来(多指标评价)预测结果基本介绍程序设计参考资料 预测结果 基本介绍 Matlab实现BP神经网络时间序列预测未来&#xff08;完整…

提升物流管理效率,快递批量查询高手软件助你一臂之力

物流管理中&#xff0c;准确跟踪和掌握快递的物流信息是非常重要的。而快递批量查询高手软件的出现&#xff0c;大大提高了物流管理的效率&#xff0c;为企业带来了诸多便利。 传统的快递查询方式往往需要手动逐个输入快递单号&#xff0c;费时费力且容易出错。而有了快递批量查…

收银一体化-亿发2023智慧门店新零售营销策略,实现全渠道运营

伴随着互联网电商行业的兴起&#xff0c;以及用户理念的改变&#xff0c;大量用户从线下涌入线上&#xff0c;传统的线下门店人流量急剧收缩&#xff0c;门店升级几乎成为了每一个零售企业的发展之路。智慧门店新零售收银解决方案是针对传统零售企业面临的诸多挑战和问题&#…

混合云环境中 Kubernetes 可观测性的 6 个有效策略...

2023 年&#xff0c;原生云应用程序和平台将快速增长。组织不断努力最大限度地发挥其应用程序的潜力&#xff0c;确保无缝的用户体验并推动业务增长。 混合云环境的兴起以及 Kubernetes 等容器化技术的采用彻底改变了现代应用程序的开发、部署和扩展方式。 在这个数字领域&am…

i++和++i在操作数栈和局部变量表的分配

1、执行运算指令时&#xff0c;压入操作数栈的顺序不受运算优先级影响 2、i 先将i值压入到操作数栈&#xff0c;再在局部变量表自增 3、i 先在局部变量表自增&#xff0c;再压入到操作数栈 记忆方法&#xff1a;i的先后&#xff0c;表示压入操作数栈的先后。 看如下例子&am…

AOP与SpringAOP

AOP与SpringAOP 一、什么是AOP&#xff0c;什么是SpringAOP二、AOP与拦截器的区别三、实现SpringAOP1.添加SpringBootAOP依赖2.创建切面3.创建切点4.创建通知5.创建连接点 效果 一、什么是AOP&#xff0c;什么是SpringAOP AOP即Aspect-Oriented Programming面向切面编程。 它是…

08 - 追加commit和修改最新的commit message

查看所有文章链接&#xff1a;&#xff08;更新中&#xff09;GIT常用场景- 目录 文章目录 1. 追加提交2. 修改最新的commit message 1. 追加提交 将改动追加到上一次的commit 现在我已经修改了Readme文件并且已经add、commit操作&#xff0c;但是还没有push到远程仓库&#x…

kafka基本概念及操作

kafka介绍 Kafka是最初由Linkedin公司开发&#xff0c;是一个分布式、支持分区的&#xff08;partition&#xff09;、多副本的 &#xff08;replica&#xff09;&#xff0c;基于zookeeper协调的分布式消息系统&#xff0c;它的最大的特性就是可以实时的处理大量数据以满足各…

【脚踢数据结构】内核链表

(꒪ꇴ꒪ )&#xff0c;Hello我是祐言QAQ我的博客主页&#xff1a;C/C语言,Linux基础,ARM开发板&#xff0c;软件配置等领域博主&#x1f30d;快上&#x1f698;&#xff0c;一起学习&#xff0c;让我们成为一个强大的攻城狮&#xff01;送给自己和读者的一句鸡汤&#x1f914;&…

ide internal errors【bug】

ide internal errors【bug】 前言版权ide internal errors错误产生相关资源解决1解决2 设置虚拟内存最后 前言 2023-8-15 12:36:59 以下内容源自《【bug】》 仅供学习交流使用 版权 禁止其他平台发布时删除以下此话 本文首次发布于CSDN平台 作者是CSDN日星月云 博客主页是h…

8.15号经典模型复习笔记

文章目录 Deep Residual Learning for Image Recognition(CVPR2016)方法 Densely Connected Convolutional Networks&#xff08;CVPR2017&#xff09;方法 EfficientNet: Rethinking Model Scaling for Convolutional Neural Networks&#xff08;ICML2019&#xff09;方法 Re…

深入了解 Rancher Desktop 设置

Rancher Desktop 设置的全面概述 Rancher Desktop 拥有方便、强大的功能&#xff0c;是最佳的开发者工具之一&#xff0c;也是在本地构建和部署 Kubernetes 的最快捷方式。 本文将介绍 Rancher Desktop 的功能和特性&#xff0c;以及 Rancher Desktop 作为容器管理平台和本地…

KU Leuven TU Berlin 推出“RobBERT”,一款荷兰索塔 BERT

荷兰语是大约24万人的第一语言&#xff0c;也是近5万人的第二语言&#xff0c;是继英语和德语之后第三大日耳曼语言。来自比利时鲁汶大学和柏林工业大学的一组研究人员最近推出了基于荷兰RoBERTa的语言模型RobBERT。 谷歌的BERT&#xff08;来自Transformers的B idirectional …

vant van-tabs van-pull-refresh van-list 标签栏+上拉加载+下拉刷新

<template><div class"huibj"><div class"listtab"><!--顶部导航--><div class"topdh"><topnav topname"余额明细"></topnav></div><!--Tab 标签--><van-tabs v-model"…

linux 查看文件被那个进程所调用

使用lsof 命令 显示文件被哪个进程所占用 lsof /var/log/messagesCOMMAND&#xff1a;进程的名称PID&#xff1a;进程标识符USER&#xff1a;进程所有者FD&#xff1a;文件描述符&#xff0c;应用程序通过文件描述符识别该文件。如cwd、txt等TYPE&#xff1a;文件类型&#…

设计HTML5文档结构

定义清晰、一致的文档结构不仅方便后期维护和拓展&#xff0c;同时也大大降低了CSS和JavaScript的应用难度。为了提高搜索引擎的检索率&#xff0c;适应智能化处理&#xff0c;设计符合语义的结构显得很重要。 1、头部结构 在HTML文档的头部区域&#xff0c;存储着各种网页元…