W moim bieżącym projekcie (aplikacja .NET Windows Forms) mam wymóg, aby formularze okien .NET były zlokalizowane, ale elementy lokalizacyjne (tylko tłumaczenia, a nie obrazy lub pozycje kontrolne) powinien pochodzić z bazy danych, aby umożliwić użytkownikom końcowym modyfikowanie właściwości kontrolowanych lokalizacji (tylko podpis/tekst) zgodnie z własnym życzeniem. Aby deweloperzy nie obciążali problemów związanych z lokalizacją, najlepszym rozwiązaniem byłoby po prostu oznaczenie formularza jako Lokalizowalne w projektancie VS. Spowoduje to umieszczenie wszystkich wartości właściwości lokalizowalnych w pliku .resx formularzy..NET Lokalizacja WinForms - zastępowanie ComponentResourceManager
Teraz mój problem polega na tłumaczeniu z bazy danych. W momencie, gdy formularz zostanie oznaczony jako Localizable, projektant formularzy VS umieści wszystko, co może być zlokalizowane, w pliku .resx w postaci. Projektant zmodyfikuje również standardową metodę designer.cs InitializeComponent, aby utworzyć instancję ComponentResourceManager, a następnie użyć tego menedżera zasobów, aby załadować lokalizowalne właściwości obiektów (formantów i komponentów).
Widziałem rozwiązania, w których ludzie stworzyli własną metodę zastosowania zlokalizowanych właściwości do formularza i jego kontroli. Wszystkie rozwiązania, które widziałem zwykle sprowadzają się do rekursywnej iteracji poprzez kolekcję Formantów i jej kontroli oraz stosowania tłumaczeń. Niestety lokalizacja .NET Forms nie jest tak prosta i nie obejmuje wszystkich scenariuszy, szczególnie jeśli masz jakieś kontrole innych firm. Na przykład:
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"))});
Wszystkie rozwiązania, które widziałem, nie mogą wykonywać tłumaczeń wymaganych przez powyższe składniki.
Ponieważ VS już wygenerował kod, który zapewnia tłumaczenie w razie potrzeby, moim idealnym rozwiązaniem byłoby jakoś zastąpić ComponentResourceManager moją własną klasą pochodną. Gdybym tylko mógł zastąpić następującą linię w InitializeComponent:
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
z
System.ComponentModel.ComponentResourceManager resources = new MyComponentResourceManager(typeof(Form1));
wtedy mogę rozwiązać wszystko inne bez żadnych problemów.
Niestety nie znalazłem sposobu, w jaki mogłem zrobić coś takiego, więc tutaj jestem na ZADZEJ, zadając pytanie, jak to zrobić.
P.S. Chciałbym również przyjąć innego rozwiązania lokalizacyjne, które odpowiada wymaganiom: 1. Zmiana tłumaczenia powinno być możliwe bez przesunięcia wniosku 2. deweloper nie powinien dbać o tłumaczeniu podczas tworzenia formularzy/użytkownik kontroluje
Dziękuję.
EDYCJA: Larry podał świetne odniesienie do książki, która ma kod, który częściowo rozwiązuje mój problem. Dzięki tej pomocy udało mi się stworzyć własny komponent, który zastępuje domyślny ComponentResourceManager w metodzie InitializeComponent. Oto kod dla składnika o nazwie Lokalizator który zastępuje ComponentResourceManager z niestandardowym MyResourceManager tak, że ktoś inny może z niego korzystać:
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);
}
}
}
Znalazłem więcej wyjaśnień, jak Localizer działa tutaj: http://flylib.com/books/en/3.147.1.147/1/ –