ASP.NET Core DisplayAttribute Localization - asp.net-core

According to the documentation:
The runtime doesn’t look up localized strings for non-validation attributes. In the code above, “Email” (from [Display(Name = "Email")]) will not be localized.
I'm looking for a way to localize text in DisplayAttribute. Any suggestions to do it in a proper way(s)?

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)

Having a Central location for all your localization whether in view or dataannotations is the best approach I can think of, and this how I got to work.
In Startup.cs file after you installed nuget packages for localization add the following code
services.AddMvc().AddViewLocalization().AddDataAnnotationsLocalization(options =>
options.DataAnnotationLocalizerProvider = (type, factory) => new StringLocalizer<Resources>(factory));
services.Configure<RequestLocalizationOptions>(options => {
var cultures = new[]
{
new CultureInfo("en"),
new CultureInfo("ar")
};
options.DefaultRequestCulture = new RequestCulture("en", "en");
options.SupportedCultures = cultures;
options.SupportedUICultures = cultures;
});
This way the DataAnnotationLocalizerProvider will be from the Resources.{culture}.rex -( The Resource file must have an access modifier of No code gen)- assuming that no resources will be needed for the default language, and to be able to access the resource file since no code will be generated and empty class with the same name must be created.
and in _ViewImports.cshtml file inject the following
#inject IHtmlLocalizer<Resources> Localizer
by doing this you now have a global variable Localizer to be used in any of the views for localization purposes.
you can find further information on Globalization and localization in ASP.NET Core

For those who struggle (#lucius, #vladislav) with error:
Cannot retrieve property 'Name' because localization failed. Type 'Xxxx.EmployeeResx' is not public or does not contain a public static string property with the name 'FirstName'.
It is caused by access modifier on .resx files which is by default set to Internal (in my case it was No code generation). Change it to public in Access Modifier dropdown in the resource file toolbar.
After that you should be able to see the properties from the resource type:
Also, consider not using special signs in field names as they are a basis for auto-generated C# property names. The field names are converted into C# friendly names and that is why you can end up with inconsistency between name of resource file field and name of auto-generated property. Best to avoid any hyphens - or dots . Underscores _ are fine. You can always look up how the auto-generated properties look like in resource_file_name.Designer.cs class under the related resource file.
Many thanks to Bala Murugan who wrote a good article concerning this topic on Code Digest.

Actually I found an simple solution for the followers. The display name in most of time is used in the label of an input field. So do this if you like:
<label asp-for="Email">#Localizer["Email"]</label>
of course, you can pass the property name by #Html.DisplayNameFor, but most of time, this one already works well.

I have just created a project which demonstrates localization including localization of Display attribute for class properties as well as enums.
The project can be found here https://github.com/feradz/ASPNetCoreLocalization/wiki
The Display attribute has to be localized using the approach prior to ASP.NET Core 1.0. Have a look at the DataAnnotations.resx file in the project.
The Name property of Display cannot contain empty spaces, and special characters.
[Display(Name = "NoSpacesAndSpecialChanractersHere", ResourceType = typeof(Resources.DataAnnotations))]
public string FirstName { get; set; }
ResourceType should be the fully qualified resource class name (i.e. including the name space).

Related

How to reference a resource file from a Model in ASP.Net Core (3.1)?

So, I read all about the new localization system in ASP.Net Core (3.1) and successfully made use of the IStringLocalizer<MyController> and the IViewLocalizer<MyView>. I also could use the localization for the [DisplayName("Property description") in Models.
Below what I seem unable to do:
In good old .Net Framework I could do this:
public class Month
{
public int MonthNumber { get; set; }
public string Name
{
get
{
switch(MonthNumber)
{
case 1:
return Properties.Resources.Jan;
case 2:
return Properties.Resources.Feb;
default:
return "?";
}
}
}
But how can I do this in a Model in ASP.Net Core 3.1?
I solved it thus:
Just add the resource files following the structure you have chosen. All docs and tutorials suggest you take a folder named "Resources" as your base folder, so that is what you see here.
The link that is most probable to survive over time that explains how to use resources in an ASP.Net Core project:
Microsoft docs on Localization for ASP.Net Core
Make sure that you mark all three "Month" resx files as Public:
Visual Studio will at first complain with the message:
Custom tool PublicResXFileCodeGenerator failed to produce an output for input file 'Month.en.resx' but did not log a specific error.
Simply get rid of this error by restarting Visual Studio!
Now you can use the resources as follows:
public string Name
{
get
{
return MonthNumber switch
{
1 => Resources.Models.Month.Jan,
2 => Resources.Models.Month.Feb,
_ => "?"
};
}
}
You need to inject IStringLocalizer to the class:
public class Month
{
public int MonthNumber { get; set; }
private readonly IStringLocalizer Localizer;
public Month(IStringLocalizer localizer)
{
Localizer = localizer;
}
public string Name
{
get
{
switch(MonthNumber)
{
case 1:
return Localizer["Jan"];
case 2:
return Localizer["Feb"]
default:
return "?";
}
}
}
}
Another approach can be done by adding localized month names for the numbers in the resource file, so:
var monthName = Localizer["4"];
// result: April for English culture
// or Nisan for Turkish culture
Just for clarification;
The resource key can have three types of access modifiers:
Internal
Public
No code generation
If the key is marked with Internal or Public you can access it as you mentioned, because the compiler will auto generate a static class .cs linked to the relevant resource file with the key names as accessible properties.
But the common approach with Asp.Net Core is to work with Shared resources, and shared resource has the access modifier as No code generation; so thats mean no peoperty keys will be generated at the backend (.cs will not be generated). And in this case you have to inject the IStringLocalizer or whatever locaizer in use to the class.
So changing the key access modifier to Internal or Public can work as well, but it is not a best practice ;)

ResourceLoader returns wrong language inside ViewModel

I'm making a UWP application with Template10, and I currently have this folder structure inside my project for localization:
Localization
|_en
| |_Resources.resw
|_it-IT
|_Resources.resw
The default app language is en, and my current culture is it-IT.
The resource files are correct and the behavior is consistent through the app, showing always the Italian version first.
I'm using the ResourceLoader class inside View's code behind just fine, but as long as I use it inside the ViewModel it always returns the English localization, even if it doesn't exist (= empty string, although it exists in Italian file).
I'm using it inside the VM is because I'm creating a new Character and giving it a default name ("No name"), as a placeholder for the user, binding the name inside the View.
This is the code I use:
public class CharacterManagerViewModel : ViewModelBase
{
public Character Character { get; set; }
private ResourceLoader loader = new ResourceLoader();
public override async void OnNavigatedTo(object parameter, NavigationMode mode, IDictionary<string, object> state)
{
if (parameter == null)
{
//Creating a default empty character
Character = new Character();
Character.Name = loader.GetString("NewCharacterName");
}
else
{
//Loading from the Model
}
}
}
As I call the GetString() method, the current culture is it-IT but somehow the framework ignores it.
What am I doing wrong?
I found the origin of my problem:
inside the Model, in a method I cut in the code for simplicity, I load some data for the "Character" object from XML files included in the app.
Inside that method I change the culture to InvariantCulture to ensure the format is independent from the user's one.
At the end of that method, I change it back to user's culture but somewhat something goes wrong.
The issue happens after I make the double swap, and it is reflected afterwards to the entire software, even for localized strings from the View.
The issue is not dependent to the ViewModel, I'll work on the issue and if I cannot find anything I'll post a new question.

Piranha CMS on existing MVC 5 site

I am new to Piranha CMS (and in any CMS in general). I have a question about integrating the system to an existing MVC 5 application. Here is what I have done so far:
I have followed the steps in the Getting Started section
I have created the database and set up the connection string
I have created the admin user and can log in to the manager interface
What I am having trouble is understanding how to bring my existing pages into the manager and how to expose them to the users for editing. I did look into the documentation, but could not find anything dedicated to this topic.
Any help is greatly appreciated.
Thank you.
Daniel D.
It's really simple getting data from Piranha CMS into your existing application.
1. Page types
First of all, take a look at the different page types you need to create (i.e the different kind of page structures you need) and create them, either from the manager interface or by code. You'll find the docs here:
http://piranhacms.org/docs/pages/page-types
http://piranhacms.org/docs/extend/page-post-types
2. Create your pages
Next up, just create the pages you need in the manager and add the content. If you want to prohibit the users to add pages, you can always remove the "Add" buttons later, either by injecting CSS into the manager or by customizing it.
3. Adding the data to your models
Here you have two options, either you let your model inherit from the PageModel, or your just add a new property with the CMS data to your existing model.
// With inheritance
public class MyModel : Piranha.Models.PageModel {
...
}
// With the CMS data in a property
public class MyModel {
public Piranha.Models.PageModel CMSData { get; set; }
}
4. Getting the data
Now populating the data is just a calling a method. You can either access it by id or by permalink. Let's for example access it by permalink and let's say we have a page with the permalink about-us. Just add the following to your controller:
// With inheritance
public class MyController : Controller {
public ActionResult MyAction() {
// Get the CMS-data
var model = Piranha.Models.PageModel.GetByPermalink<MyModel>("about-us");
// Fill the model with your custom data
...
}
}
// With the CMS data in a property
public class MyController : Controller {
public ActionResult MyAction() {
// Create and fill your custom model
var model = new MyModel();
...
// Get the CMS-data
var model.CMSData = Piranha.Models.PageModel.GetByPermalink("about-us");
}
}
5. Configuration
Note that if you want your application to control the routing your should configure Piranha CMS to run in passive mode. You can find the docs on configuration here:
http://piranhacms.org/docs/api-reference/configuration
I hope this helps you get started!
Regards
/Håkan

How to hide a Model class field based on custom logic in MVC Web Api RC

I am using Asp.Net Mvc Web api RC.
I wanted to hide the fields/properties of my model class using custom attribute. Below is my class:
public class Employee
{
public int EmpId { get; set; }
public string Name{ get; set; }
//Wanted to hide this attribute based on custom logic. Like for a certain role, i want to hide the designation
public string Designation{ get; set; }
public string Department{ get; set; }
}
How can we achieve using Data Annotations. I mean i wanted to create a separate attribute to use in this manner:
[HideForRoles(Roles="Admin,Writer")]
public string Designation{ get; set; }
UPDATE :
As i am developing web api. The response is serialized to either XML or Json format depend upon the formatter. So better question would be how not to allow the fields to be serialize while writing to the response.
However one option could be using IgnoreDataMember attribute. Like
[IgnoreDataMember]
public string Designation{ get; set; }
But the above is a compile time declaration where i cannot impose any condition.
Question: How to ignore the field/property while serializing based on some condition at runtime?
Totally missed on the first go-round that you were using Web Api, my apologies.
What you want to do is to create a custom formatter.
There's a good article here on the flow/differences between MVC and Web Api (which I'm getting that you already understand, still some valid points here):
http://lostechies.com/jimmybogard/2012/04/10/asp-net-web-api-mvc-viewmodels-and-formatters/
And here's a sample implementation of a custom formatter:
http://www.tugberkugurlu.com/archive/creating-custom-csvmediatypeformatter-in-asp-net-web-api-for-comma-separated-values-csv-format
Building from that, you would use reflection to read from the attributes, building on the custom ActionFilterAttribute you would have to write, where you evaluate the user's roles and determine which fields should be omitted/included. Here's a sample of an action filter:
https://github.com/MisterJames/MovieFu/blob/master/MovieFu/ActionFilters/UserNameFilter.cs
Hope this helps more.
Cheers.
Your best bet is to return a dynamic object. In this case you can say:
dynamic viewModel = new ExpandoObject();
viewModel.Id = 12;
if(role == "Admin")
{
viewModel.SecureStuff = "Others should not see it";
}
It won't be as straightforward as that, as you'll need to have the fields conditionally rendering in the view. But you can get most of the way there through the attribute.
You will need to make your custom attribute meta-data aware, then check the attribute in your view. A solution is posted here: Can't get Custom Attribute Value in MVC3 HTML Helper.
Cheers.
I have done the authorization checking in the model repository itself. Rather ideal way was to create custom formatters for hiding the certain fields based on some condition.
After getting the list of Employees from db and have them in list, i iterated again and place a NULL to the fields i don't want to display.
The code i have written as:
foreach (var employee in listEmployees)
{
//get all props. of Employees object using reflection
var props = employee .GetType().GetProperties();
//loop through each field to match with the field name to remove/place null
foreach (var propertyInfo in props)
{
var fieldName = propertyInfo.Name;
if (fieldsNamesToRemove .Contains(fieldName))
{
propertyInfo.SetValue(employee , null, null);
}
}
}
here fieldsNamesToRemove is a list that i created dynamically based on roles of current user.
This solution actually placing a NULL for the fields we do not want display. As a result in JSon format the fields are not displaying but in the XML the fields are displaying with syntax like lt; Designation i:nil="true"/ gt;, but manageable as we need to deal mostly with json response.
Thanks Ali and MisterJames for your valuable suggestions

What are the parameters for the Salesforce WebServiceCallout.invoke method?

I would like to know the parameters for the invoke method used by Salesforce to invoke remote web services. I have a service that I'm suposed to be able to invoke, but the service WSDL does not define the security requirements, so I'm hoping I can add that information manually (The services uses WS-Security passed through Soap headers).
Here is what I (think I) know so far:
WebServiceCallout.invoke(
Class servicePort, //Usually set to "this", contains httpheader info as well as ?
request_x, //Request object, defining schema, properties, and field order
response_map_x, //Response object, defining schema, properties, and field order
new String[]{
String endpoint, //Endpoint of the service
String ?, //what is this?
String methodSchema, //Schema for the request object?
String method, //Name of the request method?
String responseSchema, //Schema for the response object?
String response, //Name of the response object?
String responseClass} //Name of the Apex class the response will be converted to
);
Can anyone help fill in the gaps?
Here's what I have discovered so far for WebServiceCallout.invoke:
Object servicePort - A class with the following variables:
String enpoint_x: containing the service endpoint (not sure if necessary)
Map<String,String> inputHttpHeaders_x: custom httpHeaders
Map<String,String> outputHttpHeaders_x: I think this is the httpHeaders that were returned
String clientCertName_x: Used in configuring an SSL cert?
String clientCert_x: Used in configuring an SSL cert?
String clientCertPassword: Used in configuring an SSL cert?
Integer timeout_x: How long (in milliseconds?) to wait for the response
String[] ns_map_type_info: The first String is the namespace of the service schema, the second is the name of the object that contains the Apex classes defining the schema objects
Object request_x - The Apex object that will form the XML schema object
Map<String, Object> response_map_x - Object is the object that the result is to be unserialized into. String is the name of Object variable.
String[] {
endpoint - The service endpoint
soapAction - If the service call requires a soapAction, put it here. Otherwise leave blank.
methodSchema - Schema for the request object
method - Name of the request method
responseSchema Schema for the response
responseClass The Apex class that the response will be unserialized into
}
In addition, Soap headers can be inserted by creating an object in the servicePort class as well as
a String with the same variable name+"_hns" that specifies the namespace for that object:
public SoapSecurity Security;
private String Security_hns = "Security=http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
The apex XML Schema objects should contain variables for each child element (or attribute). Arrays whose variable names match certain patterns define how the object variables are used in the xml.
Given the following example XML:
<foo a="b"><bar>baz</bar></foo>
The Apex classes would be something like this:
public class MyService {
public class bar {
public String bar;
private String[] bar_type_info = new String[] {'bar','http://www.w3.org/2001/XMLSchema','string','0','1','true'};
private String[] apex_schema_type_info = new String[] {'http://schema.myservice.com', 'false', 'false'};
private String[] field_order_type_info = new String[] {'bar'};
}
public class foo {
public MyService.bar bar;
public String a;
private String[] bar_type_info = new String[] {'bar','http://schema.myservice.com','bar','0','1','true'};
private String[] a_att_info = new String[] {'a'};
private String apex_schema_type_info = new String[] {'http://schema.myservice.com','false','false'};
private String[] field_order_type_info = new String[] {'bar'};
}
}
Here's a (brief) breakdown of these objects:
If the variable represents another XML element or a text node, then there needs to be a matching _type_info String[] e.g. bar_type_info. The elements of this array are:
1. XML element name
2. Schema
3. XML type
4. minOccurs
5. maxOccurs (set to '-1' for unbounded)
6. isNillable
If the variable represents an attribute, then there must be a matching _att_info String[] e.g. a_type_info. Thise simply contains the XML name of the attribute.
Note that if an class variable name is a reserved word, then _x is appended to it e.g. bar_x. This would affect the other variables names: bar_x_type_info. The Apex Developer's Guide explains their rules for names, but if you are manually creating it, I think you can give it whatever name you want--the arrays determine the XML element name...
I have not found a way to represent a simple XML type that also contains an attribute: e.g.
<foo bar="baz">bar</foo>
The apex_schema_type_info array specifies information about the XML element represented by the class:
1. Schema
2. 'true' if elementFormDefault="qualified"
3. 'true' if attributeFormDefault="qualified"
I'm still rather fuzzy on what 2 and 3 actually do, but it seems to affect how child elements (and attributes) inherit the parent namespace (whether it's implied or must be specified in the resulting XML).
field_order_type_info simply specifies the order of the child elements.
Please feel free to correct or clarify...
There is the Force.com Apex Code Developers Guide - Understanding the Generated Code, but it is currently fairly sparse on details for WebServiceCallout.invoke(...).
There is also Apex Web Services and Callouts, again not any useful details.
Up-voting Ideas: Documentation for WebServiceCallout may help in the long run.
Salesforce have just done an opensource release of wsdl2apex on Github, so you can now check to code to see exactly what is occurring. Announcing the Open-Source WSDL2Apex Generator