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论坛举行试读推广,我当时随口说了几句(没说 ...
随机推荐
- 在 sql 语句出现 warning 之后,立刻执行 `show warnings;` 就可以看到 warning 提示信息
在 sql 语句出现 warning 之后,立刻执行 show warnings; 就可以看到 warning 提示信息
- (C#)中的DataSet、string、DataTable等对象转换成Json
ConvertJson.cs类 using System; using System.Collections.Generic; using System.Text; using System.Data ...
- jekins构建触发器详解
jenkins版本:2.89.2 1.触发远程构建 (例如,使用脚本):通过一个网址的访问来触发构建,这样就不需要登录jenkins系统也能触发构建了. 示例地址: http://localhost: ...
- Office_PPT_让你一分钟完成上百张图片的快速保存
1 方式 修改PPT文件格式,由PPT修改为rar,再进行解压操作 进入到ppt->media中找到你在PPT为文件中使用的图片. 2 PPT北京图片下载网址 别样网:https://www.s ...
- Alsa aplay S8 U8 S16_LE S16_BE U16_LE U16_BE格式
举个例子 aplay -r 16000 -f S16_LE -D hw:0,0 -c 2 -d 3 ~/Private/Private_Tools/02_ALSA_Learning/left_1k_r ...
- 保存数据到Excel中
调用的方法传值 Export(dt, "Cal_Report_" + DateTime.Now.ToString("yyyyMMddhhmmss") + &qu ...
- nowcoder 寻找(LCA)
这个题貌似是过的最少的? smeow一眼给出了一个单log的算法orz 首先求出x和y的lca, x和c的lca,y和c的lca, 然后分类讨论以下就行了 实际上只有三种情况 #include< ...
- 彻底征服 Spring AOP 之 实战篇
Spring AOP 实战 看了上面这么多的理论知识, 不知道大家有没有觉得枯燥哈. 不过不要急, 俗话说理论是实践的基础, 对 Spring AOP 有了基本的理论认识后, 我们来看一下下面几个 ...
- 谈谈线上CPU100%排查套路
知识点总结 ---------------------------------------------------------------------------------------------- ...
- gentoo 图像方面的软件
图像方面的软件一般包括:查看图像,屏幕截图,图像修改. 查看图像简单的可以安装 feh,但是 feh 一般作为墙纸来用.稍微好一些的是 gqview. 屏幕截图可以用 screengrab,使用的时候 ...