web-dev-qa-db-ja.com

列挙型記述属性のローカライズ

.netで列挙型の説明をローカライズする最良の方法は何ですか?

(列挙の説明の例については、 列挙定数への説明の追加 を参照してください)

理想的には、ResourceManagerとリソースファイルを使用して、アプリの他の領域がどのようにローカライズされるかに適合するものが欲しいです。

36
Ryan

これは私が最終的に行ったものであり、リソースキーを保持するカスタム属性クラスを追加してからリソースファイルを調べることで値がわかりませんでした。なぜ、列挙型の名前+値をリソースキーとして使用しないのですか?

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

私の解決策、ネイティブの説明属性を使用:

_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()を呼び出します

23
nairik

簡単な解決策があります。LocalizedDescription属性を使用してリソースキーを渡します。

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

    }
8

私が一度やった方法の1つは、文字列を返す列挙型と同じ名前空間に拡張メソッドを追加することでした。私の場合はハードコードされただけですが、リソースファイルから取得しても問題ありません。

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

多分非常に滑らかなまたは派手なソリューションではないかもしれませんが、それは機能します=)

3
Svish

フラグ列挙型のサポートを追加するには、@ 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);
    }
}
1
kerem

この質問の私のテーブルの例を参照してください:

LINQ to SQLのデータベースデータのローカリゼーション/ I18n

ステータスタイプテーブルは列挙値にマップされます。ここでの真の利点は、レポートやアプリケーション全体でローカライズを行い、内部IDを必要としないサードパーティと統合するために外部IDを指定できることです。これにより、列挙型の説明がその値から切り離されます。

0
Neil Barnwell

複数のSystem.ComponentModel.DescriptionAttributeを適用することはできません(そのため、このオプションは無効です)。

したがって、間接レベルを追加し、説明にリソース名を保持してから、リソースのローカリゼーションサポートを使用します。列挙型のユーザーは、これを行うためにヘルパーメソッドを呼び出す必要があることは明らかです。

0
Richard
0
Darren