Nel mio progetto corrente (applicazione Windows Form .NET) ho bisogno che i moduli di Windows .NET siano localizzati ma gli elementi di localizzazione (solo le traduzioni, non le immagini o le posizioni di controllo) dovrebbe provenire dal database per consentire agli utenti finali di modificare le proprietà localizzabili di controllo (solo la didascalia/testo) come desiderano. Per mantenere gli sviluppatori alleggeriti dai problemi di localizzazione, la soluzione migliore per me sarebbe semplicemente contrassegnare il modulo come localizzabile nel designer VS. Ciò inserirà tutti i valori di proprietà localizzabili nel file .resx di moduli.Localizzazione .NET WinForms - sostituzione ComponentResourceManager
Ora il mio problema è come fornire le traduzioni dal database. Nel momento in cui il modulo viene contrassegnato come Localizzabile, il designer VS Form posizionerà tutto ciò che può essere localizzato in un file .resx. Il progettista modificherà anche il metodo standard designer.cs InitializeComponent in modo da creare un'istanza di ComponentResourceManager e quindi utilizzare tale gestore risorse per caricare le proprietà localizzabili degli oggetti (controlli e componenti).
Ho visto soluzioni in cui le persone hanno creato il proprio metodo per applicare le proprietà localizzate a Form e ai suoi controlli. Tutte le soluzioni che ho visto di solito si riducono a iterazione ricorsiva attraverso la raccolta Controls di Form e dei suoi controlli e l'applicazione delle traduzioni. Sfortunatamente la localizzazione di .NET Form non è così semplice e questo non copre tutti gli scenari, specialmente se si dispone di alcuni controlli di terze parti. Ad esempio:
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"))});
Tutte le soluzioni che ho visto non potevano eseguire traduzioni come richiesto dai componenti di cui sopra.
Poiché il VS ha già generato il codice che fornisce la traduzione dove necessario, la mia soluzione ideale sarebbe in qualche modo sostituire ComponentResourceManager con la mia classe derivata. Se solo potessi sostituire la seguente riga in InitializeComponent:
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
con
System.ComponentModel.ComponentResourceManager resources = new MyComponentResourceManager(typeof(Form1));
quindi ho potuto risolvere tutto il resto senza problemi.
Purtroppo non ho trovato come avrei potuto fare una cosa del genere, quindi eccomi qui a fare una domanda su come potrebbe essere fatto.
P.S. Vorrei anche accettare qualsiasi altra soluzione di localizzazione che soddisfa i requisiti: 1. Cambiare le traduzioni dovrebbero essere possibili senza ridistribuire l'applicazione 2. Lo sviluppatore non dovrebbe prendere cura di traduzione per la creazione di forme/utente controlla
Grazie.
EDIT: Larry ha fornito un ottimo riferimento a un libro che ha un codice che in parte risolve il mio problema. Con questo aiuto sono stato in grado di creare il mio componente che sostituisce il ComponentResourceManager predefinito nel metodo InitializeComponent. Qui è il codice per un componente chiamato localizzatore che sostituisce la ComponentResourceManager con MyResourceManager personalizzato in modo che qualcun altro può anche trarre beneficio da esso:
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);
}
}
}
Ho trovato ulteriori spiegazioni su come funziona Localizer qui: http://flylib.com/books/en/3.147.1.147/1/ –