【问题标题】:Localizing enum descriptions attributes本地化枚举描述属性
【发布时间】:2009-02-20 11:48:50
【问题描述】:

在 .net 中本地化枚举描述的最佳方法是什么?

(枚举描述示例见Adding descriptions to enumeration constants

理想情况下,我想要一些使用 ResourceManager 和资源文件的东西,这样它就可以适应应用程序的其他区域的本地化方式。

【问题讨论】:

    标签: c# .net localization enums


    【解决方案1】:

    这就是我最终的结果,我没有看到添加自定义属性类来保存资源键然后查找资源文件的价值 - 为什么不直接使用枚举类型名 + 值作为资源键?

    using System;
    using System.Resources;
    using System.Reflection;
    
    public class MyClass
    {
      enum SomeEnum {Small,Large};
    
      private ResourceManager _resources = new ResourceManager("MyClass.myResources",
                              System.Reflection.Assembly.GetExecutingAssembly());    
    
      public string EnumDescription(Enum enumerator)
      {     
        string rk = String.Format("{0}.{1}",enumerator.GetType(),enumerator);
        string localizedDescription = _resources.GetString(rk);
    
        if (localizedDescription == null)
           {
           // A localized string was not found so you can either just return
           // the enums value - most likely readable and a good fallback.
           return enumerator.ToString();
    
           // Or you can return the full resourceKey which will be helpful when
           // editing the resource files(e.g. MyClass+SomeEnum.Small) 
           // return resourceKey;
           }
        else
           return localizedDescription;
        }
    
    
      void SomeRoutine()
      {
        // Looks in resource file for a string matching the key
        // "MyClass+SomeEnum.Large"
        string s1 = EnumDescription(SomeEnum.Large);       
      }
    }
    

    【讨论】:

    • 枚举不是枚举数,是吗?它是一种枚举类型,但我认为枚举器是完全不同的东西......
    • 使用 C# 3.5,您可以将该方法设置为扩展方法,以便您可以使用 SomeEnum.Large.EnumDescription();
    • 在搜索另一个问题时偶然发现了这个问题。我只想提醒一下,使用类型和成员名称会使您的应用程序更难混淆(您必须从流程中排除合理的声明)。
    • @Ryan 如何调整此代码以在 Enum.GetValues(typeof(SomeEnum)) 上使用它?我希望能够将我的枚举用作数据源
    【解决方案2】:

    我的解决方案,使用本机解密属性:

    public class LocalizedEnumAttribute : DescriptionAttribute
    {
        private PropertyInfo _nameProperty;
        private Type _resourceType;
    
        public LocalizedEnumAttribute(string displayNameKey)
            : base(displayNameKey)
        {
    
        }
    
        public Type NameResourceType
        {
            get
            {
                return _resourceType;
            }
            set
            {
                _resourceType = value;
    
                _nameProperty = _resourceType.GetProperty(this.Description, BindingFlags.Static | BindingFlags.Public);
            }
        }
    
        public override string Description
        {
            get
            {
                //check if nameProperty is null and return original display name value
                if (_nameProperty == null)
                {
                    return base.Description;
                }
    
                return (string)_nameProperty.GetValue(_nameProperty.DeclaringType, null);
            }
        }
    }
    
    public static class EnumExtender
    {
        public static string GetLocalizedDescription(this Enum @enum)
        {
            if (@enum == null)
                return null;
    
            string description = @enum.ToString();
    
            FieldInfo fieldInfo = @enum.GetType().GetField(description);
            DescriptionAttribute[] attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
    
            if (attributes.Any())
                return attributes[0].Description;
    
            return description;
        }
    }
    

    枚举声明

    public enum MyEnum
    {
        [LocalizedEnum("ResourceName", NameResourceType = typeof(ResourceType))]
        Test = 0
    }
    

    然后拨打MyEnumInstance.GetLocalizedDescription()

    【讨论】:

    • 需要 BindingFlags.NonPublic 并且需要访问 NameResourceType.set 中的 base.Description。请参阅我对标志枚举支持的回答。
    • 这对我来说似乎比其他解决方案更可重用。谢谢
    • 我必须将 NameResourceType 更改为 typeof(Resource) 才能使其正常工作。谢谢!
    【解决方案3】:

    有一个简单的解决方案: 使用 LocalizedDescription 属性传递资源键。

        [Serializable]
        public class LocalizableDescriptionAttribute:DescriptionAttribute
        {
            public LocalizableDescriptionAttribute(string resourceKey)
                :base(Resources.ResourceManager.GetString(resourceKey))
            { }
    
        }
    

    【讨论】:

    • 我不确定使用枚举完整类型名称 (namespace.enumname.value) 作为 resourceKey 会让您感到困惑。这似乎是一个额外的不必要的步骤(使用反射来获取 LocalizableDescriptionAttribute)而不是仅仅去 resourceManager - 我错过了什么?
    • 我认为我们有误会。我使用我的属性来本地化枚举值描述。我猜你说的是本地化枚举而不是它们的值。
    • 你不能用 Description 属性装饰枚举值并传递资源键。
    • 不——我想让枚举值保持不变,只需本地化描述。我自己在下面的回答可能会使我的评论更清楚。 (顺便说一句 - 忽略我所说的反射,我应该说 LocalizableDescriptionAttribute.GetCustomAttribute)
    【解决方案4】:

    我曾经做过的一种方法是在与枚举相同的命名空间中添加一个扩展方法,该方法返回一个字符串。就我而言,它只是硬编码的,但从资源文件中获取它们是没有问题的。

        public static string Describe(this SomeEnum e)
        {
            switch(e)
            {
                SomeEnum.A:
                    return "Some text from resourcefile";
                SomeEnum.B:
                    return "Some other text from resourcefile";
                ...:
                    return ...;
            }
        }
    

    也许不是一个非常顺利或花哨的解决方案,但它确实有效 =)

    【讨论】:

    • + 1 用于使用扩展方法...虽然我更喜欢使用枚举的完全限定类型名称作为资源键(请参阅 Ryans 答案)
    • @SDX2000:是的,这可能是一个非常好的获取字符串的替代方法。
    【解决方案5】:

    将@nairik 的方法替换为以下内容以添加对标志枚举的支持。

    public static string GetLocalizedDescription(this Enum @enum)
    {
        if ( @enum == null )
            return null;
    
        StringBuilder sbRet = new StringBuilder();
    
        string description = @enum.ToString();
    
        var fields = description.Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);
    
        foreach ( var field in fields )
        {
            FieldInfo fieldInfo = @enum.GetType().GetField(field);
            DescriptionAttribute[] attributes = ( DescriptionAttribute[] )fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
    
            if ( attributes.Any() )
                sbRet.AppendFormat("{0}, ", attributes[0].Description);
            else
                sbRet.AppendFormat("{0}, ", field);
        }
    
        if ( sbRet.Length > 2 )
            sbRet.Remove(sbRet.Length - 2, 2);
    
        return sbRet.ToString();
    }
    

    并替换属性中的NameResourceType:

    public Type NameResourceType
    {
        get
        {
            return _resourceType;
        }
        set
        {
            _resourceType = value;
    
            _nameProperty = _resourceType.GetProperty(base.Description, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
        }
    }
    

    【讨论】:

      【解决方案6】:

      在这个问题中查看我的表格示例:

      Localisation/I18n of database data in LINQ to SQL

      状态类型表映射到枚举值。真正的好处是您可以在报告和应用程序中进行本地化,并指定外部 ID 以与不想要您的内部值等的第三方集成。它将枚举描述与其值分离。

      【讨论】:

      • 以 db 为中心的应用程序的好方法,但对于我的需求来说确实有点过头了。
      【解决方案7】:

      您不能应用多个 System.ComponentModel.DescriptionAttribute(因此该选项已失效)。

      所以添加一个间接级别,描述保存一个资源名称,然后在资源中使用本地化支持。显然,枚举的用户需要调用您的辅助方法来执行此操作。

      【讨论】:

      • 这是 Valentin Vasiliev 提出的,但不需要使用辅助方法——同样的评论也适用。
      【解决方案8】:
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-05-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-07
      • 2018-11-19
      相关资源
      最近更新 更多