2013-06-29 1 views
40

Как вы можете локализовать перечисления для ListBoxFor, где возможны несколько вариантов?Локализация Enum

например 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. Но есть ли способ сделать это для нескольких избранных списков?

[EDIT]

Это, как я хотел бы использовать его, что, как она работает сейчас, но не переводятся на другой язык:

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

Примечание: Я переименовал enum из Role в RoleType.

+1

Вы имеете в виду, используя значения, которые могут быть объединены с поразрядными операций, как [это] (http://stackoverflow.com/questions/8447/ перечисляемые-флаги-атрибутов)? –

+0

@ChrisSinclair, нет, я имею в виду для перевода в представлении. Описание перечислений сохраняется в файле ресурсов. – Quoter

+0

Возможный дубликат [Как использовать локализацию в C#] (http://stackoverflow.com/questions/1142802/how-to-use-localization-in-c-sharp) – MikeT

ответ

57

Вы можете реализовать атрибут описания.

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"); 
+0

Привет, это выглядит интересно. Как получить все перечисления для списка? И автоматически ли он выбирает правильный язык с этой строкой , определенный в webconfig? – Quoter

+0

ваш код имеет тип «LocalizedDescriptionAttributre». Я исправил его и, похоже, работал с языковыми файлами. Мне просто не хватает числа за ролями. Эти числа необходимы как идентификаторы для таблицы соединений. Как получить номера выбранных ролей, когда страница возвращается к контроллеру? – Quoter

+0

Как вы уже упоминали в своем вопросе. Я отредактировал свой ответ, вы можете увидеть пример кода в разделе «И использовать его так» – eluxen

2

Вы можете использовать свое значение перечисления, чтобы делать то, что хотите.

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

Если вы хотите, чтобы получить выбранное перечисление на ListBox, вы извлечь элемент ListBox, а затем вы получите соответствующий номер перечисления.

Тогда вы будете конвертировать что к элементу перечислений как этот

Roles myrol = (Roles) i 

(i ассоциирован INT юдоли для данного примера)

Преобразование элемента перечисления в целое и целое значение обратно к пункту перечислений

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, то вы можете сделать это таким образом

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

Когда вы выбираете элемент из хеш-таблицы, конвертируйте его обратно в элемент Enum и используйте его там, где хотите. Вы можете использовать тот же способ для заполнения Datatables/Comboboxes, выпадающих списков и т. Д.

+0

как это будет переводить перечисление, например, на голландский язык? – Quoter

+0

Что вы хотите сделать, чтобы перевести enum item ba другого типа и наоборот, правильно? Enum iem to> Integer Integer> enum item –

+0

Нет, примерно так: http://ruijarimba.wordpress.com/2012/02/17/asp-net-mvc-creating-localized-dropdownlists-for-enums – Quoter

8

Существует способ использования атрибутов, чтобы указать строку использовать для перечислений, когда их отображения , но мы обнаружили, что это слишком сложно, когда вам приходится обрабатывать локализацию.

Так что мы обычно делаем для перечислений, которые должны быть локализованы, - это написать класс расширения, предоставляющий метод для получения переведенного имени. Вы можете просто использовать переключатель, который возвращает строки из обычных ресурсов. Таким образом, вы предоставляете переведенные строки для перечислений через ресурсы, как и для других строк.

Например:

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 , Конечно, вы также можете добавить к этому классу любые другие полезные расширения для перечисления.

[EDIT]

Если вы хотите обработать несколько параметров флагов («Администратор и замедлителя и веб-мастеров»), то вам нужно сделать что-то немного по-другому:

[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

+0

Как именно это работает с файлами ресурсов? И мне нужно, чтобы он использовал его в списке, где возможны множественные выборы. – Quoter

+0

@Quoter Он работает так же, как и все остальные строки в файлах ресурсов - как вы обрабатываете все ваши другие переведенные строки? Я предполагаю, что вы используете встроенную обработку файлов ресурсов, с файлами ресурсов, названными для языка, для которого они предназначены. Если вы хотите разрешить множественный выбор, то это обрабатывается путем создания значений перечислимых значений из двух, чтобы вы могли их объединить. Я обновлю код, чтобы показать, как создать строку для нескольких значений. –

+0

Я обрабатываю другие строки с расширением, называемым modelmetadata, который можно найти здесь: https://github.com/Code52/internationalization-mvc4/wiki/Localizing-Data-Annotations Но кажется, что это не работает для перечислений. – Quoter

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]" 
+0

Итак, это загружает все языки в переменную, а затем выбирает ее в зависимости от набора языков. Я прав? – Quoter

+0

На самом деле. Это загружает все перечисления (а не языки), которые имеют отображаемые имена в одну переменную. –

22

Я решил проблему, создав EnumExtension, который я использую в своем представлении. Это расширение ищет файл ресурсов с именем «EnumResources.resx» и ищет ресурс по следующему соглашению об именах {Name of EnumType} _ {Значение перечислимого числа, переданного в}. Если ключ ресурса отсутствует, будет отображаться значение ресурса, заключенного в двойные скобки [[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; 
    } 
} 

Файл ресурсов выглядит следующим образом: Resource file

Использование:

<div>@ContractStatus.Created.GetDisplayName()</div> 
+0

Выглядит отлично и просто, мне это нравится. Один вопрос, однако, все ли перечислены в одном файле ресурсов? Возможно ли иметь несколько файлов? – Quoter

+0

@Quoter, вы можете настроить свой ResourceManager, используя это: http://msdn.microsoft.com/en-us/library/yfsz7ac5(v=vs.110).aspx Я еще не пробовал это tho , – disco

+1

@Quoter Я попробовал, и я получил его на работу. Вы можете заменить создание ResourceManager примерно следующим образом: var rm = new ResourceManager («MyAppNamespace.Web.Resources.EnumResources», typeof (EnumResources) .Сборка); Вам не нужно использовать EnumResources в инструкции typeof(), просто используйте любой класс, расположенный в той же сборке, что и ваши файлы ресурсов. Затем все, что вам нужно сделать, это разрешить файл ресурсов, который вы хотите использовать, и задать первый аргумент для этого конкретного ресурса. Возможно, вы можете использовать e.GetType(). Имя каким-то образом? – disco

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() 

Надеется, что это ж больной помощи кому-то

+0

да, но это другой способ приблизиться к нему. – Iceburg

0

Метод расширения для подбородка это сработало для меня.

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

I holpe помогает.

0

Версия @eluxen's answer, работающая для некоторых портативных (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; 
     } 
    } 
} 

Для использования см original answer. И если вы не сталкиваясь ни с какими проблемами, я бы все-таки использовать оригинальный ответ, потому что он не содержит обходные пути через отражение

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(); 
     } 
    } 
} 
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 для получения дополнительной информации. Кроме того, он сохраняет переводы перечислений (как вперед, так и назад) путем отображения их переводов таким образом, что нам не нужно искать перевод каждый раз, когда мы сталкиваемся с значением перечисления.

Надеюсь, его достаточно просто понять и использовать.

+0

Вы могли бы предоставить код фрагмента, как его использовать? он отлично смотрится, но я не знаю, как его использовать! –

0

Решения, описанные выше с помощью LocalizedDescriptionAttribute, читают его с текущего языка программы. Идеально подходит для клиента, не столь гибкого для серверной части, когда вы хотите передать язык в качестве параметра.

Так что я продлил eluxen решения с LocalizedDescriptionAttribute, добавив еще один метод:

/// <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"); 
+0

что здесь не так? почему -1? – EricBDev