.NET WinForms本地化 - 替换ComponentResourceManager

Dal*_*pić 7 .net localization resourcemanager visual-studio-2010 winforms

在我当前的项目(.NET Windows Forms应用程序)中,我要求.NET窗体表单应该本地化,但是本地化元素(只是翻译,而不是图像或控件位置)应该来自数据库,以便启用最终用户可以根据需要修改控件可本地化属性(只是标题/文本).为了让开发人员不受本地化问题的影响,对我来说最好的解决方案就是在VS设计器中将表单标记为Localizable.这将把所有可本地化的属性值放在表单.resx文件中.

现在我的问题是如何从数据库提供翻译.在表单被标记为Localizable的那一刻,VS Forms设计器将放置可以本地化的所有内容都是表单.resx文件.设计人员还将修改标准的designer.cs InitializeComponent方法,以便它实例化ComponentResourceManager,然后使用该资源管理器加载对象(控件和组件)的可本地化属性.

我已经看到了一些解决方案,人们已经建立了自己的方法,将本地化属性应用于Form及其控件.我见过的所有解决方案通常归结为递归迭代Form的Controls集合及其控件并应用翻译.不幸的是,.NET Forms本地化并不是那么简单,并不能涵盖所有场景,特别是如果你有一些第三方控件.例如:

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"))});
Run Code Online (Sandbox Code Playgroud)

我见过的所有解决方案都无法进行上述组件所需的翻译.

由于VS已经生成了在需要的地方提供翻译的代码,我理想的解决方案是以某种方式用我自己的派生类替换ComponentResourceManager.如果我可以在InitializeComponent中替换以下行:

System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
Run Code Online (Sandbox Code Playgroud)

System.ComponentModel.ComponentResourceManager resources = new MyComponentResourceManager(typeof(Form1));
Run Code Online (Sandbox Code Playgroud)

然后我可以毫无问题地解决其他问题.

不幸的是我没有找到我怎么能做那样的事情所以我在这里问一个关于如何做到这一点的问题.

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);

        }
    }


}
Run Code Online (Sandbox Code Playgroud)

Lar*_*rry 7

我是Visual Studio开发人员的本地化工具的作者(为了完全公开).我强烈建议获取Guy Smith-Ferrier的书".NET国际化,构建全球Windows和Web应用程序的开发人员指南".我相信这是正确的书(肯定是正确的作者),但你需要检查,因为我看了很长时间(也许他甚至写了一些新的东西).Guy是MSFT MVP和本地化大师.在他的例子中,他通过创建一个可以拖动到每个表单的托盘区域的组件来向您展示如何完成您正在尝试的操作.然后,该组件将允许您将"ComponentResourceManager"替换为您自己的(其设计中涉及多个类).然后,您可以从任何来源(包括数据库)读取您的字符串.IIRC,他自己的例子甚至使用了DB.你可以在网上找到代码,而不必购买他的书,因为我认为他甚至可以在他自己的网站上提供它.您还可以在信誉良好的图书采购网站上找到他的书中的免费段落,因为即使您不使用他的技术(很难在其他地方找到),这些信息也是非常宝贵的.请注意,我曾经(很久以前)测试过他的代码,它的工作方式与广告一样.