在我当前的项目(.NET Windows 窗体应用程序)中,我要求 .NET Windows 窗体应该本地化,但本地化元素(只是翻译,而不是图像或控制位置)应该来自数据库,以便启用最终用户可以根据需要修改控件的可本地化属性(只是标题/文本)。为了让开发人员摆脱本地化问题的负担,对我来说最好的解决方案是在 VS 设计器中简单地将表单标记为可本地化。这会将所有可本地化的属性值放在表单 .resx 文件中。
现在我的问题是如何从数据库中提供翻译。表单被标记为可本地化的那一刻,VS 表单设计器将放置所有可以本地化的表单 .resx 文件。设计器还将修改标准的 Designer.cs InitializeComponent 方法,以便实例化 ComponentResourceManager,然后使用该资源管理器加载对象(控件和组件)的可本地化属性。
我已经看到人们建立了自己的方法来将本地化属性应用于 Form 及其控件的解决方案。我见过的所有解决方案通常归结为递归地遍历 Form 及其控件的 Controls 集合并应用翻译。不幸的是,.NET Forms 本地化并不是那么简单,这并不能涵盖所有场景,尤其是如果您有一些 3rd 方控件。例如:
this.navBarControl.OptionsNavPane.ExpandedWidth = ((int)(resources.GetObject("resource.ExpandedWidth")));
//
// radioGroup1
//
resources.ApplyResources(this.radioGroup1, "radioGroup1");
...
this.radioGroup1.Properties.Items.AddRange(new DevExpress.XtraEditors.Controls.RadioGroupItem[] {
new DevExpress.XtraEditors.Controls.RadioGroupItem(resources.GetString("radioGroup1.Properties.Items"), resources.GetString("radioGroup1.Properties.Items1")),
new DevExpress.XtraEditors.Controls.RadioGroupItem(resources.GetString("radioGroup1.Properties.Items2"), resources.GetString("radioGroup1.Properties.Items3"))});
我见过的所有解决方案都无法进行上述组件所需的翻译。
由于 VS 已经生成了在需要的地方提供翻译的代码,我理想的解决方案是以某种方式用我自己的派生类替换 ComponentResourceManager。如果我可以在 InitializeComponent 中替换以下行:
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
和
System.ComponentModel.ComponentResourceManager resources = new MyComponentResourceManager(typeof(Form1));
然后我可以毫无问题地解决其他所有问题。
不幸的是,我没有找到如何做这样的事情,所以我在这里问一个关于如何做的问题。
PS 我也会接受任何其他符合要求的本地化解决方案: 1. 无需重新部署应用程序就可以更改翻译 2. 开发人员在创建表单/用户控件时不应该关心翻译
谢谢你。
编辑:拉里提供了一本很好的参考书,其中的代码部分解决了我的问题。借助该帮助,我能够创建自己的组件,该组件替换 InitializeComponent 方法中的默认 ComponentResourceManager。这是一个名为 Localizer 的组件的代码,它用自定义的 MyResourceManager 替换了 ComponentResourceManager,以便其他人也可以从中受益:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.CodeDom;
using System.ComponentModel.Design;
namespace LocalizationTest
{
[Designer(typeof(LocalizerDesigner))]
[DesignerSerializer(typeof(LocalizerSerializer), typeof(CodeDomSerializer))]
public class Localizer : Component
{
public static void GetResourceManager(Type type, out ComponentResourceManager resourceManager)
{
resourceManager = new MyResourceManager(type);
}
}
public class MyResourceManager : ComponentResourceManager
{
public MyResourceManager(Type type) : base(type)
{
}
}
public class LocalizerSerializer : CodeDomSerializer
{
public override object Deserialize(IDesignerSerializationManager manager, object codeDomObject)
{
CodeDomSerializer baseSerializer = (CodeDomSerializer)
manager.GetSerializer(typeof(Component), typeof(CodeDomSerializer));
return baseSerializer.Deserialize(manager, codeDomObject);
}
public override object Serialize(IDesignerSerializationManager manager, object value)
{
CodeDomSerializer baseSerializer =
(CodeDomSerializer)manager.GetSerializer(typeof(Component), typeof(CodeDomSerializer));
object codeObject = baseSerializer.Serialize(manager, value);
if (codeObject is CodeStatementCollection)
{
CodeStatementCollection statements = (CodeStatementCollection)codeObject;
CodeTypeDeclaration classTypeDeclaration =
(CodeTypeDeclaration)manager.GetService(typeof(CodeTypeDeclaration));
CodeExpression typeofExpression = new CodeTypeOfExpression(classTypeDeclaration.Name);
CodeDirectionExpression outResourceExpression = new CodeDirectionExpression(
FieldDirection.Out, new CodeVariableReferenceExpression("resources"));
CodeExpression rightCodeExpression =
new CodeMethodInvokeExpression(new CodeTypeReferenceExpression("LocalizationTest.Localizer"), "GetResourceManager",
new CodeExpression[] { typeofExpression, outResourceExpression });
statements.Insert(0, new CodeExpressionStatement(rightCodeExpression));
}
return codeObject;
}
}
public class LocalizerDesigner : ComponentDesigner
{
public override void Initialize(IComponent c)
{
base.Initialize(c);
var dh = (IDesignerHost)GetService(typeof(IDesignerHost));
if (dh == null)
return;
var innerListProperty = dh.Container.Components.GetType().GetProperty("InnerList", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.FlattenHierarchy);
var innerList = innerListProperty.GetValue(dh.Container.Components, null) as System.Collections.ArrayList;
if (innerList == null)
return;
if (innerList.IndexOf(c) <= 1)
return;
innerList.Remove(c);
innerList.Insert(0, c);
}
}
}