Reputation: 722
Introduction
I have a class, which has properties localized through data annotations by a resource file, like this:
[Display(Name = nameof(ResxFile.SomeProperty), ResourceType = typeof(ResxFile)]
public string SomeProperty { get; set; }
Where ResxFile
is a .resx file, and I'm using Name = nameof(ResxFile.SomeProperty)
to get the name property of the resource file row (to make it strongly typed), and ResourceType = typeof(ResxFile)
to indicate which is the resource file to use.
In my ResxFile
, for the previous example, I would have something like:
Name | Value
------------------------------------------
SomeProperty | Some property localized
And in this way, for example, I can bind my class to a grid, and the column names will be localized according to the content of the resource file.
Question
I'm working with a kind of dynamic mapping, where I use the property names of my classes, and in general I get them with something like this: string propertyName = typeof(MyClassName).GetProperty(myPropertyName).Name
In this case, what I need, is the localized name assigned to that property, according to the resource file. To be more clear: string localizedPropertyName = typeof(MyClassName).GetProperty(myPropertyName).SomeMagic();
where localizedPropertyName
would be "Some property localized"
I've been looking in CustomAttributes
, but I only could get display name attributes, and some types, and that lands me in another job, which is invoke the resource file to get the value of a name.
I'm using .Net Framework 4.7.
Thanks in advance!
Upvotes: 0
Views: 2681
Reputation: 722
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
Upvotes: 3
Reputation: 151
Hopefully this helps you as in the past I have used this method to translate keys in a database. This does not cover the pulling out data from the resource file, but you can either declare [Display]
attribute on a property and use the full name as the key or give a static string as the key to use later in the meta data provider.
Add your own meta data providor
public class MyMetadataProvider : DataAnnotationsModelMetadataProvider
{
protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes,
Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
{
var metadata = new ModelMetadata(this, containerType, modelAccessor, modelType, propertyName);
//Do what ever you want here to translate either by the property name or the display attribute key
if (propertyName != null)
{
var displayAttribute = attributes.OfType<DisplayAttribute>().FirstOrDefault();
if (displayAttribute != null)
{
//Translate using the key you provided before however you like
metadata.DisplayName = TranslateFunction(displayAttribute.Name);
}
}
return metadata;
}
}
add the translation key to the prop
[Display(Name = "ResourceKey")]
public string Something { get; set; }
Add this to application start up
protected void Application_Start(object sender, EventArgs e)
{
ModelMetadataProviders.Current = new MyMetadataProvider();
}
Upvotes: 0