Подтвердить что ты не робот

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

Каков наилучший способ локализовать описания перечислений в .net?

(см. Добавление описаний к константам перечисления для примера описания перечисления)

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

4b9b3361

Ответ 1

Это то, с чем я столкнулся, я не видел значения при добавлении настраиваемого класса атрибутов для хранения ключа ресурса, а затем поиска в файлах ресурсов - почему бы просто не использовать имя enum typename + значение как ключ ресурса?

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

Ответ 2

Мое решение, используя собственный атрибут decription:

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

Объявление Enum

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

Затем вызовите MyEnumInstance.GetLocalizedDescription()

Ответ 3

существует простое решение: используйте атрибут LocalizedDescription для передачи ключа ресурса.

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

    }

Ответ 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 ...;
        }
    }

Возможно, это не экстремально гладкое или причудливое решение, но оно работает =)

Ответ 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

Смотрите мой пример таблицы в этом вопросе:

Локализация /I 18n данных базы данных в LINQ to SQL

Таблица типов состояний отображает значения Enumeration. Настоящая выгода заключается в том, что вы можете иметь локализацию в своих отчетах и ​​в ваших приложениях и указывать внешние идентификаторы для интеграции с третьими сторонами, которые не хотят ваши внутренние значения и т.д. Это отделяет описание перечисления от его значения.

Ответ 7

Нельзя использовать несколько атрибутов System.ComponentModel.DescriptionAttribute(так что опция отсутствует).

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