这是可插拔组件本地化的好解决方案吗?

时间:2009-10-13 11:45:15

标签: .net localization internationalization pluggable

我之前问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>

3 个答案:

答案 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类的解决方案。 使用这个系统的优点是它对于程序员来说很简单,你可以让其他人添加翻译,而不必事后做手工工作(这对我来说很重要,因为我有社区驱动的翻译),所以他们经常更新,我不想花时间在那上面。 我还可以在应用程序运行时更新翻译,而无需重新启动或更新应用程序。