Как локализовать описание свойства в С#?
Я работаю над классом, который будет использоваться некоторыми людьми из других стран. Я должен локализовать каждое сообщение, предупреждая 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
{...}
}
То, что я пытаюсь сделать. Но так нельзя использовать локализации. Любые предложения о том, что я могу использовать вместо него?
Ответы
Ответ 1
подклассов:
[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;
}
}
Ответ 2
[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
Возьмем этот образец класса:
// ------------------------------------------------------------------------
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 );
Ответ 4
Незначительное расширение ответа Jani (что хорошо сработало для меня):
string description = Strings.GetString( Key );
можно более четко сделать как
ResourceManager rm = YourResourceClassName.ResourceManager;
string description = rm.GetString(Key);
Это было неопределенно, но было неясно, чего не хватает в исходном коде. Свойство .ResourceManager
класса ресурса автогенерируется.
Затем, чтобы получить доступ к нему, используйте метод расширения, описанный Гленнулом здесь: fooobar.com/questions/31650/... вместо hardcoding в именах полей/классов (просто используйте LocDescriptionAttribute
вместо DescriptionAttribute
).
Единственная другая настройка, которую я сделал, заключалась в том, чтобы сделать ее статической на основе this IComparable enum
, чтобы она могла использоваться любым перечислением вместо того, чтобы быть заблокирована для определенного типа; это было лучшее, что я мог сделать, учитывая, что я не могу применить интерфейс, чтобы ограничить его только перечислениями, которые я добавил в описания.
Ответ 5
Я объединил Марк Гравелл и dsmith:
Сначала: создайте класс атрибута с именем LocalizedDescriptionAttribute
public class LocalizedDescriptionAttribute : DescriptionAttribute
{
static string Localize(string key)
{
return YourResourceClassName.ResourceManager.GetString(key);
}
public LocalizedDescriptionAttribute(string key)
: base(Localize(key))
{
}
}
Используйте его как LocalizedDescription (слово "Attribute" не требуется)
public class Foo
{
// myString is a key that exists in your Resources file
[LocalizedDescription("myString")]
public string Bar { get; set; }
}
Ответ 6
Кажется, что 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;
}
}
Я думаю, вы должны расширить атрибуты и создать свою собственную реализацию, передав в ResourceType. Что-то вроде этого, основываясь на логике 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;
}
}
}