【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
}
实现效果

相关文章:
【WPF】 本地化的最佳做法
【WPF】 本地化的最佳做法 资源文件英文资源文件 en-US.xaml中文资源文件 zh-CN.xaml 资源使用App.xaml主界面布局cs代码 App.config辅助类语言切换操作类资源 binding 解析类 实现效果 应用程序本地化有很多种方式,选择合适的才是最好的。这里只讨论一种方式&#…...
VS2017编译同事VS2022创建的库,报错无法运行rc.exe
项目场景: 项目场景:有一个qt的版本,迁移到VS中进行开发编译,同事使用的是VS2022已经编译成功。但是拷贝到我电脑,我使用VS2017打开编译,却报错:报错无法运行rc.exe。 原因分析: 1.…...
Leetcode49. 字母异位词分组
给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。 字母异位词 是由重新排列源单词的所有字母得到的一个新单词。 解题思路:计数 思路:题意是字符串的字符都是小写,可以对每个字符串统计其中字符的…...
设计模式之工厂方法模式(FactoryMethod)
一、概述 定义一个用于创建对象的接口,让子类决定实例化哪一个类。FactoryMethod使一个类的实例化延迟到其子类。 二、适用性 1.当一个类不知道它所必须创建的对象的类的时候。 2.当一个类希望由它的子类来指定它所创建的对象的时候。 3.当类将创建对象的职责委…...
python的进度条tqdm库,显示时进度条不换行设置
问题描述 tqdm库,显示时进度条不要换行,直接在一行里显示当前进度 换行显示: 不换行显示 解决方案(参考链接) 添加参数position0 和 leaveTrue 举例: 修改前: import tqdm import time f…...
spark的使用
spark的使用 spark是一款分布式的计算框架,用于调度成百上千的服务器集群。 安装pyspark # os.environ[PYSPARK_PYTHON]解析器路径 pyspark_python配置解析器路径 import os os.environ[PYSPARK_PYTHON]"D:/dev/python/python3.11.4/python.exe"pip inst…...
力扣:66. 加一(Python3)
题目: 给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。 最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。 你可以假设除了整数 0 之外,这个整数不会以零开头。 来源:力扣…...
Go的标准库Context理解
作为一个才入门的菜鸟,还没写过真正的 go 项目,要理解这个 Context 还是有点难,不过还是要尝试一下。在 Go http包的Server中,每一个请求在都有一个对应的 goroutine 去处理。请求处理函数通常会启动额外的 goroutine 用来访问后端…...
Nuxt3_1_路由+页面+组件+资源+样式 使用及实例
1、 简介 1.1 开发必备 node版本 v16.10.0 我使用的是16.14.0编辑器推荐使用Volar Extension 的VS code插件Terminal 运行nuxt指令 1.2 环境搭建 安装项目: npx nuxilatest init [first_nuxt3]进入项目目录: cd [first_nuxt3]安装依赖:n…...
Kafka第三课
Flume 由三部分 Source Channel Sink 可以通过配置拦截器和Channel选择器,来实现对数据的分流, 可以通过对channel的2个存储容量的的设置,来实现对流速的控制 Kafka 同样由三大部分组成 生产者 服务器 消费者 生产者负责发送数据给服务器 服务器存储数据 消费者通过从服务器取…...
elasticsearch修改es集群的索引副本数量
前言 最近es集群进行调整,从2节点变成了单节点。所以需要将集群模式改为单点模式,并需要将es 集群的全部索引副本个数改为0,不然会有很多未分配的分片,导致集群状态为 yellow。 具体实践 1. 先将现有的index的副本数量为0个 此…...
【SpringCloud】Ribbon定制化配置
文章目录 使用Ribbon自带负载均衡算法添加负载均衡算法ConfigurationRestTemplate使用上面负载均衡算法 自定义负载均衡算法负载均衡算法实现RestTemplate在Controller中使用该负载均衡算法ServiceIInstance解释 使用Ribbon自带负载均衡算法 添加负载均衡算法Configuration /…...
Mac terminal 每次打开都要重新配置文件
1. 问题描述 每次打开 Terminal,base_profile文件中配置的内容就不生效,需要重新执行source ~/.bash_profile才可以使用。 2. 原因分析 zsh加载的是~/.zshrc文件,而.zshrc 文件中并没有定义任务环境变量。 3. 解决办法 在~/.zshrc文件末尾添…...
el-button实现按钮,鼠标移入显示,移出隐藏
2023.8.18今天我学习了 如何实现鼠标移入显示按钮,鼠标移出隐藏按钮。 效果如图: 鼠标移入时: 鼠标移出时: mouseover //鼠标移入事件 mouseleave //鼠标移出事件 原本我是想直接在el-button写入这两个方法,但是elem…...
uniapp+uview封装小程序请求
提要: uniapp项目引入uview库 此步骤不再阐述 1.创建环境文件 env.js: let BASE_URL;if (process.env.NODE_ENV development) {// 开发环境BASE_URL 请求地址; } else {// 生产环境BASE_URL 请求地址; }export default BASE_URL; 2.创建请求文件 该…...
idea常见错误大全之:解决全局搜索失效+搜索条件失效(条件为空)+F8失灵
问题一:全局搜索快捷键ctrlshiftf 突然失灵了,键盘敲烂了 都没反应,这是为什么呢? 肯定不是idea本身的原因,那么就是其它外在因素影响到了idea的快捷键,那么其它的快捷键为什么没失效呢,原因只有…...
【论文阅读】基于深度学习的时序预测——LTSF-Linear
系列文章链接 论文一:2020 Informer:长时序数据预测 论文二:2021 Autoformer:长序列数据预测 论文三:2022 FEDformer:长序列数据预测 论文四:2022 Non-Stationary Transformers:非平…...
02.FFMPEG的安装和添加硬件加速自编译
说一个极其郁闷的事情,就在昨天收到3399的一块板子后,往电脑上面一插,然后悲剧的事情就发生了,我的电脑蓝屏重启了,这下好了,我写到一半的帖子也不见了,我的SSH里面的记录全部消失了,…...
elementUI 的上传组件<el-upload>,自定义上传按钮样式
方法一: 原理:调用<el-upload>组件的方法唤起选择文件事件 效果: 页面代码: 1、选择图片按钮 <div class"flex_row_spacebetween btn" click"chooseImg"><span class"el-icon-plus ic…...
【卷积神经网络】卷积,池化,全连接
随着计算机硬件的升级与性能的提高,运算量已不再是阻碍深度学习发展的难题。卷积神经网络(Convolution Neural Network,CNN)是深度学习中一项代表性的工作,CNN 是受人脑对图像的理解过程启发而提出的模型,其…...
C++:std::is_convertible
C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...
智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql
智慧工地管理云平台系统,智慧工地全套源码,java版智慧工地源码,支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求,提供“平台网络终端”的整体解决方案,提供劳务管理、视频管理、智能监测、绿色施工、安全管…...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...
MMaDA: Multimodal Large Diffusion Language Models
CODE : https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA,它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构…...
学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...
【生成模型】视频生成论文调研
工作清单 上游应用方向:控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...
面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...
Linux 下 DMA 内存映射浅析
序 系统 I/O 设备驱动程序通常调用其特定子系统的接口为 DMA 分配内存,但最终会调到 DMA 子系统的dma_alloc_coherent()/dma_alloc_attrs() 等接口。 关于 dma_alloc_coherent 接口详细的代码讲解、调用流程,可以参考这篇文章,我觉得写的非常…...
GAN模式奔溃的探讨论文综述(一)
简介 简介:今天带来一篇关于GAN的,对于模式奔溃的一个探讨的一个问题,帮助大家更好的解决训练中遇到的一个难题。 论文题目:An in-depth review and analysis of mode collapse in GAN 期刊:Machine Learning 链接:...
李沐--动手学深度学习--GRU
1.GRU从零开始实现 #9.1.2GRU从零开始实现 import torch from torch import nn from d2l import torch as d2l#首先读取 8.5节中使用的时间机器数据集 batch_size,num_steps 32,35 train_iter,vocab d2l.load_data_time_machine(batch_size,num_steps) #初始化模型参数 def …...
