可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have a localized application, and I am wondering if it is possible to have the DisplayName
for a certain model property set from a Resource.
I'd like to do something like this:
public class MyModel {
[Required]
[DisplayName(Resources.Resources.labelForName)]
public string name{ get; set; }
}
But I can't to it, as the compiler says: "An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type" :(
Are there any workarounds? I am outputting labels manually, but I need these for the validator output!
回答1:
How about writing a custom attribute:
public class LocalizedDisplayNameAttribute: DisplayNameAttribute
{
public LocalizedDisplayNameAttribute(string resourceId)
: base(GetMessageFromResource(resourceId))
{ }
private static string GetMessageFromResource(string resourceId)
{
// TODO: Return the string from the resource file
}
}
which could be used like this:
public class MyModel
{
[Required]
[LocalizedDisplayName("labelForName")]
public string Name { get; set; }
}
回答2:
If you use MVC 3 and .NET 4, you can use the new Display
attribute in the System.ComponentModel.DataAnnotations
namespace. This attribute replaces the DisplayName
attribute and provides much more functionality, including localization support.
In your case, you would use it like this:
public class MyModel
{
[Required]
[Display(Name = "labelForName", ResourceType = typeof(Resources.Resources))]
public string name{ get; set; }
}
As a side note, this attribute will not work with resources inside App_GlobalResources
or App_LocalResources
. This has to do with the custom tool (GlobalResourceProxyGenerator
) these resources use. Instead make sure your resource file is set to 'Embedded resource' and use the 'ResXFileCodeGenerator' custom tool.
(As a further side note, you shouldn't be using App_GlobalResources
or App_LocalResources
with MVC. You can read more about why this is the case here)
回答3:
If you open your resource file and change the access modifier to public or internal it will generate a class from your resource file which allows you to create strongly typed resource references.
Which means you can do something like this instead (using C# 6.0).
Then you dont have to remember if firstname was lowercased or camelcased. And you can see if other properties use the same resource value with a find all references.
[Display(Name = nameof(PropertyNames.FirstName), ResourceType = typeof(PropertyNames))]
public string FirstName { get; set; }
回答4:
Update:
I know it's too late but I'd like to add this update:
I'm using the Conventional Model Metadata Provider which presented by Phil Haacked it's more powerful and easy to apply take look at it :
ConventionalModelMetadataProvider
Old Answer
Here if you wanna support many types of resources:
public class LocalizedDisplayNameAttribute : DisplayNameAttribute
{
private readonly PropertyInfo nameProperty;
public LocalizedDisplayNameAttribute(string displayNameKey, Type resourceType = null)
: base(displayNameKey)
{
if (resourceType != null)
{
nameProperty = resourceType.GetProperty(base.DisplayName,
BindingFlags.Static | BindingFlags.Public);
}
}
public override string DisplayName
{
get
{
if (nameProperty == null)
{
return base.DisplayName;
}
return (string)nameProperty.GetValue(nameProperty.DeclaringType, null);
}
}
}
Then use it like this:
[LocalizedDisplayName("Password", typeof(Res.Model.Shared.ModelProperties))]
public string Password { get; set; }
For the full localization tutorial see this page.
回答5:
I got Gunders answer working with my App_GlobalResources by choosing the resources properties and switch "Custom Tool" to "PublicResXFileCodeGenerator" and build action to "Embedded Resource".
Please observe Gunders comment below.
Works like a charm :)
回答6:
public class Person
{
// Before C# 6.0
[Display(Name = "Age", ResourceType = typeof(Testi18n.Resource))]
public string Age { get; set; }
// After C# 6.0
// [Display(Name = nameof(Resource.Age), ResourceType = typeof(Resource))]
}
- Define ResourceType of the attribute so it looks for a resource
Define Name of the attribute which is used for the key of resource, after C# 6.0, you can use nameof
for strong typed support instead of hard coding the key.
Set the culture of current thread in the controller.
Resource.Culture = CultureInfo.GetCultureInfo("zh-CN");
Set the accessibility of the resource to public
Display the label in cshtml like this
@Html.DisplayNameFor(model => model.Age)