How to use c# 'DisplayNameAttribute' with dynamic values? - c#

Want to display Name & country field in localized language.
In our application, we support various language based on the user preferred language, the translation has to happen for below model properties. Thought of encapsulating the translation with the "custom attribute" class but how to get the "Name", "Country" properties values in the custom attributes, so that I can find the translation word match for the given property value and assign to the 'DisplayName' property of 'DisplayNameAttribute' type.
If 'DisplayNameAttribute' type can't be used for dynamic displayname value, suggest me how the localization can be achieved using c# attributes or there better way to do localization.
P.S: our translation files are stored in the Azure bolob as .json files not as .resx files as part of app build package
class Sample
{
[LocalizedDisplayName()]
public string Name { get; set; }
[LocalizedDisplayName()]
public string Country{ get; set; }
}
internal sealed class LocalizedDisplayName : DisplayNameAttribute
{
private readonly string _dependentPropValue; //how to get this??
private string displayName;
public LocalizedDisplayName(string dependentPropValue)
{
_dependentPropValue = dependentPropValue;
}
public override string DisplayName
{
get
{
return displayName = DoTranslation(dependentPropValue);
}
}
}

Related

How to use localization with data annotation in a separate DTO Assembly (Asp.net Core Web Api)

I have:
a Dto.Library (.Net 5 Library)
a SharedResourceLibrary with Resource.resx (.Net 5 Library)
How can i use the Resource File Messages in conjunction with Data Annotation in my DTO.Library?
The ErrorMessage should be the text from the resx files:
public class MeterForCreationDto
{
[Required(ErrorMessage = "Name must not be empty!")]
public string Name { get; set; }
[Required(ErrorMessage = "Unit must not be empty!")]
public string Unit { get; set; }
}
SharedResourceLibrary: looks like this answer #Shiran Dror
You can create a custom attribute for the properties. Something like this:
public class LocalizedDisplayNameAttribute: DisplayNameAttribute
{
public LocalizedDisplayNameAttribute(string resourceKey)
: base(GetMessageFromResource(resourceId))
{ }
private static string GetMessageFromResource(string resourceKey)
{
// return the translation out of your .rsx files
}
}
and then you need to add
public class MeterForCreationDto
{
[LocalizedDisplayName("Name")]
public string Name { get; set; }
[LocalizedDisplayName("Unit")]
public string Unit { get; set; }
}
but you need to add exactly the same key in the attribute which is in your .rsx file. If your searching for "asp.net localizeddisplayname" there are a lot of different sites with examples.
Some help for creating custom attributes:
https://learn.microsoft.com/en-gb/dotnet/standard/attributes/writing-custom-attributes
Hopefully, it helps. :)
you can use:
[Required(ErrorMessageResourceName ="NameInRecourseFile",ErrorMessageResourceType =typeof(RecourseFileName))]
also u need to make the Recourse file public from this menu:
finally u need to have a default Resource file[for your default culture] Default resource file name shouldn't have any specific culture (.en،.fr،....)
SharedService.en.resx => SharedService.resx note .en is the default culture in your app
so it will like these:
SharedService.resx[for your default culture]
SharedService.ar.resx
SharedService.fr.resx
Hope this helped you.
Best wishes.

Localise Display DataAnnotation without the Name Attribute in .NET Core 5 ViewModel

I've
[Required]
[Display(Name ="Email")]
public string Email { get; set; }
[Required]
[Display (Name = "Password")]
public string Password { get; set; }
In my ViewModel. I'm able to localize this. Additionally, I was able to put a different localization to the 'Required' message [without specifying ResourceType and ResourceName manually] than the default Microsoft message using the resource file. How I did that? Here is the link:
https://stackoverflow.com/a/41385880/931305
Now, I want to remove the 'Name' attribute of the 'Display'. Because most of the time Display Name is always going to be the same as the actual Property name. If you notice both are 'Email'/'Password'. So it will make the code looking clean.
I was able to do this in classic ASP.NET MVC. Here is the link:
https://haacked.com/archive/2011/07/14/model-metadata-and-validation-localization-using-conventions.aspx/
Now, how do I do this in .NET Core 5? I'm unable to use IValidationAttributeAdapterProvider to inject 'Display'. [I was getting all 'validation' attributes, but not Display]
The best and standard solution is to using localization in ASP.NET Core application.
In summary, the steps to localize your application are rough as follows:
Add the required localization services
Configure the localization middleware and if necessary a culture
provider
Inject IStringLocalizer into your controllers and services to
localize strings
Inject IViewLocalizer into your views to localize strings in views
Add resource files for non-default cultures
Add a mechanism for users to choose their culture
Take a look at this article for a detailed walkthrough.
You could write an attribute like so
public sealed class MyDisplayAttribute : DisplayNameAttribute, IModelAttribute
{
private string _name = string.Empty;
public MyDisplayAttribute(string displayName)
{
_name= displayName;
}
public override string DisplayName => _name;
public string Name => nameof(MyDisplayAttribute);
}
usage:
public class MyModel
{
[MyDisplay("MyString")]
public string MyString { get; set; }
}

IOptions binding with non-matching property names

Is is possible to bind properties from a JSON file (appsettings.json) to a class that uses different property names?
{
"WebTarget": {
"WebURL": "http://www.stackoverflow.com"
}
}
public class MyServiceOptions
{
public string Url { get; set; }
}
I want to take the WebURL setting and map it to the Url property in the options class. I've tried [DataMember] and [JsonProperty] but they don't work.
I know it's not ideal and the property names should match what's in the JSON but this one is a special case.
Yes it is possible. It requires a little more manual configuration
services.Configure<MyServiceOptions>(myOptions => {
myOptions.Url = Configuration.GetSection("WebTarget").GetValue<string>("WebURL", string.Empty);
});
Reference Configure simple options with a delegate

Automagically handling multi-lingual properties in a .NET class

I am trying to handle multiple languages in an ASP.NET Webforms (.NET 4.5, C#) application of mine.
Basically, some of my entities in my SQL Server 2012 database have properties like Name or Description which exist in three languages - German, French, Italian.
Those are stored as columns Name_De (German), Name_Fr (French), and Name_It (Italian).
When I create my .NET objects from the database, of course, I also get those three properties in my entity class. But for displaying on screen, in a grid for instance, it would be nice if I could somehow "magically" always show the "right" language. This should be based on the Thread.Current.CurrentUICulture.TwoLetterISOLanguageName (which returns de, fr or it, depending on the browser's language preferences).
So I was hoping to somehow be able to create e.g. a .NET attribute that would allow me to do something like this:
Base "Module" entity - generated from existing SQL Server database:
public partial class Module
{
public string ModuleCode { get; set; }
public string Name_De { get; set; }
public string Name_Fr { get; set; }
public string Name_It { get; set; }
... // other properties
}
Partial extension in a separate file
public partial class Module
{
[Multilingual]
public string Name { get; set; }
}
The base idea is: I can access the Module.Name property, and depending on the current setting of CurrentUICulture, either the value of Name_De, Name_Fr or Name_It would be fetched, when I access the getter of the Name property.
Can something like this be done in C# ? I have looked at a lot of custom attribute implementations, but nothing ever seemed to be doing something like this...
Assuming you are using two separate entities (one generated by your SQL entities and one "business entity" which only contains a Name property), are you open to using something like AutoMapper ?
If you are, then you could tweak your resolve function to map the entity depending on the current thread culture.
switch(Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName.ToUpperInvariant())
{
case "DE":
return dto.Name_De;
case "FR":
return dto.Name_Fr;
// ...
default :
return String.Empty;
}
which would work for your scenario.
If this is a solution that could work for you, I think this question is very close to what you're looking for : Automapper Mapping for Localization Resolver in a Multi-Language Website
If you do go down the custom attribute route, you will have to deal with Reflection stuff and string parsing I'm afraid. AFAIK, there is no built in way to do this with the localization functions provided by .NET. AutoMapper will hide that from you.
The problem with custom attributes in this case is that you are still trying to access the Name property. You are trying to "shadow" the default behaviour of the property by making it access other properties. If I understand correctly you want the Multilingual custom attribute to turn your property into :
public String Name
{
get
{ switch(Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName.ToUpperInvariant())
{
case "DE":
return dto.Name_De;
case "FR":
return dto.Name_Fr;
// ...
default :
return String.Empty;
}
}
}
If that's correct, then you won't be able to do that easily with attributes, simply because the attribute will never be aware of the existence of the Name_De property.
Other option that still isn't quite what you're looking for :
void Main()
{
Module mod = new Module();
mod.Name_De = "name";
mod.Name_Fr = "nom";
// This is the unfortunate nasty bit. I address the property using its name
// in a String which is just bad. I don't think there is a way
// you will be able to address the ".Name" property directly and have
// it return the localized value through your custom attribute though
String localizedValue = mod.GetLocalizedProperty("Name");
}
[AttributeUsage(AttributeTargets.Property)]
public sealed class MultilingualAttribute : Attribute
{
public MultilingualAttribute()
{
}
}
public static class ModuleExtensions
{
public static String GetLocalizedProperty(this Module module, String propName)
{
var type = typeof(Module);
var propInfo = type.GetProperty(propName);
// Make sure the prop really is "Multilingual"
if(Attribute.IsDefined(propInfo, typeof(MultilingualAttribute)))
{
String localizedPropName = propInfo.Name;
switch(Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName.ToUpperInvariant())
{
case "DE":
localizedPropName += "_De";
return type.GetProperty(localizedPropName).GetValue(module, null).ToString();
case "FR":
localizedPropName += "_Fr";
return type.GetProperty(localizedPropName).GetValue(module, null).ToString();
}
}
return String.Empty;
}
}
public class Module
{
public String Name_De {get; set;}
public String Name_Fr {get; set;}
[Multilingual]
public String Name {get; set;}
public Module()
{
}
}
I don't know of a more powerful way to use custom attributes for what you're looking for unfortunately. Quite frankly, I don't think this is a good solution, only posted because I was trying to see what I could do with custom attributes. There is no real point in using this code over a more "normal" property which would do the same thing in a clearer way (without attributes). As you say in your original question, your goal is to intercept the call to the Name property and this doesn't achieve it.

Global Resources in ASP.Net MVC Model ErrorMessage Attribute

I am creating web application in ASP.Net MVC 5.
I need to add user defined languages. (So, it can work in any language).
I have added English text/messages in resources file.
While, for other languages , resources will be generated run time in App_GlobalResources folder.
With this custom resources, I can able to show labels (, buttons etc.) as per selected language.
But, I have issue with ErrorMessage , which is given as attribute in properties of model.
Model classes are in class library, and reference of class library project is added in MVC.
So, can't access resources from App_GlobalResources folder.
And, if I add resources under Project of model class, I can give customize message with following code.
[Required(ErrorMessage = "*")]
[System.Web.Mvc.Compare("Password", ErrorMessageResourceType = typeof(Resources), ErrorMessageResourceName = "PasswordCompare")]
public string ConfirmPassword { get; set; }
But, with this code, I can't use App_GlobalResources.
What will be solution in this scenario?
Finally, I got solution:
Created custom attribute class.
[Required(ErrorMessage = "*")]
[CompareCustomAttribute("Password", ClassKey = "Resources", ResourceKey = "PasswordCompare")]
public string ConfirmPassword { get; set; }
Custom Attribute class is inheriting CompareAttribute class.
public sealed class CompareCustomAttribute : System.Web.Mvc.CompareAttribute
{
public CompareCustomAttribute(string otherProperty)
: base(otherProperty)
{
}
public string ResourceKey { get; set; }
public string ClassKey { get; set; }
public override string FormatErrorMessage(string name)
{
return Convert.ToString(HttpContext.GetGlobalResourceObject(this.ClassKey, this.ResourceKey));
}
}
In overridden FormatErrorMessage method, I have put code to get customized error message from Global Resources.

Categories