c#: 界面多语言动态切换简单实现
终于有空整理下多语言实现思路。查阅已有方案,有用不同resx文件的,有每个控件动态设置的,有用反射去整的,颇为繁琐。
结合项目中实现方法,并做简化,实现通用的多语言切换方案,以做备忘。
它支持语言自定义添加与扩充,灵活易用,更易于维护。它以xml格式存储语言信息,支持自定义语言、ToolTip等字串,支持即时切换。
一、语言格式:
每种语言对应一个xml格式文件,比如中文为Chinese.lng,英文为English.lng,置于程序运行目录之Languages文件夹下,其存放位置可自定义。
本欲以反射获取已有程序集之字串,却未有成功,因为字串值初始化在InitializeComponent()中动态设置,反射不出来这些属性值,所以手动提取,以NotePad++做批量处理,亦是方便。
文件分为自定义字串区(strings节点)、窗体语言文件区(forms节点),结构如下图示:
- <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
- <language Id="CHS" Name="Chinese" LocaleName="中文(简体)" LangID="0x0409" CodePage="1252">
- <strings>
- <INF_AppName>记事本</INF_AppName>
- <INF_NoTitle>无标题</INF_NoTitle>
- <INF_Information>提示</INF_Information>
- <INF_CannotFound>找不到 "{0}"</INF_CannotFound>
- <INF_ContentChanged>文件内容已被改变,要保存吗?</INF_ContentChanged>
- </strings>
- <forms>
- <MainForm Text="记事本">
- <miFile Text="文件(&F)" />
- <miNew Text="新建(&N)" />
- <miOpen Text="打开(&0)..." />
- <miSave Text="保存(&S)" />
- <miSaveAs Text="另存为(&A)..." />
- <miPageSet Text="页面设置(&U)..." />
- <miPrint Text="打印(&P)..." />
- <miExit Text="退出(&X)" />
- ...
- <miHelp Text="帮助(&H)" />
- <miHelpTopic Text="帮助主题(&H)" />
- <miLanguage Text="语言" />
- <miAbout Text="关于记事本(&A)" />
- </MainForm>
- <GotoLineForm Text="跳转到指定行">
- <lblLine Text="行数(&L):" />
<txtLine ToolTip="请输入您想要跳转到的行号:" />- <btnOk Text="确定" />
- <btnCancel Text="取消" />
- </GotoLineForm>
- <SearchForm Text="查找">
- <btnCancel Text="取消" />
- <btnSearch Text="查找下一个(&F)" />
- <lblContent Text="查找内容(&N):" />
- <cbCaseSensitive Text="区分大小写" />
- <gbDirection Text="方向" />
- <rbDown Text="向下" />
- <rbUp Text="向上" />
- </SearchForm>
- </forms>
- </language>
二、解析与加载类
仍然对每个窗体做遍历,加载对应字串,提取语言文件语种信息。
此文件是个完整的语言管理器类,且可根据需要自行扩充,列码如下:
- //多语言管理类
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Windows.Forms;
- using System.Xml;
- namespace Notepad
- {
- //单个语言项
- public class LanguageItem
- {
- private const string RootNodeName = "language";
- private const string StringsNodeName = "strings";
- private const string FormsNodeName = "forms";
- private XmlNode stringsNode;
- private XmlNode formsNode;
- private string id;
- private string name;
- private string localeName;
- public LanguageItem(string fileName)
- {
- var xmlDoc = new XmlDocument();
- xmlDoc.Load(fileName);
- var root = xmlDoc.DocumentElement;
- if (root != null)
- {
- this.id = GetAttributeValue(root, "Id");
- this.name = GetAttributeValue(root, "Name");
- this.localeName = GetAttributeValue(root, "LocaleName");
- this.stringsNode = root.SelectSingleNode(StringsNodeName);
- this.formsNode = root.SelectSingleNode(FormsNodeName);
- }
- }
- public string Id
- {
- get { return this.id; }
- }
- public string Name
- {
- get { return this.name; }
- }
- public string LocaleName
- {
- get { return this.localeName; }
- }
- private void ApplyLanguage(Control control, XmlNode formNode, ToolTip toolTip = null)
- {
- if (string.IsNullOrEmpty(control.Name))
- return;
- var ctrlNode = formNode.SelectSingleNode(control.Name);
- if (ctrlNode != null)
- {
- control.Text = GetAttributeValue(ctrlNode, "Text");
- string tips = GetAttributeValue(ctrlNode, "ToolTip");
- if (!string.IsNullOrEmpty(tips) && toolTip != null)
- toolTip.SetToolTip(control, tips);
- }
- //弹出菜单
- if (control.ContextMenuStrip != null)
- ApplyMenuLanguage(control.ContextMenuStrip, formNode);
- foreach (Control ctrl in control.Controls)
- ApplyLanguage(ctrl, formNode, toolTip);
- //菜单项,特别遍历
- if (control is ToolStrip)
- {
- foreach (ToolStripItem toolItem in (control as ToolStrip).Items)
- ApplyMenuLanguage(toolItem, formNode);
- }
- }
- private void ApplyMenuLanguage(ToolStrip menu, XmlNode formNode)
- {
- foreach (ToolStripItem toolItem in menu.Items)
- ApplyMenuLanguage(toolItem, formNode);
- }
- private void ApplyMenuLanguage(ToolStripItem menuItem, XmlNode formNode)
- {
- if (string.IsNullOrEmpty(menuItem.Name))
- return;
- var itemNode = formNode.SelectSingleNode(menuItem.Name);
- if (itemNode != null)
- {
- menuItem.Text = GetAttributeValue(itemNode, "Text");
- menuItem.ToolTipText = GetAttributeValue(itemNode, "ToolTip");
- }
- if (menuItem is ToolStripDropDownItem)
- {
- foreach (ToolStripItem item in (menuItem as ToolStripDropDownItem).DropDownItems)
- ApplyMenuLanguage(item, formNode);
- }
- }
- public bool LoadLanguageRes(ContainerControl cc)
- {
- if (cc == null || formsNode == null || !formsNode.HasChildNodes || formsNode.SelectSingleNode(cc.Name) == null)
- return false;
- //创建ToolTip控件, 以支持ToolTip显示
- var toolTip = new ToolTip();
- var formNode = formsNode.SelectSingleNode(cc.Name);
- cc.Text = GetAttributeValue(formNode, "Text");
- foreach (Control ctrl in cc.Controls)
- ApplyLanguage(ctrl, formNode, toolTip);
- return true;
- }
- public bool LoadLanguageRes(ContainerControl cc, ToolStrip menu)
- {
- if (cc == null || formsNode == null || !formsNode.HasChildNodes || formsNode.SelectSingleNode(cc.Name) == null)
- return false;
- var formNode = formsNode.SelectSingleNode(cc.Name);
- ApplyLanguage(menu, formNode);
- return true;
- }
- private string GetAttributeValue(XmlNode xmlNode, string attrName)
- {
- if (xmlNode.Attributes != null && xmlNode.Attributes[attrName] != null)
- return xmlNode.Attributes[attrName].Value;
- return string.Empty;
- }
- public string GetText(string textID, string defaultText = "")
- {
- if (stringsNode == null || !stringsNode.HasChildNodes)
- return defaultText;
- foreach (XmlNode node in stringsNode.ChildNodes)
- if (node.Name.Equals(textID))
- return node.InnerText;
- return defaultText;
- }
- }
- public class LanguageList : List<LanguageItem>
- {
- }
- /// <summary>
- /// 多语言管理器
- /// </summary>
- public static class ML
- {
- private static LanguageItem activeLanguage;
- private static LanguageList languages;
- //最初调用
- public static int LoadLanguages(string searchPattern, string defaultLanguageId = "")
- {
- string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Languages");
- return LoadLanguages(path, searchPattern, defaultLanguageId);
- }
- public static int LoadLanguages(string path, string searchPattern, string defaultLanguageId = "")
- {
- languages = new LanguageList();
- if (!Directory.Exists(path))
- return ;
- var files = Directory.GetFiles(path, searchPattern);
- foreach (string file in files)
- languages.Add(new LanguageItem(file));
- if (!string.IsNullOrEmpty(defaultLanguageId))
- LoadLanguageById(defaultLanguageId);
- return languages.Count;
- }
- public static string ActiveLanguageId
- {
- get { return (activeLanguage != null) ? activeLanguage.Id : string.Empty; }
- }
- public static string[] LanguageLocalNames
- {
- get
- {
- if (languages == null || languages.Count == )
- return new string[];
- var names = new string[languages.Count];
- for (int i = ; i <= languages.Count - ; i++)
- names[i] = languages[i].LocaleName;
- return names;
- }
- }
- public static LanguageItem ActiveLanguage
- {
- get { return activeLanguage; }
- }
- public static LanguageList Languages
- {
- get { return languages; }
- }
- public static bool LoadLanguageRes(ContainerControl cc)
- {
- return (ActiveLanguage != null) ? ActiveLanguage.LoadLanguageRes(cc) : false;
- }
- //为动态弹出菜单设计
- public static bool LoadLanguageRes(ContainerControl cc, ToolStrip menu)
- {
- return (ActiveLanguage != null) ? ActiveLanguage.LoadLanguageRes(cc, menu) : false;
- }
- public static string Text(string textId, string defaultText = "")
- {
- return GetText(textId, defaultText);
- }
- public static string GetText(string textId, string defaultText = "")
- {
- return (ActiveLanguage != null) ? ActiveLanguage.GetText(textId, defaultText) : defaultText;
- }
- public static bool LoadLanguageById(string id)
- {
- if (languages == null)
- return false;
- foreach (var language in languages)
- {
- if (language.Id.Equals(id))
- {
- activeLanguage = language;
- return true;
- }
- }
- return false;
- }
- public static bool LoadLanguageByIndex(int index)
- {
- if (index < || index > languages.Count - )
- return false;
- activeLanguage = languages[index];
- return true;
- }
- }
- }
OK,一行加载完成。
窗体创建事件中,加入加载语言代码:
- //改写构造函数
- public MainForm(string[] args)
- {
- InitializeComponent();
- appName = ML.GetText("INF_AppName", APP_NAME);
- ML.LoadFormLanguage(this);
//若有未与控件关联的弹出菜单,可加如下代码实现:
//ML.LoadLanguageRes(this, pmMain);- BuildLanguageMenuItems();
- this.args = args;
- }
这样就完成了语言加载。
这里有个多语言项生成菜单,其动态生成语言菜单项并加入事件触发,函数片段如下:
- private void BuildLanguageMenuItems()
- {
- if (ML.LanguageLocalNames.Length == )
- {
- miLanguage.Visible = false;
- return;
- }
- for (int i = ; i <= ML.LanguageLocalNames.Length - ; i++)
- {
- string ln = ML.LanguageLocalNames[i];
- var menuItem = new ToolStripMenuItem(ln);
- //menuItem.CheckOnClick = true;
- menuItem.Tag = i;
- if (i == )
- menuItem.Checked = true;
- menuItem.Click += new EventHandler((sender, e) =>
- {
- foreach (ToolStripMenuItem item in miLanguage.DropDownItems)
- item.Checked = (item == sender);
- ML.LoadLanguageByIndex((int)(sender as ToolStripItem).Tag);
- ML.LoadFormLanguage(this);
- });
- miLanguage.DropDownItems.Add(menuItem);
- }
- }
功能完美实现。
四、自定义语言用法
语言串中,自定义语言定义在了strings节点,程序中如此调用:
- MessageBox.Show(this.searchForm, string.Format(ML.GetText("INF_CannotFound", "找不到 \"{0}\""), this.searchText), ML.GetText("INF_Information", "提示"), MessageBoxButtons.OK, MessageBoxIcon.Warning);
- switch (MessageBox.Show(ML.GetText("INF_ContentChanged", "文件内容已被改变,要保存吗?"), ML.GetText("INF_Information", "提示"), MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question))
- {
- case DialogResult.Yes:
- return Save();
- case DialogResult.No:
- return true;
- case DialogResult.Cancel:
- return false;
- default:
- return true;
- }
颇为灵活。
五、效果图:
Demo源码下载:
c#: 界面多语言动态切换简单实现的更多相关文章
- C语言 动态库简单开发
动态库项目 //简单的动态库开发----报文发送 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib. ...
- Qt 国际化之二:多国语界面动态切换的实现
第一步在你的pro里面加入 TRANSLATIONS = myexec_zh.ts (根据对应的ts文件修改)第二步用lupdate 操作pro 将要翻译的提取到ts文件 命令是 lupdate my ...
- Spring简单实现数据源的动态切换
Spring简单实现数据源的动态切换: 1. 创建一个数据源切换类: 2. 继承AbstractRoutingDataSource,创建多数据源路由类,并注入到spring的配置文件中: 3. AOP ...
- 动态切换采用 CSplitterWnd 静态划分的视图布局(MFC)
标题读起来有些拗口,具体是什么情况,我们来看: 一.问题的提出 一个采用MFC开发的软件,其窗体视图采用CSplitterWnd三分,效果如下图所示: 图1 软件的默认视图布局 该MFC开发的软件功能 ...
- QT皮肤系统的动态切换
应用需求: 提供皮肤切换选项,在不重启应用程序的情况下实现皮肤的动态切换. 理论基础: 1) 图片资源是如何被利用的 这里先简要说明一下实现原理,皮肤的动态切换其关键在于图片资源的加载方式.QT中每个 ...
- 动态切换数据库(EF框架)
文章简略:本文测试项目为Silverlight+EF+RIA Service动态切换数据库的问题 通常,Ado.net EntityFramework的数据库连接字符串Connect ...
- Matlab中界面和注释---中英文切换问题
有参考网页后实践的心得: Matlab中界面和注释---中英文切换问题 网上有大把的方法,并不是一一有效,这里介绍一种比较简单的方法我自己的电脑挺好用的,大家的电脑matlab需要你们自己实验了. 1 ...
- Spring AOP动态切换数据源
现在稍微复杂一点的项目,一个数据库也可能搞不定,可能还涉及分布式事务什么的,不过由于现在我只是做一个接口集成的项目,所以分布式就先不用了,用Spring AOP来达到切换数据源,查询不同的数据库就可以 ...
- 踢爆IT劣书出版黑幕——由清华大学出版社之《C语言入门很简单》想到的(1)
1.前言与作者 首先声明,我是由于非常偶然的机会获得<C语言入门很简单>这本书的,绝对不是买的.买这种书实在丢不起那人. 去年这书刚出版时,在CU论坛举行试读推广,我当时随口说了几句(没说 ...
随机推荐
- java_oop_类
类的初始化顺序 再论类的组成 类的初始化顺序详解 变量 实例变量(成员变量) 类变量(静态变量) 方法 实例方法 ...
- jQuery基础(二)DOM
DOM节点的创建 jQuery节点创建与属性的处理 创建元素节点: $("<div></div>") 创建为文本节点: $("<div> ...
- Linux内核分析第九次作业
理解进程调度时机跟踪分析进程调度与进程切换的过程 一.基础知识 Linux系统的一般执行过程 一般情况:正在运行的用户态进程X切换到运行用户态进程Y的过程 1. 正在运行的用户态进程X 2. 发生中断 ...
- 《Linux内核原理与分析》第四次作业
跟踪分析Linux内核的启动过程 使用实验楼的虚拟机打开shell 使用 gdb 跟踪调试内核 使用 qemu qemu -kernel linux-3.18.6 /arch/x86/boot/baI ...
- [转]python中pandas库中DataFrame对行和列的操作使用方法
转自:http://blog.csdn.net/u011089523/article/details/60341016 用pandas中的DataFrame时选取行或列: import numpy a ...
- centos-rpm安装的mariadb,php52源码编译安装时注意点
1.不要静态指定with-mysql 以扩展的mysql.so的形式安装 2.找不到header file之类的 要yum install mysql-devel find / -name mysql ...
- Numpy、SciPy、MatPlotLib在Python2.7.9下的安装与配置
前言: Python安装完Numpy,SciPy和MatplotLib后,可以成为非常犀利的科研利器.网上关于这三个库的安装都写得非常不错,但是大部分人遇到的问题并不是如何安装,而是安装好后因为配置不 ...
- java中的内部类详解
https://www.cnblogs.com/dolphin0520/p/3811445.html https://www.cnblogs.com/chenssy/p/3388487.html
- [转][C#]程序的动态编译
转自:https://blog.csdn.net/clb929/article/details/51385399 附 文件下载
- Unity Shader Graph(一)初次尝试
软件环境 Unity Version: 2018.1.2f1 边缘发光材质效果 创建工程 打开Unity并创建一个新工程 安装依赖项 Window -> Package Manager打开包管理 ...