我之前问question只有一个答案。我现在有时间玩这个并制定计划,但想要一些好主意的反馈意见。
问题:
我想要一个具有名称(不变量,用于标识组件)的组件,使其名称本地化在正在使用它的应用程序中,而不会使用DisplayName属性污染组件的模型。该组件可以存在于单独的dll中,并在运行时动态加载。
我的感觉是组件dll应该负责提供本地化名称(这看起来像是很好的封装),但是使用组件的应用程序应该负责获取/使用本地化名称(组件具有用于显示目的的不同名称不是组件的关注点,而是使用该组件的“视图”
解决方案:
将资源添加到组件dll,其名称与组件类所在的文件同名。使用键作为组件名称向字符串添加字符串。
在应用程序中获取本地化名称,如下所示:
ExternalObject obj = GetExternalObject ();
ResourceManager manager = new ResourceManager (obj.GetType ());
string localisedName= manager.GetString (obj.Name);
这段代码可能会封装在Localiser类中,但传达了这一点。这似乎有用,但这是一个好主意,还是有更好/更标准的方法来做到这一点?
编辑:我应该指出,我对此解决方案不确定的一件事是资源必须位于.resx文件中,该文件与该类所在的文件具有相同的名称。这使得它可以工作,因为可以从类型名称中识别资源文件。这与表单的本地化似乎有效,并使visual studio将.resx作为.cs文件的“子组件”,这看起来都很好。但是,如果我尝试编辑这个文件,visual studio会抛出一个警告(关于编辑属于另一个项目项的资源),这让我觉得也许还有其他一些方法我应该这样做。 / p>答案 0 :(得分:1)
我认为你有正确的想法,但有更好的方法来实现这一目标。
据推测,您有一个可插拔组件实现的接口。说,IPluggable:
interface IPluggable {
...
string LocalizedName {get;}
...
}
从您的主二进制文件中,加载可插入程序集并使用反射创建IPluggable实例(我假设您使用的是GetExternalObject()
方法),然后使用LocalizedName
属性访问本地化名称。 Inside IPluggable实现,创建一个ResourceManager
并从该可插入程序集的resx访问LocalizedName
。
你所做的是对可插拔程序集中的行为的良好封装 - 它负责为你提供本地化的名称,但是它选择这样做,而你的man程序假设可以创建ResourceManager
访问本地化名称。
答案 1 :(得分:0)
前段时间我有一个问题是本地化枚举值,我不确定它是否能回答你的问题,但至少会给你另一种方法。
首先创建我自己的本地化属性
/// <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描述键并访问Resource文件以获得本地化(属性描述必须与资源键匹配:))
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;
}
}
}
我最初posted this solution on my blog。希望它可以帮助你:))
答案 2 :(得分:-1)
你所建议的方式的问题是它很难更新翻译,甚至可能需要程序员。另外,如何在不更新整个应用程序的情况下更新翻译?
我已经完成了很多已翻译的应用程序,我所做的是有一个单独的文本文件,翻译形成如下:
[英文版]
DONE =完成[挪威]
做= Ferdig
我有一个名为TranslateForm()的函数,我在Form Show事件中调用,它将转换所有UI元素。 TranslateForm()函数将具有类似
的内容buttonDone.Text = Translate.GetTranslation("Done");
使用TranslateForm的最后一部分不是最佳解决方案,我认为随着时间的推移,我将迁移到控件本身调用Translate类的解决方案。 使用这个系统的优点是它对于程序员来说很简单,你可以让其他人添加翻译,而不必事后做手工工作(这对我来说很重要,因为我有社区驱动的翻译),所以他们经常更新,我不想花时间在那上面。 我还可以在应用程序运行时更新翻译,而无需重新启动或更新应用程序。