1.实现效果

2.实现思路
- 使用JSON格式存放多语言翻译的文本,以窗体名称. Translations.json进行文件的命名。
- 将翻译方法写入基类窗体Load(frmBaseForm_Load)事件中,找到翻译文件即可进行翻译;未找到文件,则遍历窗体上控件得到需要翻译的文本,然后使用腾讯机器翻译API/自定义离线翻译工具API进行翻译的调用形成翻译文件,然后进行翻译。
3.具体实现
- 多语言下拉按钮
使用json文件进行配置
json
{
"中文": "zh",
"English": "en",
"italiano": "it",
"español": "es",
"ภาษาไทย": "th"
}
然后在xml里面存放当前选择的是哪种语言

初始化状态栏的时候添加
c#
string jsonString = System.IO.File.ReadAllText(new SkinInfoHeleper().GetAbsolutePath() + "\\" + "Language.json");
languageList = JsonConvert.DeserializeObject<Dictionary<string, string>>(jsonString) ?? new Dictionary<string, string>();
foreach (var item in languageList)
{
index++;
DevExpress.XtraBars.BarButtonItem barButtonItem = new DevExpress.XtraBars.BarButtonItem();
barButtonItem.Caption = item.Key;
barButtonItem.Name = "barButtonlanguageItemAuto" + index.ToString();
barButtonItem.Id = 999 + index;
barButtonItem.ItemClick += BarButtonItem_ItemClick; ;
this.barLanguageChange.LinksPersistInfo.AddRange(new DevExpress.XtraBars.LinkPersistInfo[] {
new DevExpress.XtraBars.LinkPersistInfo(barButtonItem) });
}
- 工具类代码
LanguageInjector :翻译窗体类
c#
public static class LanguageInjector
{
//public static ProjectManager ProMgr = new ProjectManager();
/// <summary>
/// 为指定窗体应用对应语言的文本
/// 读取文件:{窗体名}.Translations.json
/// 文件格式
/// </summary>
public static void ApplyLanguage(Form form, string cultureName)
{
if (form == null || string.IsNullOrWhiteSpace(cultureName))
return;
// 1. 构造文件路径:Form1.Translations.json
string fileName = $"{form.GetType().Name}.Translations.json";
string jsonPath = Path.Combine(Application.StartupPath.Replace("\\bin", "\\Language"), fileName);
// 2. 检查文件是否存在
if (!System.IO.File.Exists(jsonPath))
{
// 文件不存在,跳过,不报错
return;
}
try
{
//这里是为了防止修改了之后在数据库中还存在着手动修改的数据,在替换的时候优先使用手动修改的数据
var dataalist = GetTranslateData(cultureName, form.GetType().Name);
//var dataalist = new List<Fnd_Translation>();//不使用数据可以反注释这句话,或者删除相关的dataalist代码
// 3. 读取整个嵌套 JSON 文件
string json = System.IO.File.ReadAllText(jsonPath, Encoding.UTF8);
var allTranslations = JsonConvert.DeserializeObject<Dictionary<string, Dictionary<string, string>>>(json);
// 4. 根据 cultureName 获取对应语言的翻译字典
Dictionary<string, string> langMap = null;
if (allTranslations != null && allTranslations.ContainsKey(cultureName))
{
langMap = allTranslations[cultureName];
}
else
{
// 5. 回退到 zh-CN(默认中文)
if (allTranslations != null && allTranslations.ContainsKey("zh-CN"))
{
langMap = allTranslations["zh-CN"];
}
else
{
// 6. 无任何语言数据,直接返回
return;
}
}
//7.遍历窗体所有控件,应用翻译
foreach (Control control in GetAllControls(form))
{ // 跳过 ComboBox,避免干扰 SelectedIndex
//if (control is ComboBox)
// continue;
if (control.Name == "")
continue;
if (control.Name == "lbl")
{
continue;
}
// 跳过输入类控件(用户可编辑的)
if (control is DevExpress.XtraEditors.TextEdit || control is DevExpress.XtraEditors.ComboBoxEdit)
continue;
if (control is DevExpress.XtraGrid.GridControl gridControl)
{
// 获取主视图和所有从属视图
var views = new List<DevExpress.XtraGrid.Views.Base.BaseView>();
if (gridControl.MainView != null)
views.Add(gridControl.MainView);
if (gridControl.ViewCollection != null)
{
foreach (DevExpress.XtraGrid.Views.Base.BaseView view in gridControl.ViewCollection)
{
if (view != gridControl.MainView && !views.Contains(view))
views.Add(view);
}
}
// 遍历每个视图,如果是 GridView,则翻译其列标题
foreach (DevExpress.XtraGrid.Views.Base.BaseView view in views)
{
if (view is DevExpress.XtraGrid.Views.Grid.GridView gridView)
{
foreach (DevExpress.XtraGrid.Columns.GridColumn column in gridView.Columns)
{
// 构造列的唯一键:{GridControl名称}.{列名称}.Caption
string columnKey = $"{column.Name}.Caption";
if (dataalist.FirstOrDefault(a => a.LABEL_NAME == columnKey) != null)
{
column.Caption = dataalist.FirstOrDefault(a => a.LABEL_NAME == columnKey).TRANSLATION_TEXT;
}
else
{
if (langMap.TryGetValue(columnKey, out string translatedCaption))
{
column.Caption = translatedCaption;
}
}
}
}
}
}
if (control is UCBtnExt btn)
{
var keyWord = $"{control.Name}.Text";
if (dataalist.FirstOrDefault(a => a.LABEL_NAME == keyWord) != null)
{
btn.BtnText = dataalist.FirstOrDefault(a => a.LABEL_NAME == keyWord).TRANSLATION_TEXT;
}
else
{
btn.BtnText = langMap[$"{control.Name}.Text"];
}
}
string key = $"{control.Name}.Text";
if (langMap.TryGetValue(key, out string translatedText))
{
if (dataalist.FirstOrDefault(a => a.LABEL_NAME == key) != null)
{
control.Text = dataalist.FirstOrDefault(a => a.LABEL_NAME == key).TRANSLATION_TEXT;
}
else
{
control.Text = translatedText;
}
}
}
// 7. 遍历窗体自身及其所有控件,应用翻译
//var allControls = new List<Control> { form }; // 先加入窗体自身
//allControls.AddRange(GetAllControls(form));
//foreach (Control control in allControls)
//{
// if (string.IsNullOrEmpty(control.Name))
// continue;
// // 跳过输入类控件(用户可编辑的)
// if (control is DevExpress.XtraEditors.TextEdit || control is DevExpress.XtraEditors.ComboBoxEdit)
// continue;
// string key = $"{control.Name}.Text";
// if (langMap.TryGetValue(key, out string translatedText))
// {
// control.Text = translatedText;
// }
//}
}
catch (Exception ex)
{
// 翻译文件格式错误或读取失败,静默处理,不影响程序
System.Diagnostics.Debug.WriteLine($"加载翻译文件失败: {jsonPath},错误: {ex.Message}");
}
}
/// <summary>
/// 递归获取所有控件(包括嵌套容器中的)
/// </summary>
private static IEnumerable<Control> GetAllControls(Control parent)
{
foreach (Control c in parent.Controls)
{
yield return c;
if (c.HasChildren)
foreach (var child in GetAllControls(c))
yield return child;
}
}
}
TencentCloudTranslator:腾讯机工具类 要使用Nuget包下载TencentCloud.Tmt
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using TencentCloud.Common;
using TencentCloud.Common.Profile;
using TencentCloud.Tmt.V20180321;
using TencentCloud.Tmt.V20180321.Models;
public class TencentCloudTranslator
{
private readonly TmtClient _client;
// 构造函数:初始化客户端(建议在应用启动时单例使用)
public TencentCloudTranslator(string region = "ap-chongqing")
{
// 仅用于本地调试,生产环境绝对禁止!
Environment.SetEnvironmentVariable("TENCENTCLOUD_SECRET_ID", "E7nFQct");
Environment.SetEnvironmentVariable("TENCENTCLOUD_SECRET_KEY", "Kk5H4cofOvIWgill");
var secretId = Environment.GetEnvironmentVariable("TENCENTCLOUD_SECRET_ID");
var secretKey = Environment.GetEnvironmentVariable("TENCENTCLOUD_SECRET_KEY");
if (string.IsNullOrEmpty(secretId) || string.IsNullOrEmpty(secretKey))
{
throw new InvalidOperationException("请在环境变量中配置 TENCENTCLOUD_SECRET_ID 和 TENCENTCLOUD_SECRET_KEY。");
}
var cred = new Credential
{
SecretId = secretId,
SecretKey = secretKey
};
var httpProfile = new HttpProfile
{
Endpoint = "tmt.tencentcloudapi.com"
};
var clientProfile = new ClientProfile
{
HttpProfile = httpProfile
};
_client = new TmtClient(cred, region, clientProfile);
}
/// <summary>
/// 翻译文本
/// </summary>
/// <param name="text">要翻译的原文</param>
/// <param name="sourceLang">源语言代码,如 "zh"(中文)</param>
/// <param name="targetLang">目标语言代码,如 "en"(英文)、"ja"(日文)等</param>
/// <param name="projectId">项目ID,默认为 0</param>
/// <returns>翻译后的文本</returns>
public string Translate(string text, string sourceLang = "zh", string targetLang = "en", long projectId = 0)
{
if (string.IsNullOrWhiteSpace(text))
throw new ArgumentException("待翻译文本不能为空。", nameof(text));
try
{
var req = new TextTranslateRequest
{
SourceText = text,
Source = sourceLang,
Target = targetLang,
ProjectId = projectId
};
TextTranslateResponse resp = _client.TextTranslateSync(req);
return resp.TargetText;
}
catch (Exception ex)
{
// 建议记录日志而不是仅输出到控制台
Console.WriteLine($"翻译失败: {ex}");
throw; // 或返回 null / 默认值,根据业务需求决定
}
}
/// <summary>
/// 翻译多条文本
/// </summary>
/// <param name="text">要翻译的原文</param>
/// <param name="sourceLang">源语言代码,如 "zh"(中文)</param>
/// <param name="targetLang">目标语言代码,如 "en"(英文)、"ja"(日文)等</param>
/// <param name="projectId">项目ID,默认为 0</param>
/// <returns>翻译后的文本</returns>
public List<string> batchTranslate(List<string> textList, string sourceLang = "zh", string targetLang = "en", long projectId = 0)
{
if (textList.Count == 0)
throw new ArgumentException("待翻译文本不能为空。");
try
{
var req = new TextTranslateBatchRequest
{
SourceTextList = textList.ToArray(),
Source = sourceLang,
Target = targetLang,
ProjectId = projectId
};
TextTranslateBatchResponse resp = _client.TextTranslateBatchSync(req);
return resp.TargetTextList.ToList();
}
catch (Exception ex)
{
// 建议记录日志而不是仅输出到控制台
Console.WriteLine($"翻译失败: {ex}");
throw; // 或返回 null / 默认值,根据业务需求决定
}
}
}
TranslateExistingJson:中文模板工具类
c#
public static class TranslateExistingJson{
public static void AutoTranslateExistingJson(string FromName)
{
string fileName = $"{FromName}.Translations.json";
string filePath = Path.Combine(Application.StartupPath.Replace("\\bin", "\\Language"), fileName);
if (!File.Exists(filePath))
{
MessageBox.Show($"未找到翻译模板文件:{fileName}\n请先执行"导出翻译模板"功能。", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
// 1. 读取现有 JSON
string jsonContent = File.ReadAllText(filePath);
var allTranslations = JsonConvert.DeserializeObject<Dictionary<string, Dictionary<string, string>>>(jsonContent);
if (!allTranslations.TryGetValue("zh", out var sourceDict) || sourceDict.Count == 0)
{
MessageBox.Show("模板中缺少 zh 数据或为空。", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); return;
}
string jsonString = System.IO.File.ReadAllText(new SkinInfoHeleper().GetAbsolutePath() + "\\" + "Language.json");
var targetLanguages = JsonConvert.DeserializeObject<Dictionary<string, string>>(jsonString) ?? new Dictionary<string, string>();
// 2. 定义目标语言映射(.NET 文化名 → 腾讯云语言代码)
//var targetLanguages = new Dictionary<string, string>
// {
// { "en-US", "en" },
// { "fr-FR", "fr" },
// { "ru-RU", "ru" },
// { "ja-JP", "ja" },
// { "es-ES", "es" }
// // 可按需扩展
// };
// 3. 初始化翻译器
TencentCloudTranslator translator;
try
{
translator = new TencentCloudTranslator();
}
catch (Exception ex)
{
MessageBox.Show($"翻译器初始化失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
// 4. 提取键和值(保持顺序一致)
var keys = sourceDict.Keys.ToList();
var texts = sourceDict.Values.ToList();
// 5. 对每种语言进行翻译并合并
foreach (var lang in targetLanguages)
{
string cultureName = lang.Value; // 如 "en-US"
string tmtCode = lang.Value; // 如 "en"
if (allTranslations.ContainsKey(cultureName))
{
// 可选:跳过已存在的语言,或覆盖
// 这里选择跳过以避免重复翻译
continue;
}
try
{
var translatedList = translator.batchTranslate(texts, "zh", tmtCode);
var translatedDict = new Dictionary<string, string>();
for (int i = 0; i < keys.Count; i++)
{
translatedDict[keys[i]] = translatedList[i];
}
allTranslations[cultureName] = translatedDict;
}
catch (Exception ex)
{
MessageBox.Show($"翻译到 {cultureName} 时出错:{ex.Message}", "警告", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
// 6. 保存回文件
string updatedJson = JsonConvert.SerializeObject(allTranslations, Newtonsoft.Json.Formatting.Indented);
File.WriteAllText(filePath, updatedJson);
//MessageBox.Show($"多语言翻译已完成并更新到:{filePath}", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
/// <summary>
/// 获取语言列表
/// </summary>
/// <returns></returns>
public static Dictionary<string, string> GetLanguageList()
{
string jsonString = System.IO.File.ReadAllText(new SkinInfoHeleper().GetAbsolutePath() + "\\" + "Language.json");
var languageList = JsonConvert.DeserializeObject<Dictionary<string, string>>(jsonString) ?? new Dictionary<string, string>();
return languageList;
}
/// <summary>
/// 获取语言对应的国家
/// </summary>
public static string GetLanguageContry(string language)
{
string jsonString = System.IO.File.ReadAllText(new SkinInfoHeleper().GetAbsolutePath() + "\\" + "Language.json");
var languageList = JsonConvert.DeserializeObject<Dictionary<string, string>>(jsonString) ?? new Dictionary<string, string>();
return languageList[language];
}
/// <summary>
/// 是否存在窗体的语言文件
/// </summary>
/// <param name="FromName"></param>
/// <returns></returns>
public static bool IsExistFromNameLanguage(string FromName)
{
string fileName = $"{FromName}.Translations.json";
string filePath = Path.Combine(Application.StartupPath.Replace("\\bin", "\\Language"), fileName);
if (!File.Exists(filePath))
{
return false;
}
else
{
return true;
}
}
public static string GetTranslationFileString(string FromName)
{
string fileName = $"{FromName}.Translations.json";
string filePath = Path.Combine(Application.StartupPath.Replace("\\bin", "\\Language"), fileName);
string jsonContent = File.ReadAllText(filePath);
return jsonContent;
}
public static string GetFormFilePath(string FromName)
{
string fileName = $"{FromName}.Translations.json";
string filePath = Path.Combine(Application.StartupPath.Replace("\\bin", "\\Language"), fileName);
return filePath;
}
}
-
基类窗体Load事件中调用
c#private void frmBaseForm_Load(object sender, EventArgs e) { this.OnInitBar(null, null, null); this.InitBarTools(); var lamg = LicenseXMLHelper.GetItemValue("Languages"); //获取语言实际的国家 var language = TranslateExistingJson.GetLanguageContry(lamg); LanguageInjector.ApplyLanguage(this, language); } -
翻译主体框架
C#/// <summary> /// 找到需要翻译的所有控件 一定要在页面加载之后进行 /// </summary> private void GetNeedTrant(List<ZR.Model.Privilege.MenuInfo> PublicList) { // 1. 收集当前窗体所有控件的 Text var controlTexts = new Dictionary<string, string>(); // 遍历窗体中所有控件,提取所有控件的 Text 属性 foreach (Control ctrl in LicenseXMLHelper.GetAllControls(this)) { if (!string.IsNullOrEmpty(ctrl.Text)) { controlTexts[$"{ctrl.Name}.Text"] = ctrl.Text; } //如果是列表 if (ctrl is DevExpress.XtraGrid.GridControl gridControl) { // 遍历该 GridControl 的所有视图(包括主视图和从属视图) var views = new List<DevExpress.XtraGrid.Views.Base.BaseView>(); views.Add(gridControl.MainView); // 主视图 // 如果是主从表结构,获取所有从属视图 if (gridControl.ViewCollection != null) { foreach (DevExpress.XtraGrid.Views.Base.BaseView view in gridControl.ViewCollection) { if (view != gridControl.MainView && !views.Contains(view)) { views.Add(view); } } } // 遍历每个视图,如果是 GridView,则提取其列 foreach (DevExpress.XtraGrid.Views.Base.BaseView view in views) { if (view is DevExpress.XtraGrid.Views.Grid.GridView gridView) { foreach (DevExpress.XtraGrid.Columns.GridColumn column in gridView.Columns) { if (!string.IsNullOrEmpty(column.Caption)) { controlTexts[$"{column.Name}.Caption"] = column.Caption; } } } } } staticItems = LicenseXMLHelper.GetAllBarStaticItems(this.barManager1).ToList(); foreach (var item in staticItems) { controlTexts[$"{item.Name}.Caption"] = item.Caption; } var toolItems = LicenseXMLHelper.GetItemsRecursive(this.contextMenuStrip1.Items); foreach (var item in toolItems) { controlTexts[$"{item.Name}.Text"] = item.Text; } foreach (var item in PublicList) { var formName = item.LinkForm.ClassName.Split('.').Last(); if (!string.IsNullOrWhiteSpace(formName)) { controlTexts[$"{formName}.Text"] = item.Caption; } } } // 2. 包装成嵌套结构:{ "zh-CN": { ... } } var nestedTranslations = new Dictionary<string, Dictionary<string, string>> { { "zh", controlTexts } // 关键:用 zh-CN 作为外层键 }; // 3. 输出到文件:Form1.Translations.json string fileName = $"{this.Name}.Translations.json"; string filePath = Path.Combine(Application.StartupPath.Replace("\\bin", "\\Language"), fileName); //需要重新主体框架翻译就解开下面两行的注释 这句话很重要 //File.WriteAllText(filePath, JsonConvert.SerializeObject(nestedTranslations, Newtonsoft.Json.Formatting.Indented)); //TranslateExistingJson.AutoTranslateExistingJson(this.Name); //加载xml配置文件中定义的当前语言,此方法仅在这个地方有效果,其他地方都不能完成语言的切换 LoadMainThenTrantslate(); } /// <summary> /// 所有页面加载完成之后翻译主体框架 /// </summary> private void LoadMainThenTrantslate() { var lamg = LicenseXMLHelper.GetItemValue("Languages"); //获取语言实际的国家 var language = TranslateExistingJson.GetLanguageContry(lamg); //应用翻译 ApplyLanguage(this, language); }需要在打开新窗口的代码下面加上调用ApplyLanguageXtab调用,用于Table页的切换
C#if (!string.IsNullOrEmpty(FromInfo.DllPath) && !string.IsNullOrEmpty(FromInfo.ClassName)) { string dllPath = FromInfo.DllPath; //模块路径 string formName = FromInfo.ClassName; //类名 Assembly outerAsm = Assembly.LoadFrom(dllPath); Type outerForm = outerAsm.GetType(formName, false);//找到指定窗口 frmBaseForm form = (Activator.CreateInstance(outerForm) as frmBaseForm);//转换成窗体类 form.Text = FromInfo.ClassTitle; form.MdiParent = this; form.CanClose = FromInfo.CanClose; form.Line.DESCRIPTIONS = FromInfo.LineName; form.Dot.DESCRIPTIONS = FromInfo.PointName; form.FormType = FromInfo.FormType; form.LookAndFeel.UseDefaultLookAndFeel = true; form.Show(); //如果不存在语言包则调用语言包生成 if (!TranslateExistingJson.IsExistFromNameLanguage(form.Name)) { LicenseXMLHelper.ExportTranslations(form); } var lamg = LicenseXMLHelper.GetItemValue("Languages"); //获取语言实际的国家 var language = TranslateExistingJson.GetLanguageContry(lamg); //加载页面之后重新翻译Tab名称 ApplyLanguageXtab(this,language); }C#/// <summary> /// 翻译Xtab页面 /// </summary> /// <param name="form"></param> /// <param name="cultureName"></param> private void ApplyLanguageXtab(Form form,string cultureName) { if (form == null || string.IsNullOrWhiteSpace(cultureName)) return; // 1. 构造文件路径:Form1.Translations.json string fileName = $"{form.GetType().Name}.Translations.json"; string jsonPath = Path.Combine(Application.StartupPath.Replace("\\bin", "\\Language"), fileName); // 2. 检查文件是否存在 if (!File.Exists(jsonPath)) { // 文件不存在,跳过,不报错 return; } try { // 3. 读取整个嵌套 JSON 文件 string json = File.ReadAllText(jsonPath, Encoding.UTF8); var allTranslations = JsonConvert.DeserializeObject<Dictionary<string, Dictionary<string, string>>>(json); // 4. 根据 cultureName 获取对应语言的翻译字典 Dictionary<string, string> langMap = null; if (allTranslations != null && allTranslations.ContainsKey(cultureName)) { langMap = allTranslations[cultureName]; } else { // 5. 回退到 zh-CN(默认中文) if (allTranslations != null && allTranslations.ContainsKey("zh-CN")) { langMap = allTranslations["zh-CN"]; } } foreach (var items in xtraTabbedMdiManager1.Pages.ToList()) { var control = items.MdiChild; string key = $"{control.Name}.Text"; if (langMap.TryGetValue(key, out string translatedText)) { control.Text = translatedText; } } } catch (Exception ex) { } }