4

我之前问过一个只有一个答案的问题。我现在有一些时间来解决这个问题并制定了一个计划,但希望得到一些反馈,看看这是否是一个好主意。

问题:

我希望一个具有名称(不变,用于标识组件)的组件在使用它的应用程序中本地化其名称,而不会使用 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 会抛出一个警告(关于编辑作为另一个项目项的一部分的资源),这让我认为也许我应该这样做的其他方式。

4

3 回答 3

1

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

大概,您有​​一个可插入组件实现的接口。说,IPluggable:

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

从您的主二进制文件中,加载可插入程序集并使用反射创建 IPluggable 实例(我假设这就是GetExternalObject()您所做的方法),然后使用LocalizedName属性访问本地化名称。IPluggable 实现中,创建一个ResourceManagerLocalizedName从该可插入程序集的 resx 访问。

您所做的是对可插入程序集中的行为进行了良好封装 - 它负责为您提供本地化名称,但它选择这样做,而不需要您的 man 程序假设ResourceManager可以创建 a 来访问本地化名称。

于 2009-10-13T20:29:34.213 回答
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;
        }
    }
}

我最初在我的博客上发布了这个解决方案。希望它可以帮助你:)

于 2009-10-13T13:10:57.133 回答
-1

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

我做了很多翻译的应用程序,我所做的是有一个单独的文本文件,其翻译格式如下:

[英文]
完成=完成

[挪威语]
完成=Ferdig

我有一个名为 TranslateForm() 的函数,我在 Form Show 事件中调用它,它将翻译所有 UI 元素。TranslateForm() 函数将具有以下内容

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

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

于 2009-10-13T12:27:14.577 回答