Является ли это хорошим решением для локализации подключаемых компонентов?
-
21-09-2019 - |
Вопрос
Я спросил у вопрос ранее на который был только один ответ.Сейчас у меня было некоторое время, чтобы поиграть с этим, и у меня есть план, но я хочу получить отзывы о том, хорошая ли это идея.
В чем проблема:
Я хочу, чтобы компонент, у которого есть имя (инвариантное, используемое для идентификации компонента), чтобы его имя было локализовано в приложении, которое его использует, без загрязнения модели компонента атрибутом DisplayName.Компонент может существовать в отдельной библиотеке dll и динамически загружаться во время выполнения.
мне кажется, что библиотека dll компонента должна отвечать за предоставление локализованного имени (это кажется хорошей инкапсуляцией), но приложение, использующее компонент, должно отвечать за получение / использование локализованного имени (тот факт, что компонент имеет другое имя для целей отображения, касается не компонента, а "представления", использующего этот компонент)
Решение:
Добавьте ресурс в библиотеку dll components с тем же именем, что и файл, в котором находится класс component.Добавьте к ресурсу строку с ключом, который является именем компонента.
В приложении получите локализованное имя следующим образом:
ExternalObject obj = GetExternalObject ();
ResourceManager manager = new ResourceManager (obj.GetType ());
string localisedName= manager.GetString (obj.Name);
Этот код, вероятно, будет инкапсулирован в класс локализатора, но передает суть.Кажется, это работает, но хорошая ли это идея, или есть лучший / более стандартный способ сделать это?
Редактировать:Я должен отметить, что одна вещь, в которой я не уверен в этом решении, заключается в том, что ресурсы должны находиться в файле .resx, который имеет то же имя, что и файл, в котором находится класс.Это заставляет его работать, так как файл ресурсов можно идентифицировать по имени типа.Это то же самое, что локализация для форм, похоже, работает, и заставляет visual studio помещать .resx в качестве "подкомпонента" файла .cs, что все кажется приятным.Но затем Visual Studio выдает предупреждение (о редактировании ресурса, являющегося частью другого элемента проекта), если я попытаюсь отредактировать этот файл, что заставляет меня думать, что, возможно, есть какой-то другой способ, которым я должен это делать.
Решение
Я думаю, у вас правильная идея, но есть лучший способ добиться этого.
Предположительно, у вас есть интерфейс, который реализует подключаемый компонент.Скажем, IPluggable:
interface IPluggable {
...
string LocalizedName {get;}
...
}
Из вашего основного двоичного файла загрузите подключаемую сборку и создайте экземпляр IPluggable, используя отражение (я предполагаю, что это то, что GetExternalObject()
метод, который у вас есть), а затем получите доступ к локализованному имени, используя LocalizedName
собственность. Внутри реализация ippluggable, создайте ResourceManager
и получить доступ к LocalizedName
из resx этой подключаемой сборки.
То, что вы получаете, выполняя, - это хорошая инкапсуляция поведения в подключаемой сборке - она отвечает за предоставление вам локализованного имени, однако она решает это сделать, без того, чтобы ваша программа man предполагала, что ResourceManager
может быть создан для доступа к локализованному имени.
Другие советы
Некоторое время назад у меня возникла проблема с локализацией значений перечислений. Я не уверен, что это отвечает на ваш вопрос, но, по крайней мере, дает вам другой подход, который следует иметь в виду.
Начал с создания собственного атрибута локализации.
/// <SUMMARY>
/// Attribute used for localization. Description field should contain a reference to the Resource file for correct localization
/// </SUMMARY>
public class LocalizationAttribute : Attribute
{
public LocalizationAttribute(string description)
{
this._description = description;
}
private string _description;
/// <SUMMARY>
/// Used to reference a resource key
/// </SUMMARY>
public string Description
{
get
{
return this._description;
}
}
}
Оттуда я создаю само перечисление
[TypeConverter(typeof(EnumToLocalizedString))]
public enum ReviewReason
{
[LocalizationAttribute("ReviewReasonNewDocument")]
NewDocument = 1,
[LocalizationAttribute("ReviewReasonInternalAudit")]
InternalAudit = 2,
[LocalizationAttribute("ReviewReasonExternalAudit")]
ExternalAudit = 3,
[LocalizationAttribute("ReviewReasonChangedWorkBehaviour")]
ChangedWorkBehaviour = 4,
[LocalizationAttribute("ReviewReasonChangedWorkBehaviourBecauseOfComplaints")]
ChangedWorkBehaviourBecauseOfComplaints = 5,
[LocalizationAttribute("ReviewReasonMovedFromOlderSystem")]
MovedFromOlderSystem = 6,
[LocalizationAttribute("ReviewReasonPeriodicUpdate")]
PeriodicUpdate = 7,
[LocalizationAttribute("ReviewReasonDocumentChanged")]
DocumentChanged = 8
}
Затем я создал преобразователь типов, который будет получать ключ описания LocalizationAttribute и получать доступ к файлу ресурсов для получения локализации (описание атрибута должно соответствовать ключу ресурса :))
public class EnumToLocalizedString : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return (sourceType.Equals(typeof(Enum)));
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return (destinationType.Equals(typeof(String)));
}
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
return base.ConvertFrom(context, culture, value);
}
public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
{
if (!destinationType.Equals(typeof(String)))
{
throw new ArgumentException("Can only convert to string.", "destinationType");
}
if (!value.GetType().BaseType.Equals(typeof(Enum)))
{
throw new ArgumentException("Can only convert an instance of enum.", "value");
}
string name = value.ToString();
object[] attrs = value.GetType().GetField(name).GetCustomAttributes(typeof(LocalizationAttribute), false);
if (attrs.Length != 1 !(attrs[0] is LocalizationAttribute))
{
throw new ArgumentException("Invalid enum argument");
}
return Handbok.Code.Resources.handbok.ResourceManager.GetString(((LocalizationAttribute)attrs[0]).Description);
}
}
Наконец, я создал клиент, который использует TypeConverter, который в данном случае представляет собой коллекцию
public class ReviewReasonCollection
{
private static Collection<KEYVALUEPAIR<REVIEWREASON,>> _reviewReasons;
public static Collection<KEYVALUEPAIR<REVIEWREASON,>> AllReviewReasons
{
get
{
if (_reviewReasons == null)
{
_reviewReasons = new Collection<KEYVALUEPAIR<REVIEWREASON,>>();
TypeConverter t = TypeDescriptor.GetConverter(typeof(ReviewReason));
foreach (ReviewReason reviewReason in Enum.GetValues(typeof(ReviewReason)))
{
_reviewReasons.Add(new KeyValuePair<REVIEWREASON,>(reviewReason, t.ConvertToString(reviewReason)));
}
}
return _reviewReasons;
}
}
}
Я изначально разместил это решение в своем блоге.Надеюсь, это вам поможет :)
Проблема с предложенным вами способом заключается в том, что обновить переводы будет сложно, и для этого может даже потребоваться программист.И как вы собираетесь обновлять переводы, не обновляя все приложения?
Я сделал много переведенных приложений, и у меня есть отдельный текстовый файл с переводами, отформатированный примерно так:
[Английский]
Готово=Готово[Норвежский язык]
Done=Фердиг
И у меня есть функция TranslateForm(), которую я вызываю внутри события Form Show, которая будет переводить все элементы пользовательского интерфейса.Функция TranslateForm() будет иметь такие вещи, как
buttonDone.Text = Translate.GetTranslation("Done");
Последняя часть с TranslateForm не является оптимальным решением, я думаю, что со временем я перейду к решению, в котором сам элемент управления вызывает класс Translate.Преимущество использования этой системы заключается в том, что она проста для программиста: вы можете попросить других людей добавлять переводы без необходимости впоследствии выполнять ручную работу (это важно для меня, поскольку у меня есть переводы, управляемые сообществом), поэтому они часто обновляются, и я не хочу тратить на это время.Я также могу обновлять переводы во время работы приложения без необходимости его перезапуска или обновления.