8

現在のプロジェクト(.NET Windowsフォームアプリケーション)では、.NET Windowsフォームをローカライズする必要がありますが、ローカリゼーション要素(画像やコントロールの位置ではなく、翻訳のみ)をデータベースから取得して、エンドユーザーは、コントロールのローカライズ可能なプロパティ(キャプション/テキストのみ)を必要に応じて変更できます。開発者にローカリゼーションの問題を負わせないようにするための最善の解決策は、VSデザイナーでフォームをローカライズ可能としてマークすることです。これにより、ローカライズ可能なすべてのプロパティ値がフォームの.resxファイルに配置されます。

今私の問題は、データベースから翻訳を提供する方法です。フォームがローカライズ可能としてマークされた瞬間、VSフォームデザイナはローカライズ可能なすべてのものをforms.resxファイルに配置します。デザイナは、標準のdesigner.cs InitializeComponentメソッドも変更して、ComponentResourceManagerをインスタンス化し、そのリソースマネージャを使用して、オブジェクト(コントロールとコンポーネント)のローカライズ可能なプロパティをロードします。

ローカライズされたプロパティをフォームとそのコントロールに適用する独自の方法を人々が構築するソリューションを見てきました。私が見たすべてのソリューションは、通常、FormとそのコントロールのControlsコレクションを再帰的に反復し、翻訳を適用することになります。残念ながら、.NETフォームのローカリゼーションはそれほど単純ではなく、特にサードパーティのコントロールがある場合、これはすべてのシナリオを網羅しているわけではありません。例えば:

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と呼ばれるコンポーネントのコードを次に示します。これは、ComponentResourceManagerをカスタムMyResourceManagerに置き換えて、他の誰かもその恩恵を受けることができるようにします。

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

私はVisualStudio開発者向けのローカリゼーションツールの作成者です(完全な開示のため)。ガイ・スミス・フェリエの著書「.NET Internationalization、グローバルWindowsおよびWebアプリケーションを構築するための開発者ガイド」のコピーを入手することを強くお勧めします。とにかくそれは正しい本だと思いますが(確かに正しい著者です)、私がそれを見てから長い時間が経っているので、チェックする必要があります(そしておそらく彼はそれ以来何か新しいものを書いています)。Guyは、MSFTMVPおよびローカリゼーションの第一人者です。彼は、各フォームのトレイ領域にドラッグできるコンポーネントを作成することにより、あなたが試みていることを正確に実行する方法を示しています。コンポーネントにより、「ComponentResourceManager」を置き換えることができます あなた自身で(彼のデザインに関係するいくつかのクラスがあります)。その後、DBを含む任意のソースから文字列を読み取ることができます。IIRC、彼自身の例ではDBも使用しています。彼は自分のサイトでコードを提供しているかもしれないので、彼の本を購入しなくてもオンラインでコードを見つけることができるでしょう。彼のテクニックを使わなくても情報は非常に貴重であるため、評判の良い本の購入サイトで彼の本から無料の文章を見つけることもできます(他の場所で見つけるのは非常に困難です)。私は彼のコードを昔々(ずっと前に)テストしましたが、宣伝どおりに機能することに注意してください。彼のテクニックを使わなくても情報は非常に貴重であるため、評判の良い本の購入サイトで彼の本から無料の文章を見つけることもできます(他の場所で見つけるのは非常に困難です)。私は彼のコードを昔々(ずっと前に)テストしましたが、宣伝どおりに機能することに注意してください。彼のテクニックを使わなくても情報は非常に貴重であるため、評判の良い本の購入サイトで彼の本から無料の文章を見つけることもできます(他の場所で見つけるのは非常に困難です)。私は彼のコードを昔々(ずっと前に)テストしましたが、宣伝どおりに機能することに注意してください。

于 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の代わりに継承する必要があります:UserControlCustomForm : Form

public partial class SomeView : CustomUserControl { ... }

そして、生成されたデザイナーファイルになります:

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