8

在我当前的项目(.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);

        }
    }


}
4

2 回答 2

7

I'm the author of a localization tool for Visual Studio developers (in the interest of full disclosure). I strongly suggest getting hold of a copy of Guy Smith-Ferrier's book, ".NET Internationalization, The Developer's Guide to Building Global Windows and Web Applications". I believe that's the correct book anyway (correct author for sure), but you'll need to check, since it's been a long time since I looked at it (and maybe he's even written something newer since then). Guy is a MSFT MVP and localization guru. He shows you how to do exactly what you're attempting, in his case, by creating a component that you can drag onto the tray area of each of your forms. The component will then allow you to replace the "ComponentResourceManager" with your own (there are several classes involved in his design). You can then read your strings from any source including a DB. IIRC, his own example even uses a DB. You can probably find the code online without having to purchase his book, since I think he may even provide it on his own site. You can also find free passages from his book on reputable book purchasing sites, since the info is invaluable even if you don't use his techniques (very hard to find elsewhere). Note that I tested his code once upon a time (a long time ago), and it works as advertised.

于 2013-03-05T16:54:07.317 回答
2

我是这样替换的:

public class CustomCodeDomSerializer : CodeDomSerializer
{
    public override object Serialize(IDesignerSerializationManager manager, object value)
    {
        for (var i = 0; manager.Context[i] != null; i++)
        {
            var collection = manager.Context[i] as CodeStatementCollection;
            if (collection != null)
            {
                foreach (var statement in collection)
                {
                    var st = statement as CodeVariableDeclarationStatement;
                    if (st?.Type.BaseType == typeof(ComponentResourceManager).FullName)
                    {
                        var ctr = new CodeTypeReference(typeof(CustomComponentResourceManager));
                        st.Type = ctr;
                        st.InitExpression = new CodeObjectCreateExpression(ctr, new CodeTypeOfExpression(manager.GetName(value)));
                    }
                }
            }
        }
        var baseClassSerializer = (CodeDomSerializer)manager.GetSerializer(value.GetType().BaseType, typeof(CodeDomSerializer));
        var codeObject = baseClassSerializer.Serialize(manager, value);
        return codeObject;
    }
}

[DesignerSerializer(typeof(CustomCodeDomSerializer), typeof(CodeDomSerializer))]
public class CustomUserControl : UserControl { }

然后视图应该继承CustomUserControl而不是UserControl(或CustomForm : Form):

public partial class SomeView : CustomUserControl { ... }

然后生成生成的设计器文件:

private void InitializeComponent()
{
    CustomComponentResourceManager resources = new CustomComponentResourceManager(typeof(SomeView));
    ...
}
于 2018-03-23T11:24:30.507 回答