Как локализовать описание свойства в C #? - PullRequest
16 голосов
/ 13 сентября 2011

Я работаю над классом, который будет использоваться некоторыми людьми из других стран. Я должен локализовать каждое сообщение, предупреждение e.c. чтобы они могли понять, что мы имеем в виду. Во многих случаях я достиг своей цели. Но такие атрибуты свойств, как описания, являются такой болью в заднице.

Вот что у меня сейчас есть:

[Category("Editable Values"), Description("Sets the minimum select...")]
    public Ampere Simin
    {
        get
        {...}
        set
        {...}
    }

и

[Category("Editable Values"), Description(Localisation.Simin)] // "Localisation" here is the internal resource file that i wrote for messages, warnings, exceptions and -unfortunately- descriptions
        public Ampere Simin
        {
            get
            {...}
            set
            {...}
        }

Это то, что я пытаюсь сделать. Но использовать локализацию таким способом невозможно. Любые предложения о том, что я могу использовать вместо этого?

Ответы [ 6 ]

21 голосов
/ 13 сентября 2011

Подклассы:

[STAThread]
static void Main()
{   // just some example code to show it working in winforms, but
    // anything using System.ComponentModel should see the change
    Application.EnableVisualStyles();        
    Application.Run(new Form {Controls = {new PropertyGrid {Dock = DockStyle.Fill, SelectedObject = new Foo()}}});
}

class Foo
{   // assume the following literals are keys, for example to a RESX
    [LocalizedCategory("cat")]
    [LocalizedDescription("desc")]
    [LocalizedDisplayName("disp name")]
    public string Bar { get; set; }
}
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Parameter | AttributeTargets.Delegate | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter)]
class LocalizedDescriptionAttribute : DescriptionAttribute
{
    static string Localize(string key)
    {
        // TODO: lookup from resx, perhaps with cache etc
        return "Something for " + key;
    }
    public LocalizedDescriptionAttribute(string key)
        : base(Localize(key))
    {
    }
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event)]
class LocalizedDisplayNameAttribute : DisplayNameAttribute
{
    static string Localize(string key)
    {
        // TODO: lookup from resx, perhaps with cache etc
        return "Something for " + key;
    }
    public LocalizedDisplayNameAttribute(string key)
        : base(Localize(key))
    {
    }
}
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Parameter | AttributeTargets.Delegate | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter)]
class LocalizedCategoryAttribute : CategoryAttribute
{
    public LocalizedCategoryAttribute(string key) : base(key) { }
    protected override string  GetLocalizedString(string value)
    {
            // TODO: lookup from resx, perhaps with cache etc
        return "Something for " + value;
    }
}
4 голосов
/ 13 сентября 2011
[Required(ErrorMessageResourceName = "LogOnModel_UserName_Required",     
      ErrorMessageResourceType = typeof(Resources.Global))]    
[Display(Name = "LogOnModel_UserName_Required",resourceType = typeof(Resources.Global))]  
public string UserName { get; set; }

см .: http://geekswithblogs.net/shaunxu/archive/2010/05/06/localization-in-asp.net-mvc-ndash-3-days-investigation-1-day.aspx

3 голосов
/ 23 декабря 2015

Я объединил Марк Гравелл и Дсмит :

Первое: создайте класс Attribute с именем LocalizedDescriptionAttribute

public class LocalizedDescriptionAttribute : DescriptionAttribute
{
    static string Localize(string key)
    {
        return YourResourceClassName.ResourceManager.GetString(key);
    }

    public LocalizedDescriptionAttribute(string key)
        : base(Localize(key))
    {
    }
}

Используйте его как LocalizedDescription (слово "Атрибут" не обязательно)

public class Foo
{   
    // myString is a key that exists in your Resources file
    [LocalizedDescription("myString")]
    public string Bar { get; set; }
}
3 голосов
/ 13 сентября 2011

Давайте возьмем этот пример класса:

// ------------------------------------------------------------------------
public class Customer
{
    // ----------------------------------------------------------------------
    [Category( "Editable Values" ), LocDescription( "FirstName", "Sets the first name..." )]
    public string FirstName { get; set; }

    // ----------------------------------------------------------------------
    [Category( "Editable Values" ), LocDescription(  Key = "LastName", DefaultDescription = "Sets the last name..." )]
    public string LastName { get; set; }
} // class Customer

Теперь вы можете реализовать собственный класс атрибутов:

// ------------------------------------------------------------------------
public class LocDescriptionAttribute : DescriptionAttribute
{
    // ----------------------------------------------------------------------
    public LocDescriptionAttribute()
    {
    } // LocDescriptionAttribute

    // ----------------------------------------------------------------------
    public LocDescriptionAttribute( string key, string defaultDescription ) :
        base( defaultDescription )
    {
        Key = key;
        DefaultDescription = defaultDescription;
    } // LocDescriptionAttribute

    // ----------------------------------------------------------------------
    public string Key { get; set; }

    // ----------------------------------------------------------------------
    public string DefaultDescription { get; set; }

    // ----------------------------------------------------------------------
    public override string Description
    {
        get
        {
            // load from resx
            string description = Strings.GetString( Key );
            if ( string.IsNullOrEmpty( description ) )
            {
                description = DefaultDescription;
            }
            return description;
        }
    } // Description
} // class LocDescriptionAttribute

Теперь у вас есть локализованное описание:

AttributeCollection attributes = TypeDescriptor.GetProperties( customer )[ "FirstName" ].Attributes;
DescriptionAttribute myAttribute = (DescriptionAttribute)attributes[ typeof( DescriptionAttribute ) ];
ConsoleWiterLine( myAttribute.Description );
2 голосов
/ 05 апреля 2012

Небольшое продолжение ответа Яни (который хорошо сработал для меня):

string description = Strings.GetString( Key );

можно сделать более четко, как

ResourceManager rm = YourResourceClassName.ResourceManager;
string description = rm.GetString(Key);

Это было неясно, но не былоПонятно, чего не хватало в исходном коде.Свойство .ResourceManager класса ресурса генерируется автоматически.

Затем для доступа к нему используйте метод расширения, описанный здесь Glennular: StackOverflow вместо жесткого кодирования в именах полей / классов(просто используйте LocDescriptionAttribute вместо DescriptionAttribute).

Единственное другое изменение, которое я сделал, было сделать его статическим на основе this IComparable enum, чтобы позволить его использовать любым перечислением вместо привязки копределенный тип;это было лучшее, что я мог сделать, учитывая, что я не могу применить интерфейс, чтобы ограничить его только перечислениями, к которым я добавил описания.

1 голос
/ 13 сентября 2011

Кажется, CategoryAttribute имеет код для поиска локализованной строки на основе внутренних ресурсов, но DescriptionAttribute не имеет локализации.

От отражателя:

public string Category
{
    get
    {
        if (!this.localized)
        {
            this.localized = true;
            string localizedString = this.GetLocalizedString(this.categoryValue);
            if (localizedString != null)
            {
                this.categoryValue = localizedString;
            }
        }
        return this.categoryValue;
     }
}

Я думаю, вы бы расширили атрибуты и создали свою собственную реализацию, передав ResyType. Примерно так, смутно основанный на DataAnnotation логике. Очевидно, нужно немного больше убрать.

public class LocaleDescriptionAttribute : DescriptionAttribute
{
    ...

    public LocaleDescriptionAttribute(string resourceKey, Type resourceType)
        : base(resourceKey)
    {
        this.resourceType = resourceType;
    }

    public override string  Description
    {
        get 
        { 
            var description = base.Description;
            PropertyInfo property = resourceType.GetProperty(
                       description, BindingFlags.Public | BindingFlags.Static);
            if (property == null)
            {
                return description;
            }
            return property.GetValue(null, null) as string;
        }
    }    
}
...