2009-10-13 53 views
4

我问过以前只有一个答案的question。我已经有一段时间了解这一点,并制定了计划,但是如果这是一个好主意,需要一些反馈意见。这是可插拔组件本地化的一个很好的解决方案吗?

问题:

我想其中有一个名字(不变的,用于识别组件)将其名字中被消耗它的应用程序本地化的一个组成部分,没有与污染组件的模型DisplayName属性。该组件可能存在于单独的dll中,并可在运行时动态加载。

我的感觉是,组件dll应该负责提供本地化的名称(这看起来好像封装),但是使用组件的应用程序应该负责获取/使用本地化的名称(组件具有的事实用于显示目的不同的名称是不是组件的一个问题,但“查看”使用成分)

将溶液的:

具有相同名称添加资源的部件DLL作为该文件的组件类位于。使用作为组件名称的键向资源添加字符串。

在应用程序中获得的本地化名称,像这样:

ExternalObject obj = GetExternalObject();    
ResourceManager manager = new ResourceManager (obj.GetType()); 
string localisedName= manager.GetString (obj.Name); 

此代码可能会在航向类封装,但传达了观点。这似乎有效,但这是一个好主意,还是有更好/更标准的方法来做到这一点?

编辑:我应该指出,我不知道这个解决方案的一件事是资源必须在.resx文件中,该文件与类所在的文件具有相同的名称。使其工作,因为可以从类型名称中识别资源文件。这与表单的本地化似乎有效,并且使Visual Studio将.resx作为.cs文件的“子组件”,这看起来都很不错。但是,如果我尝试编辑这个文件,visual studio会抛出一个警告(关于编辑另一个项目的一部分的资源),这让我觉得也许还有其他一些方法,我应该这样做。

+0

要编辑rex,请使用内置的资源编辑器。但是这个警告应该是可以忽略的,AFAICT。 – psychotik 2009-10-13 20:22:23

+0

这是使用内置的编辑器,是的,它是可以忽略的,一切似乎工作正常。它只是觉得我正在做一些我不应该做的事情,当它试图保姆我这样。 – 2009-10-14 08:09:48

回答

1

我认为你有正确的想法,但有更好的方法来实现这一点。

推测,您有一个可插拔组件实现的接口。再说了,IPluggable:

interface IPluggable { 
    ... 
    string LocalizedName {get;} 
    ... 
} 

从你的主要二进制文件,加载可插入组装,并使用反射IPluggable实例(我假设是什么GetExternalObject()方法你必须做),然后使用LocalizedName属性访问的本地化名称。 Inside IPluggable实现中,创建一个ResourceManager并从该可插拔部件的resx访问LocalizedName

你在做什么是可插拔组件中行为的良好封装 - 它负责为你提供本地化名称,但它选择这样做,没有你的man程序假定可以创建ResourceManager来访问本地化名称。

+0

这是目前我遇到的情况,但正是我想摆脱的。我不希望我的模型包含LocalizedName属性,因为存在本地化名称的事实不是模型的关注点。你有什么可能是好的封装,但不是分离的担忧。想象一下,我有ITree代表一棵树。它有一个名字,这是树的拉丁名字。当向英文用户显示时,他们不应该关心他们希望看到与拉丁名称不同的名称。 – 2009-10-14 08:04:33

+0

我想保留封装(通过让资源存在于组件的dll中),并通过使用不变名称作为关键字来查看组件资源,使视图访问LocalisedName来拥有SOC。 – 2009-10-14 08:12:26

-1

您提出的方式存在的问题是,它很难更新翻译,甚至可能需要程序员。另外你如何在不更新整个应用程序的情况下更新翻译?

我已经做了很多翻译应用程序,而我所做的就是与格式化像这样translatations一个单独的文本文件:

[英]
DONE =完成

[挪威]
完成= Ferdig

,我有一个叫做TranslateForm(功能),我称之为形式显示事件中,塔t将翻译所有UI元素。该TranslateForm()函数将有事情像

buttonDone.Text = Translate.GetTranslation("Done"); 

与TranslateForm最后一部分是不是一个最佳的解决方案,我想随着时间的推移,我会迁移到一个解决方案,在控制自身调用翻译类。 使用这个系统的好处是,它对于程序员来说很简单,你可以有其他的PPL添加翻译,而不必在事后做手工工作(这对我来说是重要的,因为我有社区驱动的翻译),所以它们经常更新,我不想花时间在那。 我也可以在应用程序运行时更新翻译,而不必重新启动或更新应用程序。

+1

我不确定我了解你的顾虑。翻译是通过卫星组件完成的,并且不需要更新应用程序来提供新的翻译,只需提供一个新的卫星组件。这是标准的.net翻译方法。你的方法存在问题(虽然我承认这不适用于我的情况),你必须手工做所有翻译,并且b如果'完成'的文本不适合你创建的按钮怎么办?有些语言?使用标准方法,您可以提供新的按钮大小以及新的文本。 – 2009-10-13 12:37:57

+1

EKS,你的建议是原始的。 .NET有一些内置的支持来使用resx文件和卫星程序集,这些程序不需要运行新代码。对于更新/新的翻译,您只需发送资源二进制文件,它不是代码。 – psychotik 2009-10-13 20:21:13

+0

原始但工作。 就像我试图在我的文章中清理一样,这取决于你更新翻译的频率。世卫组织也会这样做,如果你雇人来做,我的方法可能不是那个人使用的方法。但如果你有一个社区驱动的翻译它的好,因为“任何人”都可以更新翻译。 – EKS 2009-10-14 08:18:52

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描述键和访问资源文件以获得本地化(属性描述必须匹配资源键:))

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客户端。希望它可以帮助你:)

相关问题