64

ListBoxFor对于可能有多个选项的情况,您如何本地化枚举?

例如一个enum包含角色的:

public enum RoleType
{
    [Display(Description = "Administrator", ResourceType = typeof(Resource))]
    Administrator = 1,
    [Display(Description = "Moderator", ResourceType = typeof(Resource))]
    Moderator = 2,
    [Display(Description = "Webmaster", ResourceType = typeof(Resource))]
    Webmaster = 3,
    [Display(Description = "Guest", ResourceType = typeof(Resource))]
    Guest = 4,
    Etc.... = 5,
}

我已经看到用dropdownlist/完成了selectlists。但是有没有办法为多选列表做到这一点?

[编辑]

这就是我想使用它的方式,这就是它现在的工作方式,但没有被翻译成其他语言:

var roles = from role r in Enum.GetValues(typeof(RoleType))
            select new
            {
               Id = (int)Enum.Parse(typeof(RoleType), r.ToString()),
               Name = r.ToString()
            };

searchModel.roles = new MultiSelectList(roles, "Id", "Name");

注意:我已将枚举从 Role 重命名为 RoleType。

4

15 回答 15

86

您可以实现描述属性。

public class LocalizedDescriptionAttribute : DescriptionAttribute
{
     private readonly string _resourceKey;
    private readonly ResourceManager _resource;
    public LocalizedDescriptionAttribute(string resourceKey, Type resourceType)
    {
        _resource = new ResourceManager(resourceType);
        _resourceKey = resourceKey;
    }

    public override string Description
    {
        get
        {
            string displayName = _resource.GetString(_resourceKey);

            return string.IsNullOrEmpty(displayName)
                ? string.Format("[[{0}]]", _resourceKey)
                : displayName;
        }
    }
}

public static class EnumExtensions
{
    public static string GetDescription(this Enum enumValue) 
    {
        FieldInfo fi = enumValue.GetType().GetField(enumValue.ToString());

        DescriptionAttribute[] attributes =
            (DescriptionAttribute[])fi.GetCustomAttributes(
            typeof(DescriptionAttribute),
            false);

        if (attributes != null &&
            attributes.Length > 0)
            return attributes[0].Description;
        else
            return enumValue.ToString();
    }
}

像这样定义它:

public enum Roles
{
    [LocalizedDescription("Administrator", typeof(Resource))]
    Administrator,
...
}

并像这样使用它:

var roles = from RoleType role in Enum.GetValues(typeof(RoleType))
                    select new
                    {
                        Id = (int)role,
                        Name = role.GetDescription()
                    };
 searchModel.roles = new MultiSelectList(roles, "Id", "Name");
于 2013-06-29T14:22:40.203 回答
36

我通过创建在我的视图中使用的 EnumExtension 解决了这个问题。此扩展查找名为“EnumResources.resx”的资源文件,并通过以下命名约定 {Name of EnumType}_{Value of enum passed in} 查找资源。如果缺少资源键,它将显示封装在双括号 [[EnumValue]] 内的资源值。这样很容易在您的视图中找到“未翻译的”枚举。如果您在重命名后忘记更新资源文件,这也有助于提醒您。

public static class EnumExtensions
{
    public static string GetDisplayName(this Enum e)
    {
        var rm = new ResourceManager(typeof (EnumResources));
        var resourceDisplayName = rm.GetString(e.GetType().Name + "_" + e);

        return string.IsNullOrWhiteSpace(resourceDisplayName) ? string.Format("[[{0}]]", e) : resourceDisplayName;
    }
}

资源文件如下所示: 资源文件

用法:

<div>@ContractStatus.Created.GetDisplayName()</div>
于 2014-03-19T12:57:06.763 回答
14

2019 年更新

现在它很简单,设置你的枚举:

public enum ContactOptionType
{
    [Display(Description = "ContactOption1", ResourceType = typeof(Globalization.Contact))]
    Demo = 1,

    [Display(Description = "ContactOption2", ResourceType = typeof(Globalization.Contact))]
    Callback = 2,

    [Display(Description = "ContactOption3", ResourceType = typeof(Globalization.Contact))]
    Quotation = 3,

    [Display(Description = "ContactOption4", ResourceType = typeof(Globalization.Contact))]
    Other = 4
}

每个枚举值都有一个Display attribute值,该Description值是名为 的资源程序集类中的一个条目Globalization.Contact。此资源程序集(项目)包含针对不同联系人选项类型(Demo、Callback、Quotation、Other)的各种翻译。它包含如下文件:(contact.nl.resx对于荷兰)和contact.resx(默认为 en-US),其中不同的枚举值具有其本地化值(翻译)。

现在在一个静态枚举助手类中,我们有这个方法:

public static string GetDisplayDescription(this Enum enumValue)
{
    return enumValue.GetType().GetMember(enumValue.ToString())
        .FirstOrDefault()?
        .GetCustomAttribute<DisplayAttribute>()
        .GetDescription() ?? "unknown";
}

这将获得Description属性的Display属性值。当且仅当设置了时,这将是翻译后的值CurrentUICulture。这将所有东西“粘合”在一起。

Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US");

或者

Thread.CurrentThread.CurrentUICulture = new CultureInfo("nl-NL");

现在在一个简单的单元测试(XUnit)中,我们可以看到它是否按预期/期望/设计工作:

[Theory]
[InlineData("nl-NL", "Terugbelverzoek")]
[InlineData("en-US", "Callback")]
public void TestTranslationOfDescriptionAttribute(string culture, string expectedValue)
{
    // Arrange
    CultureInfo cultureInfo = new CultureInfo(culture);
    Thread.CurrentThread.CurrentCulture = cultureInfo;
    Thread.CurrentThread.CurrentUICulture = cultureInfo;

    ContactOptionType contactOptionType = ContactOptionType.Callback;

    // Act
    string description = contactOptionType.GetDisplayDescription();

    // Assert
    Assert.Equal(expectedValue, description);
}

以上将毫不费力地成功✅‍♂️</p>

所以这个解决方案不再使用“复杂” ,而是LocalizedAttribute一个简单的帮助器,它获取 Display 属性的 Description 属性的(翻译的)值。Display 属性中存在的ResourceType值与设置相结合可以解决CurrentUICulture问题。

于 2019-09-05T10:01:35.553 回答
10

有一种方法可以在显示枚举时使用属性来指定用于枚举的字符串,但是当您必须处理本地化时,我们发现它太繁琐了。

所以对于需要本地化的枚举,我们通常做的就是写一个扩展类,提供一个方法来获取翻译后的名字。您可以只使用从常用资源返回字符串的开关。这样,您可以通过资源为枚举提供翻译后的字符串,就像为其他字符串所做的一样。

例如:

public enum Role
{
    Administrator,
    Moderator,
    Webmaster,
    Guest
}

public static class RoleExt
{
    public static string AsDisplayString(this Role role)
    {
        switch (role)
        {
            case Role.Administrator: return Resources.RoleAdministrator;
            case Role.Moderator:     return Resources.RoleModerator;
            case Role.Webmaster:     return Resources.RoleWebmaster;
            case Role.Guest:         return Resources.RoleGuest;

            default: throw new ArgumentOutOfRangeException("role");
        }
    }
}

您可以像这样使用它:

var role = Role.Administrator;
Console.WriteLine(role.AsDisplayString());

如果您将RoleExt类实现保留在实现旁边,enum Role它将有效地成为Role. 当然,您也可以为此类添加任何其他有用的 enum 扩展。

[编辑]

如果您想处理多个标志设置(“Administrator AND Moderator AND Webmaster”),那么您需要做一些不同的事情:

[Flags]
public enum Roles
{
    None          = 0,
    Administrator = 1,
    Moderator     = 2,
    Webmaster     = 4,
    Guest         = 8
}

public static class RolesExt
{
    public static string AsDisplayString(this Roles roles)
    {
        if (roles == 0)
            return Resources.RoleNone;

        var result = new StringBuilder();

        if ((roles & Roles.Administrator) != 0)
            result.Append(Resources.RoleAdministrator + " ");

        if ((roles & Roles.Moderator) != 0)
            result.Append(Resources.RoleModerator + " ");

        if ((roles & Roles.Webmaster) != 0)
            result.Append(Resources.RoleWebmaster + " ");

        if ((roles & Roles.Guest) != 0)
            result.Append(Resources.RoleGuest + " ");

        return result.ToString().TrimEnd();
    }
}

您可以像这样使用它:

Roles roles = Roles.Administrator | Roles.Guest | Roles.Moderator;
Console.WriteLine(roles.AsDisplayString());

资源文件

资源文件是您将字符串国际化的方式。有关如何使用它们的更多信息,请参见此处:

http://msdn.microsoft.com/en-us/library/vstudio/aa992030%28v=vs.100%29.aspx http://msdn.microsoft.com/en-us/library/vstudio/756hydy4%28v =vs.100%29.aspx

于 2013-06-29T14:28:13.577 回答
2

@eluxen 的答案的一个版本适用于某些可移植(PCL)库(特别是用于Profile47原始解决方案不起作用的)。解决了两个问题:DescriptionAttribute 在可移植库中不可用,并且解决了@Jitendra Pancholi 报告的“找不到任何资源”错误的问题

public class LocalizedDescriptionAttribute : Attribute
{
    private readonly string _resourceKey;
    private readonly Type _resourceType;
    public LocalizedDescriptionAttribute(string resourceKey, Type resourceType)
    {
        _resourceType = resourceType;
        _resourceKey = resourceKey;
    }

    public string Description
    {
        get
        {
            string displayName = String.Empty;
            ResourceManager resMan = _resourceType.GetProperty(
                @"ResourceManager", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).GetValue(null, null) as ResourceManager;
            CultureInfo culture = _resourceType.GetProperty(
                    @"Culture", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).GetValue(null, null) as CultureInfo;

            if (resMan != null)
            {
                displayName = resMan.GetString(_resourceKey, culture);
            }

            var ret = string.IsNullOrEmpty(displayName) ? string.Format("[[{0}]]", _resourceKey) : displayName;
            return ret;
        }
    }
}

有关用法,请参阅原始答案。如果您没有遇到任何问题,我仍然会使用原始答案,因为它不包含通过反射的解决方法

于 2016-03-22T18:21:37.757 回答
1

你可以使用你的枚举值来做你想做的事。

public enum Roles
{
    Administrator = 0,
    Moderator = 1 ,
    Webmaster  = 2,
    Guest = 3 ,
    Etc.... = 4 
}

当您想在列表框中获取选定的枚举时,您检索列表框项目,然后检索关联的枚举编号。

然后您将其转换为像这样的枚举项

Roles myrol = (Roles) i

i在此示例中关联 int vale)

将枚举项转换为整数并将整数值转换回枚举项

 Enum Item to Integer----- 
    int i =  (int)Roles.Admin ;
    Integer to enum Itenm
    Roles r = (Roles)i ; 

     //Getting the name of the enum
     string role =  Role.Admin.ToString() 

如果您要添加到哈希表,那么您可以这样做

Hashtable h = new Hashtable() ; 
h.Items.Add((int)Roles.Admin , Roles.Admin.ToStrinng() ) ;
h.Items.Add((int)Roles.Local , Roles.Local.ToStrinng() ) ; 

当您从 hashtable 中选择一个项目时,将其转换回 Enum 项目并在您想要的地方使用它。您可以使用相同的方式填充 Datatables / Comboboxes 、下拉列表等

于 2013-06-29T14:04:08.350 回答
1

波纹管的扩展方法对我有用。

    public static string GetDisplayValue(this Enum value)
    {
        try
        {
            var fieldInfo = value.GetType().GetField(value.ToString());
            var descriptionAttributes = fieldInfo.GetCustomAttributes(typeof(DisplayAttribute), false) as DisplayAttribute[];

            if (descriptionAttributes == null || descriptionAttributes.Length == 0) return value.ToString();

            if (descriptionAttributes[0].ResourceType != null)
            {
                var resource = descriptionAttributes[0].ResourceType.GetProperty("ResourceManager").GetValue(null) as ResourceManager;
                return resource.GetString(descriptionAttributes[0].Name);
            }
            else
            {
                return descriptionAttributes[0].Name;
            }
        }
        catch
        {
            return value.ToString();
        }
    }

我希望有帮助。

于 2016-01-27T16:48:09.193 回答
1

您可以使用Lexical.Localization ¹,它允许将默认值和文化特定值嵌入代码中,并在外部本地化文件(如 .json、.resx 或 .ini)中扩展以获取更多文化。

namespace Library
{
    enum Permissions
    {
        Create = 1,
        Drop = 2,
        Modify = 4,
        GrantCreate = 8,
        GrantDrop = 16,
        GrantModify = 32
    }
}

在程序代码中:

// Load localization.ini
LineRoot.Builder.AddLocalizationFile("localization.ini").Build();
// Create key for enum
ILine key = LineRoot.Global.Assembly("ConsoleApp4").Type<Permissions>().Format("{0}");
// Print 
Console.WriteLine(key.Value(Permissions.Create | Permissions.Drop));
Console.WriteLine(key.Value(Permissions.Create | Permissions.Drop).Culture("en"));
Console.WriteLine(key.Value(Permissions.Create | Permissions.Drop).Culture("fi"));

本地化.ini:

[Assembly:ConsoleApp4:Type:Library.Permissions:Culture:fi]
Key:Create = Luonti
Key:Drop = Poisto
Key:Modify = Muutos
Key:GrantCreate = Luonnin myöntö
Key:GrantDrop = Poiston myöntö
Key:GrantModify = Muutoksen myöntö

[Assembly:ConsoleApp4:Type:Library.Permissions:Culture:en]
Key:Create = Create
Key:Drop = Drop
Key:Modify = Modify
Key:GrantCreate = Grant Create
Key:GrantDrop = Grant Drop
Key:GrantModify = Grant Modify 

¹(我是那个库的维护者)

于 2019-06-10T16:46:28.000 回答
1

翻译/本地化枚举的问题之一是,您不仅必须翻译它们以显示,而且还要将翻译解析回枚举值。以下 C# 文件包含我如何克服枚举的两种方式翻译的问题。请原谅过多的评论,但我确实对我的创作相当冗长。

//
// EnumExtensions.cs  
//
using System;
using System.Collections.Generic;

namespace EnumExtensionsLibrary
{
    /// <summary>
    /// The functions in this class use the localized strings in the resources
    /// to translate the enum value when output to the UI and reverse--
    /// translate when receiving input from the UI to store as the actual
    /// enum value.
    /// </summary>
    /// 
    /// <Note>
    /// Some of the exported functions assume that the ParserEnumLocalizationHolder
    /// and ToStringEnumLocalizationHolder dictionaries (maps) may contain the enum 
    /// types since they callthe Initialize methods with the input type before executing.
    /// </Note>
    public static class EnumExtensions
    {
        #region Exported methods
        /// <summary>
        /// Save resource from calling project so that we can look up enums as needed.
        /// </summary>
        /// <param name="resourceManager">Where we fish the translated strings from</param>
        /// <remarks>
        /// We do not have access to all of the resources from the other projects directly,
        /// so they must be loaded from the code from within the project.
        /// </remarks>
        public static void RegisterResource(System.Resources.ResourceManager resourceManager)
        {
            if (!MapOfResourceManagers.Contains(resourceManager))
                MapOfResourceManagers.Add(resourceManager);
        }

        /// <summary>
        /// Parses the localized string value of the enum by mapping it 
        /// to the saved enum value
        /// </summary>
        /// <remarks>
        /// In some cases, string for enums in the applications may not be translated to the
        /// localized version (usually when the program presets parameters).  If the enumMap
        /// doesn't contain the value string, we call to Enum.Parse() to handle the conversion
        /// or throw an exception.
        /// </remarks>
        /// <typeparam name="T"></typeparam>
        /// <param name="value"></param>
        /// <exception cref="ArgumentNullException"> enumType or value is null.</exception>
        /// <exception cref="ArgumentException"> enumType is not an Enum. value is either an 
        /// empty string or only contains white space, value is a name, but not one of the 
        /// named constants defined for the enumeration.</exception>
        /// <exception cref="ArgumentNullException">enumType or value is null.</exception>
        /// <returns>
        /// The enum value that matched the input string if found.  If not found, we call 
        /// Enum.Parse to handle the value.
        /// </returns>
        public static T ParseEnum<T>(this string value) where T : struct
        {
            ParserInitialize(typeof(T));
            var enumMap = ParserEnumLocalizationHolder[typeof(T)];
            if (enumMap.ContainsKey(value))
                return (T) enumMap[value];
            return (T)Enum.Parse(typeof(T), value); 
        }

        /// <summary>
        /// Parses the localized string value of the enum by mapping it 
        /// to the saved enum value.  
        /// </summary>
        /// <remarks>
        /// In some cases, string for enums in the applications may not be translated to the
        /// localized version (usually when the program presets parameters).  If the enumMap
        /// doesn't contain the value string, we call to Enum.TryParse() to handle the 
        /// conversion. and return.
        /// </remarks>
        /// <typeparam name="T"></typeparam>
        /// <param name="value"></param>
        /// <param name="result"></param>
        /// <returns>
        /// Returns true if the enum mapping contains the localized string value and the data 
        /// in the returned result parameter will be a valid value of that enum type. if the
        /// string value is not mapped, then calls Enum.TryParse to handle the conversion and 
        /// return result.
        /// </returns>
        public static bool TryParseEnum<T>(this string value, out T result) where T : struct
        {
            ParserInitialize(typeof(T));
            var enumMap = ParserEnumLocalizationHolder[typeof(T)];
            if (!enumMap.ContainsKey(value))
                return Enum.TryParse(value, out result);
            result = (T)enumMap[value];
            return true;
        }

        /// <summary>
        /// Converts the enum value to a localized string.
        /// </summary>
        /// <typeparam name="T">must be an enum to work</typeparam>
        /// <param name="value">is an enum</param>
        /// <returns>
        /// The localized string equivalent of the input enum value
        /// </returns>
        public static string EnumToString<T>(this T value) where T : struct
        {
            ToStringInitialize(typeof(T));
            var toStringMap = ToStringEnumLocalizationHolder[typeof(T)];
            return toStringMap.ContainsKey(value) ? toStringMap[value] : value.ToString();

            //return EnumDescription(value);
        }

        /// <summary>
        /// Gathers all of the localized translations for each 
        /// value of the input enum type into an array
        /// </summary>
        /// <remarks>
        /// The return array from Type.GetEnumValues(), the array elements are sorted by 
        /// the binary values (that is, the unsigned values) of the enumeration constants.
        /// </remarks>
        /// <param name="enumType"></param>
        /// <exception cref="ArgumentException"> The current type is not an enumeration.</exception>
        /// <returns>
        /// A string array with the localized strings representing
        /// each of the values of the input enumType.
        /// </returns>
        public static string[] AllDescription(this Type enumType)
        {
            ToStringInitialize(enumType);
            var descriptions = new List<string>();
            var values = enumType.GetEnumValues();
            var toStringMap = ToStringEnumLocalizationHolder[enumType];
            foreach (var value in values)
            {
                descriptions.Add(toStringMap.ContainsKey(value) ? toStringMap[value] : value.ToString());
            }
            return descriptions.ToArray();
        }
        #endregion

        #region Helper methods
        /// <summary>
        /// Translates an enum value into its localized string equivalent
        /// </summary>
        /// <remarks>
        /// This assumes that the "name" for the localized string in the 
        /// resources will look like "enum-type-name""value".  For example,  
        /// if I have an enum setup as:
        /// 
        ///     enum Days {Sat, Sun, Mon, Tue, Wed, Thu, Fri};
        /// 
        /// the value "Sun" in the enum must have the name: "DaysSun"
        /// in the resources. The localized (translated) string will
        /// be in the value field.  E.g.,
        ///
        ///  <data name="DaysSun" xml:space="preserve">
        /// <value>Sunday</value>
        ///  </data>    
        /// 
        /// 2nd note: there may be multiple resources to pull from.
        /// Will look in each resource until we find a match or 
        /// return null.
        /// </remarks>
        /// <typeparam name="T"></typeparam>
        /// <param name="enumType">the enum type</param>
        /// <param name="value">the specific enum value</param>
        /// <returns>
        /// If the enum value is found in the resources, then return 
        /// that string.  If not, then return null. 
        /// </returns>
        private static string LocalEnumDescription<T>(Type enumType, T value)
        {
            foreach (var resourceManager in MapOfResourceManagers)
            {
                // The following live line uses string interpolation to perform:
                //var rk = string.Format("{0}{1}", enumType.Name, value);
                var rk = $"{enumType.Name}{value}";

                // Given the above string formatting/interpolation, neither the enum.Name 
                // nor the value will have a '.' so we do not have to remove it.
                var result = resourceManager.GetString(rk);
                if (!string.IsNullOrEmpty(result))
                    return result;
            }
            return null;
        }

        /// <summary>
        /// Initializes the mapping of the enum type to its mapping of localized strings to 
        /// the enum's values.
        /// </summary>
        /// <remarks>
        /// The reason for each enum type to have a mapping from the localized string back 
        /// to its values is for ParseEnum and TryParseEnum to quickly return a value rather
        /// than doing a lengthy loop comparing each value in the resources.
        /// 
        /// Also, we only map the corresponding resource string if it exists in the resources.
        /// If not in the resources, then we call the Enum methods Parse() and TryParse() to
        /// figure the results and throw the appropriate exception as needed.
        /// </remarks>
        /// 
        /// <param name="enumType"></param>
        private static void ParserInitialize(Type enumType)
        {
            if (!ParserEnumLocalizationHolder.ContainsKey(enumType))
            {
                var values = enumType.GetEnumValues();  // See remark for AllDescription().
                var enumMap = new Dictionary<string, object>();
                foreach (var value in values)
                {
                    var description = LocalEnumDescription(enumType, value);
                    if (description != null)
                        enumMap[description] = value;
                }
                ParserEnumLocalizationHolder[enumType] = enumMap;
            }
        }

        /// <summary>
        /// Initializes the mapping of the enum type to its mapping of the enum's values
        /// to their localized strings.
        /// </summary>
        /// <remarks>
        /// The reason for each enum type to have a mapping from the localized string to its
        /// values is for AllDescription and EnumToString to quickly return a value rather 
        /// than doing a lengthy loop runing through each of the resources.
        /// 
        /// Also, we only map the corresponding resource string if it exists in the resources.
        /// See the EnumDescription method for more information.
        /// </remarks>
        /// 
        /// <param name="enumType"></param>
        private static void ToStringInitialize(Type enumType)
        {
            if (!ToStringEnumLocalizationHolder.ContainsKey(enumType))
            {
                var values = enumType.GetEnumValues();  // See remark for AllDescription().
                var enumMap = new Dictionary<object, string>();
                foreach (var value in values)
                {
                    var description = LocalEnumDescription(enumType, value);
                    if (description != null)
                        enumMap[value] = description;
                }
                ToStringEnumLocalizationHolder[enumType] = enumMap;
            }
        }
        #endregion

        #region Data
        private static readonly List<System.Resources.ResourceManager> MapOfResourceManagers =
            new List<System.Resources.ResourceManager>();
        private static readonly Dictionary<Type, Dictionary<string, object>> ParserEnumLocalizationHolder =
            new Dictionary<Type, Dictionary<string, object>>();
        private static readonly Dictionary<Type, Dictionary<object, string>> ToStringEnumLocalizationHolder =
            new Dictionary<Type, Dictionary<object, string>>();
        #endregion
    }
}

这不需要每个枚举值之前的属性,但确实需要将资源中已翻译枚举字符串的名称属性格式化为枚举类型名称和枚举值的串联。有关详细信息,请参阅 LocalEnumDescription 方法上方的注释。此外,它通过映射它们的翻译来保留枚举的翻译(向前和向后),这样我们就不需要在每次遇到枚举值时搜索翻译。

希望它很容易理解和使用。

于 2016-08-26T22:26:52.120 回答
0

另一种可能性是创建一个全局显示名称存储作为Enum类的类扩展:

// Enum display names
public static class EnumDisplayNames {
  // Display name storage
  private static Dictionary<Type, Dictionary<Enum, String>> s_Names = 
    new Dictionary<Type, Dictionary<Enum, String>>();

  // Display name for the single enum's option
  private static String CoreAsDisplayName(Enum value) {
    Dictionary<Enum, String> dict = null;

    if (s_Names.TryGetValue(value.GetType(), out dict)) {
      String result = null;

      if (dict.TryGetValue(value, out result))
        return result;
      else
        return Enum.GetName(value.GetType(), value);
    }
    else
      return Enum.GetName(value.GetType(), value);
  }

  // Register new display name
  public static void RegisterDisplayName(this Enum value, String name) {
    Dictionary<Enum, String> dict = null;

    if (!s_Names.TryGetValue(value.GetType(), out dict)) {
      dict = new Dictionary<Enum, String>();

      s_Names.Add(value.GetType(), dict);
    }

    if (dict.ContainsKey(value))
      dict[value] = name;
    else
      dict.Add(value, name);
  }

  // Get display name
  public static String AsDisplayName(this Enum value) {
    Type tp = value.GetType();

    // If enum hasn't Flags attribute, just put vaue's name 
    if (Object.ReferenceEquals(null, Attribute.GetCustomAttribute(tp, typeof(FlagsAttribute))))
      return CoreAsDisplayName(value);

    // If enum has Flags attribute, enumerate all set options
    Array items = Enum.GetValues(tp);

    StringBuilder Sb = new StringBuilder();

    foreach (var it in items) {
      Enum item = (Enum) it;

      if (Object.Equals(item, Enum.ToObject(tp, 0)))
        continue;

      if (value.HasFlag(item)) {
        if (Sb.Length > 0)
          Sb.Append(", ");
        Sb.Append(CoreAsDisplayName(item));
      }
    }

    Sb.Insert(0, '[');

    Sb.Append(']');

    return Sb.ToString();
  }
}

可能的使用非常简单,例如:

  public enum TestEnum {
    None,
    One,
    Two,
    Three
  }

  [Flags]
  public enum TestOptions {
    None = 0,
    One = 1,
    Two = 2,
    Three = 4
  }

  ...

  // Let them be in German (for demonstration only)... 
  TestEnum.None.RegisterDisplayName("Nichts");
  TestEnum.One.RegisterDisplayName("Eins");
  TestEnum.Two.RegisterDisplayName("Zwei");
  TestEnum.Three.RegisterDisplayName("Drei");

  // Usually, you obtain display names from resources:
  // TestEnum.None.RegisterDisplayName(Resources.NoneName);
  // ...

  TestOptions.None.RegisterDisplayName("-");
  TestOptions.One.RegisterDisplayName("bit 0 set");
  TestOptions.Two.RegisterDisplayName("bit 1 set");
  TestOptions.Three.RegisterDisplayName("bit 2 set");
  TestOptions.Four.RegisterDisplayName("bit 3 set");

  ...

  TestEnum v = TestEnum.Two;
  String result = v.AsDisplayName(); // <- "Zwei"

  TestOptions o = TestOptions.One | TestOptions.Three | TestOptions.Four;
  String result2 = o.AsDisplayName(); // <- "[bit 0 set, bit 2 set, bit 3 set]"
于 2013-06-29T18:02:59.767 回答
0
public enum RoleEnum
{
    Administrator = 4,
    Official = 1,
    Trader = 3,
    HeadOfOffice = 2
}
public static class RoleEnumExtension
{
    private static readonly ResourceManager Resource =
        new ResourceManager("Project.CommonResource", typeof(CommonResource).Assembly);

    public static string Display(this RoleEnum role)
    {
        return Resource.GetString("RoleType_" + role);
    }
}

您可以将其用作

RoleEnum.Administrator.Display()

希望这对某人有帮助

于 2015-03-04T10:03:12.897 回答
0

我目前正在使用它,希望对您有所帮助!:

    /// <summary>
    ///  Retrieves a translated value from an enumerated list.
    /// </summary>
    /// <param name="value">Enum</param>
    /// <param name="resource">string</param>
    /// <returns>string</returns>
    protected string GetTranslatedEnum(Enum value, string resource)
    {
        string path = String.Format("Resources.{0}", resource);

        ResourceManager resources = new ResourceManager(path, global::System.Reflection.Assembly.Load("App_GlobalResources"));

        if (resources != null) {
            return resources.GetString(value.ToString());
        } else {
            return value.ToString();
        }
    }

创建了一个名为“ App_GlobalResources\ProductNames.resx ”的 .resx 文件。

用法:

// Convert the ProductId integer on the item to its Enum equivalent.
Products product = (Products) item.ProductId;

string productName = this.GetTranslatedEnum(product, "ProductNames");
于 2018-06-26T19:45:27.220 回答
0

我已经使用了公认的答案,但做了一点改动。它更短并且没有自定义类。

这是我的枚举。所有项目都有 DisplayAttribute

public enum OvertimeRequestedProvisionFor
{
    [Display(ResourceType = typeof(Localization), Name = LocalizationKeys.General_Fee)]
    Fee = 1,

    [Display(ResourceType = typeof(Localization), Name = LocalizationKeys.General_Permit)]
    Permit = 2,
}

这是扩展方法

public static string GetDisplayName(this Enum enumValue)
{
    var fi = enumValue.GetType().GetField(enumValue.ToString());

    var attributes = (DisplayAttribute[])fi.GetCustomAttributes(typeof(DisplayAttribute), false);

    return attributes != null && attributes.Length > 0
            ? attributes[0].GetName()
            : enumValue.ToString();
}

现在都要做:

var localization = OvertimeRequestedProvisionFor.Fee.GetDisplayName();
于 2020-06-17T20:50:07.130 回答
0

与接受的答案相同的答案,但没有代码分析警告

 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1019:DefineAccessorsForAttributeArguments", Justification ="Resourcetype is only needed to instantiate Resource manager, and ResourceManager is exposed")]
[AttributeUsage(AttributeTargets.All)]
public sealed class LocalizedDescriptionAttribute 
    : DescriptionAttribute
{
    private readonly string _resourceKey;
    private readonly ResourceManager _resourceManager;

    public LocalizedDescriptionAttribute(string resourceKey, Type resourceType)
    {
        _resourceManager = new ResourceManager(resourceType);
        _resourceKey = resourceKey;
    }

    public string ResourceKey
    {
        get { return _resourceKey; }
    }

    public ResourceManager ResourceManager
    {
        get { return _resourceManager; }
    }

    public override string Description
    {
        get
        {
            string displayName = _resourceManager.GetString(_resourceKey);
            return string.IsNullOrEmpty(displayName)? string.Format(CultureInfo.CurrentUICulture ,"[[{0}]]", _resourceKey) : displayName;
        }
    }
}

public static class EnumExtensions
{
    public static string GetDescription(this Enum enumValue)
    {
        if (enumValue == null)
        {
            throw new ArgumentNullException("enumValue");
        }
        FieldInfo fi = enumValue.GetType().GetField(enumValue.ToString());

        DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);

        if (attributes != null && attributes.Length > 0)
        {
            return attributes[0].Description;
        }
        else
        {
            return enumValue.ToString();
        }
    }
}
于 2016-07-04T11:27:45.383 回答
0

上面带有 LocalizedDescriptionAttribute 的解决方案是从程序的当前语言中读取的。非常适合客户端,当您想将语言作为参数传递时,对于服务器端来说不是那么灵活。

因此,我通过添加另一种方法,使用 LocalizedDescriptionAttribute 扩展了 eluxen 解决方案:

    /// <summary>
    /// Gets the description, stored in this attribute, reading from the resource using the cultureInfo defined by the language!
    /// </summary>
    /// <param name="language">The language.</param>
    /// <returns>Description for the given language if found; the default Description or ressourceKey otherwise</returns>
    public string GetDescription(string language)
    {
        return resource.GetStringFromResourceForLanguage(resourceKey, language, Description);
    }

    public static string GetStringFromResourceForLanguage(this ResourceManager resourceManager, string resourceKey, string language, string defaultValue = null)
    {
        if (string.IsNullOrEmpty(defaultValue))
            defaultValue = resourceKey;
        try
        {
            CultureInfo culture = CultureInfo.GetCultureInfo(language);
            string displayName = resourceManager.GetString(resourceKey, culture);
            return !string.IsNullOrEmpty(displayName) ? displayName : defaultValue;
        }
        catch // any, not only CultureNotFoundException
        {
            return defaultValue;
        }
    }

使用 GetDescription 扩展也带有语言参数:

            bool hasLanguage = !string.IsNullOrEmpty(language);
            if (hasLanguage)
            {
                var attribute = (LocalizedDescriptionAttribute)Attribute.GetCustomAttribute(fieldInfo, typeof(LocalizedDescriptionAttribute));
                if (attribute != null)
                {
                    description = attribute.GetDescription(language);
                }
                else
                    hasLanguage = false;
            }
            if (!hasLanguage)
            {
                var attribute = (DescriptionAttribute)Attribute.GetCustomAttribute(fieldInfo, typeof(DescriptionAttribute));
                if (attribute != null)
                {
                    description = attribute.Description;
                }
            }

最后,我更喜欢使用 nameof 来避免属性使用中的任何字符串。

public enum QualityPersonInfo
{
    Ok = 0,
    [LocalizedDescription(nameof(QualityStrings.P1), typeof(QualityStrings))]
    Duplicate = 1,
    [LocalizedDescription(nameof(QualityStrings.P2), typeof(QualityStrings))]
    IdMissing = 2,
}

我在我的代码中使用如下:

 QualityPersonInfo status = QualityPersonInfo.IdMissing; 
 var description = status.GetDescription("ch-DE");
于 2017-03-16T16:12:09.187 回答