現在のプロジェクト(.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);
}
}
}