Is this a good solution for localisation of pluggable components?

亡梦爱人 提交于 2019-12-05 19:04:49

I think you have the right idea, but there's better way to accomplish this.

Presumably, you have an interface that the pluggable component implements. Say, IPluggable:

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

From your main binary, load the pluggable assembly and create the IPluggable instance using reflection (I assume that's what the GetExternalObject() method you have does) and then access the localized name using the LocalizedName property. Inside the IPluggable implementation, create a ResourceManager and access the LocalizedName from the resx of that pluggable assembly.

What you get by doing is good encapsulation of behavior in the pluggable assembly - it is responsible for providing you the localized name, however it chooses to do it, without your man program assuming that a ResourceManager can be created to access a localized name.

I had a problem some time ago of localizing enum values, I'm not sure if it answers you question, but at least gives you another approach to have in mind.

Started by creating my own Localizing attribute

/// <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;
        }
    }
}

From there I create the enum itself

[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
}

Then I created a type converter which will fetch the LocalizationAttribute description key and access the Resource file to get the localization (Attribute description must match the resource key :))

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);
        }
    }

Finally I created the client which uses the TypeConverter, which in this case is a collection

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;
        }
    }
}

I originally posted this solution on my blog. Hope it helps you out :)

The problem with the way you have suggested is that its going to be hard to update the translations, and it may even require a programmer. Also how are you going to update translations without updating the entire applications?

I have done alot of translated applications, and what i have done is have a seperate text file with translatations formated something like this:

[English]
Done=Done

[Norwegian]
Done=Ferdig

And i have a function called TranslateForm() that i call inside Form Show event, that will translate all the UI elements. The TranslateForm() function will have things like

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

The last part with the TranslateForm is not a optimal solution, i think over time i will migrate to a solution that where the control itself calls the Translate class. The advantage of using this system is that its simple for the programer, you can have other ppl add translations without you having to do manual work afterwards ( This is importent to me as i have community driven translations ), so they are updated often and i dont want to spend time on that. I can also update the translations while the application is running, without the application having to restart or be updated.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!