cesAR
cesAR

Reputation: 722

Get localized display name attribute from a class property which use a resource file

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

Answers (2)

cesAR
cesAR

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:

How MyResx will look like

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

hobomaan
hobomaan

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

Related Questions