DisplayName attribute from Resources?
Solution 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; }
}
Solution 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)
Solution 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; }
Solution 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.
Solution 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 :)
Related videos on Youtube
Palantir
Updated on May 15, 2020Comments
-
Palantir almost 4 years
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!
-
Palantir about 14 yearsYes, I worked on a similar solution this morning and it it feasible. Then I found this post which has the same approach: adamyan.blogspot.com/2010/02/…
-
kingdango over 12 yearsThis is good for the specific example used here but it won't work for the majority of dynamic property setters that want strings.
-
321X about 12 years@Gunder his post, below this one (with the most votes), is a much nicer solution. Just for people who only read the Accepted posts
-
kbvishnu over 11 yearsThis is good when we have all our locale with us before deploying to production. But if I want to call db for db strings then this kind of approach is not proper. Also the disadvantage of resource file, is that it requires compilation again to effect the changes, that means you need to release another version to client. I am not saying this as a bad approach, please dont feel like that
-
Kek over 11 yearsJust a problem : we have to know resource type in the model. I have a model DLL and a website in two distinct projects. I'd like to be able to set display names in model and set resource type in website...
-
Fischer over 11 yearsThis actually does NOT work for accessing different translations since it will return the same value for all users no mater what. Store resourceid in a local variable and override DisplayName instead
-
Leonel Sanches da Silva about 11 yearsSuggestion for TODO: return Resources.Language.ResourceManager.GetString(resourceId);
-
anIBMer almost 11 yearsGet Resharper, and create the resource file in your project, and it will automatically remind and then help you to move the Name into the resource file.
-
René almost 11 yearsThis results in a resource file that will be compiled just as if you had added a resource file outside of App_GlobalResources. So your resource file will no longer behave as a "App_GlobalResources" resource file, which is absolutely fine. But you should just be aware of it. So you no longer have the "benefits" of putting the resource file in the App_GlobalResources. You could just as well have put it somewhere else.
-
Frank Boucher over 10 yearsYou should use: [Display(Name = "labelForName", ResourceType = typeof(Resources.Resources))] as describe below...
-
Nilzor about 10 years+1. Haack's solution is definitely the most elegant one compared to the others here. It fits very well into the convention-based style of programming in ASP.NET MVC and is easily implemented through a single nuget-command and a single line of code in Global.asax.cs.
-
philreed over 9 yearsI think this solution requires
Resources.Resources
to expose potentially hundreds of static properties. Am I wrong, or is there a way around this? Something likeGetResource(string key, string culture)
-
René over 9 yearsWell that's how resource files work: they expose the strings as static properties. What are your concerns about that? Organization? If that's the case, you can divide your strings into multiple resource files.
-
Piotr Kula about 9 yearsYea this worked for the name. Now how do I replace the Required error message? Tutorials show to use the resource file directly, I dont get how we getting errors now? Darins solution is good but it means we now need to create Localised attributres... for all the attributes.. arh!
-
NoOne about 9 yearsTo tell you the truth, missing the
ResourceType = typeof(Resources.Resources)
part of the built-in ASP.NET attribute would make my models much more readable... I wish MS had provided a static property for setting the defaultResourceType
for all their attributes. I mean, I can barely think of a project that I've worked on which required using multipleResourceType
s... And even if one needs to do that, he could always override the default. It's definitely much better that throwing an error when theResourceType
is missing. -
ajbeaven over 8 years@321X forgive my ignorance but why is this a much nicer solution? This approach gives much more flexibility and hugely improves readability.
-
Memet Olsen about 8 yearsCalm down people. At the time of writing this answer, the
DisplayAttribute
didn't even exist. -
John almost 8 years@ajbeaven Because as Fischer pointed out, it always only ever uses one culture, the culture that the attribute was instantiated with. Therefore, it's really pointless.
-
ajbeaven almost 8 years@John couldn't the culture of the app determined at the start of each new thread? So if you determine the user's culture and set it (perhaps based on a URL as per most i18n solutions) then this would change for different users?
-
John almost 8 years@ajbeaven All threads use the same instance of the attribute and also threads are reused for many requests.
-
Uwe Keim almost 8 yearsWith C# 6, instead of
Name = "labelForName"
you can also useName = nameof(Resources.Resources.labelForName)
. -
Dark_Knight almost 8 yearswill this work with Winforms? I have a class which i added Display annotation from resources and i used GetAttributeFrom method from link to get the name of the property but it doesn't show the localized one!
-
Tikall almost 8 yearsAre you using attribute.Name or attribute.GetName() to get the localized text? Documentation for .Name says "Do not use this property to get the value of the Name property. Use the GetName method instead." msdn.microsoft.com/en-us/library/…
-
Dark_Knight over 7 yearsYes, i figured it out that i had to use GetName(). Thanks
-
Sandeep over 5 yearsOn similar lines I found this article explaining this. codedigest.com/posts/29/… Just for few of users like me who may not be sure the hard-coded string in following "labelForName" is the Name in your resource file and its value could be "Users Name" in english or "उपयोगकर्ता नाम" in hindi or "Nombre de usuario" in spanish. [Display(Name = "labelForName", ResourceType = typeof(Resources.Resources))]