Localization of DisplayNameAttribute
Here is the solution I ended up with in a separate assembly (called "Common" in my case):
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event)]
public class DisplayNameLocalizedAttribute : DisplayNameAttribute
{
public DisplayNameLocalizedAttribute(Type resourceManagerProvider, string resourceKey)
: base(Utils.LookupResource(resourceManagerProvider, resourceKey))
{
}
}
with the code to look up the resource:
internal static string LookupResource(Type resourceManagerProvider, string resourceKey)
{
foreach (PropertyInfo staticProperty in resourceManagerProvider.GetProperties(BindingFlags.Static | BindingFlags.NonPublic))
{
if (staticProperty.PropertyType == typeof(System.Resources.ResourceManager))
{
System.Resources.ResourceManager resourceManager = (System.Resources.ResourceManager)staticProperty.GetValue(null, null);
return resourceManager.GetString(resourceKey);
}
}
return resourceKey; // Fallback with the key name
}
Typical usage would be:
class Foo
{
[Common.DisplayNameLocalized(typeof(Resources.Resource), "CreationDateDisplayName"),
Common.DescriptionLocalized(typeof(Resources.Resource), "CreationDateDescription")]
public DateTime CreationDate
{
get;
set;
}
}
What is pretty much ugly as I use literal strings for resource key. Using a constant there would mean to modify Resources.Designer.cs which is probably not a good idea.
Conclusion: I am not happy with that, but I am even less happy about Microsoft who can't provide anything useful for such a common task.
Localization DisplayName Attribute
For your custom DataAnnotations attribute your need to write following code in your GetMessageFromResource method:
private static string GetMessageFromResource(string resourceId)
{
var propertyInfo = typeof(DataResource).GetProperty(resourceId, BindingFlags.Static | BindingFlags.Public);
return propertyInfo.GetValue(null, null);
}
This code should do the job assuming you have an error in your question and there are should be LocalizeDisplayNameAttribute, not the DisplayName one:
[DisplayName("NameString")]
public virtual string Name { get; set; }
Anyway I recommend using of lambda accessors for getting localized strings from resources so you can rename/navigate them using your refactoring tool.
Get localized display name attribute from a class property which use a resource file
Finally, I found a solution on my own.
The problem
Then, letting a clear context, what we have is just a class
(from which we can extract its type), and a PropertyName
on a string, and what we want is the the localized DisplayName
of that property of that class, according to a Resource File
assigned on its decoration.
Let's suppose some elements to start. We have the class MyClass
, which has a property called MyProperty
, and which will be localized with the resource file MyResx
:
public class MyClass
{
private string myProperty;
[Display(Name = nameof(MyResx.MyProperty), ResourceType = typeof(MyResx))]
public string MyProperty
{
get { return myProperty; }
set { myProperty = value; }
}
}
The resource file MyResx
, has some localized string for the name MyProperty
, and will look like this:
The solution
// We start with the class type, and the property name on a string
Type classType = typeof(MyClass);
string nameOfTheProperty = "MyProperty";
/* Now we get the MemberInfo of our property, wich allow us to get the
* property metadata, where is the information we are looking for. */
MemberInfo propertyMetadata = classType.GetProperty(nameOfTheProperty);
/* The decorations we used, are "Custom Attributes". Now we get those
* attributes from our property metadata: */
var customAttributes = CustomAttributeData.GetCustomAttributes(propertyMetadata).FirstOrDefault();
/* If we pay attention to our decoration, we defined "Name = nameof(MyResx.MyProperty)"
* and "ResourceType = typeof(MyResx))", so, what we are looking for from our custom
* attribures are those members, Name and ResourceType: */
var customAttributeName = customAttributes.NamedArguments.FirstOrDefault(n => n.MemberName == "Name");
var name = (customAttributeName != null) ? (string)customAttributeName.TypedValue.Value : null;
var customAttributeResourceType = customAttributes.NamedArguments.FirstOrDefault(n => n.MemberName == "ResourceType");
var resourceType = (customAttributeResourceType != null) ? (Type)customAttributeResourceType.TypedValue.Value : null;
/* Now, having the resource file from the decoration, we just create an instance to
* use it: */
var decorationResx = new ComponentResourceManager(resourceType);
// And finally, from our resource file, we get our localized display name
string localizedAttribute = decorationResx.GetString(name);
Extra
I got a lot of important information from the Microsoft reference about the NamedArguments, here: https://learn.microsoft.com/en-us/dotnet/api/system.reflection.customattributedata.namedarguments?view=netcore-3.1
ASP.NET Core DisplayAttribute Localization
You can set the ResourceType
on the DisplayAttribute
which can be used to localize your text.
Add a resource .resx
file to your project e.g. MyResources.resx
, and add a resource for your field:
Then reference the name of the field and the MyResources
type in your DisplayAttribute
[Display(Name = "RememberMe", ResourceType = typeof(MyResources))]
public bool RememberMe { get; set; }
The localized resource will be pulled through automatically (see the text box)
Localizing my display name attribute but can't reference my resource
It appears the issue is that you are specifying a locale for the resource file without having a default one present in your solution.
DisplayName.resx
would be the default language of your application and is needed for the resolution mechanism to work
DisplayName.<locale code>.resx
will then override the default locale resources for that particular locale.
MVC3 Localized display name attribute by property name
It works fine for me.
[DataType(DataType.EmailAddress, ErrorMessageResourceName = "ThisFieldIsRequired", ErrorMessageResourceType = typeof(Resource))]
[Required(ErrorMessageResourceName = "ThisFieldIsRequired", ErrorMessageResourceType = typeof(Resource))]
[RegularExpression(@"^[\w-]+(\.[\w-]+)*@([a-z0-9-]+(\.[a-z0-9-]+)*?\.[a-z]{2,6}|(\d{1,3}\.){3}\d{1,3})(:\d{4})?$", ErrorMessageResourceName = "ThisFieldIsRequired", ErrorMessageResourceType = typeof(Resource))]
[Display(Name = "EmailID", ResourceType = typeof(Resource))]
public string EmailID { get; set; }
Localize DisplayNameAttributes in ActionFilter?
Don't change attribute values at runtime. In the best case this will be a no-op (as you'll be operating on copies of the attribute instances); in the worst case this will lead to race conditions in your code. Always treat attribute instances as immutable.
If you need to localize [DisplayName], subclass it and override the virtual DisplayName property. See Localization of DisplayNameAttribute for an example of how to do this.
Apply localization through resource files to DisplayAttribute
You should use Resources.
In attributes you can set only immutable values.
Try this.
[Display(Name = "Remember me?", ResourceType = typeof(YourResourcesType))]
public bool RememberMe { get; set; }
And look this
ASP.NET MVC 3 localization with DisplayAttribute and custom resource provider
Related Topics
How to Find If a Native Dll File Is Compiled as X64 or X86
How to Convert a Decimal to a Double in C#
Converting Dd/Mm/Yyyy Formatted String to Datetime
Is There Any Benefit of Using an Object Initializer
Get the Titles of All Open Windows
Why Can't a Duplicate Variable Name Be Declared in a Nested Local Scope
How to Add Assembly References in Visual Studio Code
Deserializing JSON with Dynamic Keys
Format of the Initialization String Does Not Conform to Specification Starting at Index 0
Uwp Binding in Style Setter Not Working
How to Programmatically Fill in a Form and 'Post' a Web Page
C# Version of Openssl Evp_Bytestokey Method
C# Dictionary - One Key, Many Values
Order of Event Handler Execution
What Strategies and Tools Are Useful for Finding Memory Leaks in .Net