AJ's blog

January 25, 2014

ASP.NET MVC I18n – Part 8: Data Validation

Filed under: .NET, .NET Framework, ASP.NET MVC, HTML5, Internationalization — ajdotnet @ 3:13 pm

The last post showed how to display localized validation error messages. Time to be able to actually validate 😉

Note: This post is part of a series

This is another topic that is widely documented for the most part, but has some notable gaps.

Remember the error from last post:

Having the error message in localized German does not solve the fact that the date is actually in the valid local format in the first place. We should not even get an error.

The basic problem is that jQuery validation uses regular JavaScript methods to do validation, thus some valid data, if in localized format, will produce validation errors. But even worse is the fact that some data validates just fine – but with a wrong value. E.g. point and comma in numeric values switch their meaning from en-US to de-DE, similarly day and month change their position between en-US and fr-FR.

The generally cited answer to solve this issue is to use Globalize from github… which requires a little handiwork.

 

First…

… you need to grab the globalize.js file from github under lib\ as well as the necessary localizations under lib\cultures\. If you require compressed versions (globalize.min.js below), you’ll have to create them yourself, e.g using http://jscompress.com/, as they are not provided. All files are then placed in the /Scripts folder of your web project.

Note: there is also a nuget package, which will install these files – including all localizations, but still no minified version.

 

Second…

… the scripts need to be included in our views. There are two options, which can both be found:

A) Define a respective bundle…

bundles.Add(new ScriptBundle("~/bundles/globalization")
    .Include("~/Scripts/globalize.js")
    .Include("~/Scripts/globalize.culture.*")
    .Include("~/Scripts/globalize.initialize.js")
);

and render it in _layout.cshtml – before the „script“ section is rendered, because that is where validation will come into play:

@Scripts.Render("~/bundles/globalization")
@RenderSection("scripts", required: false)

B) Instead of including all localization files (i.e. the globalize.culture.*.js files) you could also just include the necessary one. In that case it’s probably simpler to go without bundle:

@Scripts.Render("~/Scripts/globalize.js")
@Scripts.Render("~/Scripts/globalize.culture." + Culture + ".js")
@Scripts.Render("~/Scripts/globalize.initialize.js")

 

Third…

… we need to initialize the culture on the client side.

For this a call to Globalize.culture is necessary. You could generate the respective call an the server, passing in the current UI culture – which is what you’ll see very often in tutorials. However the information is already available on the client (because we put it there), thus a static script globalize.initialize.js will do (and will benefit http caching):

$(document).ready(function () {
    // use the language provided from server…
    var lang = $("html").attr("lang"); 
    if (typeof Globalize != ‘undefined’)
        Globalize.culture(lang);
});

 

Fourth…

…we need some plumbing.

We have the necessary infrastructure for localization, but jQuery validation does not use it yet. The final step is to wire jQuere validation with the new globalize methods. This is something that everyone has to do. Something that is quite simple. Something that could have been provided. Something that someone could have told you how to do. Guess what? Nobody did. [UPDATE: Turns out, I was wrong, see below. I still kept the following content for the explanations.]

jQuery has an object that provides the necessary validation methods: $.validator.methods. All that is necessary is to reroute them to replacements that make use of the globalization library. For MVC and unobtrusive validation, that requires just 5 methods: number, date, min, max, and range. In yet another additional script file globalize.validation.js:

// replace methods in jquery.validate.js ($.validator.methods) as necessary for localized validation:

$.validator.methods.number = function (value, element) {
    return this.optional(element) || !isNaN(Globalize.parseFloat(value));
}

$.validator.methods.date = function (value, element) {
    if (this.optional(element))
        return true;
    var result = Globalize.parseDate(value);
    return !isNaN(result) && (result != null);
}

$.validator.methods.min = function( value, element, param ) {
    return this.optional(element) || Globalize.parseFloat(value) >= param;
}

$.validator.methods.max = function( value, element, param ) {
    return this.optional(element) || Globalize.parseFloat(value) <= param;
}

$.validator.methods.range = function (value, element, param) {
    if (this.optional(element))
        return true;
    var result = Globalize.parseFloat(value);
    return (result >= param[0] && result <= param[1]);
}

Note: If you are using jQuery validation directly (not via unobtrusive validation), you’ll need to wire all methods in $.validator.methods respectively.

What‘s left is including this script in a bundle. Since there is already a bundle associated with validation and included in respective edit views, I just extended it:

var bundle = bundles.GetBundleFor("~/bundles/jqueryval");
bundle.Include("~/Scripts/globalize.validation.js");

 

And now our validation code works just fine, properly recognizing German date and number formats.

UPDATE: Just when I finished writing this post, I stumbled upon John‘s post, which is the first one I found that connects Globalize and jQuery validation both correctly and completely, and even provides a nuget package.

 

That’s all for now folks,
AJ.NET

Advertisements

January 19, 2014

ASP.NET MVC I18n – Part 7: Model Attributes

Filed under: .NET, .NET Framework, ASP.NET MVC, HTML5, Internationalization — ajdotnet @ 5:19 pm

The last post introduced resources and used them directly. When it comes to localizing views aspects based on data – i.e. databinding against models – resources are also the means to localize the data aspects.

Note: This post is part of a series

Localizing the displayed data is something that is fairly broadly documented, therefore I can keep the basics to the minimum and focus on the stuff that is rarely mentioned. If you need more background, the ASP.NET site provides the perfect starting point.

This post will concentrate on localizing the data related labels and validation messages; localizing the actual validation is going to be addressed in the next post.

 

The M-part in MVC is the model, in ASP.NET MVC usually consisting of POCOs. Data validation as well as localizing labels in MVC is based on attributes put on the data model. The very first question – and usually one that is not addressed – is one more related to architecture than to plain code: Which class shall I attribute?

Remember the contract data model that I introduced in the initial post:

namespace MyStocks.BusinessContract
{
    [DebuggerDisplay("BusinessContract.Stock: {ID} {Isin} {Name} {Price} {Date}")]
    public class Stock
    {
        public int ID { get; set; }
        /// <summary>
        /// http://en.wikipedia.org/wiki/International_Securities_Identification_Number
        /// 12-character alpha-numerical code
        /// </summary>
        public string Isin { get; set; }
        public string Name { get; set; }
        public decimal Price { get; set; }
        public DateTime Date { get; set; }
    }
}

Technically, we could just put attributes on this contract data model. However, that would result in UI-specific information bleeding into the business contract. (I elaborated on this topic in an earlier post.) So, to make it short, do NOT put view related attributes on those classes. Instead provide model classes intended for the presentation layer. For our little example the contract class is just copied to the model folder of our MVC project, but in my experience models in ASP.NET MVC have a tendency to become view related models (to avoid the term viewmodel) with the actual data just being one aspect. Now we can apply the necessary validation attributes:

[DebuggerDisplay("Models.Stock: {ID} {Isin} {Name} {Price} {Date}")]
public class Stock
{
    public int ID { get; set; }
    
    [Required]
    [RegularExpression(@"[a-zA-Z]{2}[\w]{9}[\d]{1}")]
    public string Isin { get; set; }
    
    [Required]
    public string Name { get; set; }
    
    [Required]
    [DisplayFormat(DataFormatString = "{0:f2}")] // currency would include currency sign
    public decimal Price { get; set; }
    
    [Required]
    [DataType(DataType.Date)]
    public DateTime Date { get; set; }
}

This of course requires mapping the data back and forth. For more complex applications you might consider something like AutoMapper for this task, but in simple cases plain code will do:

using ContractStock = MyStocks.BusinessContract.Stock; // business contract model
using ModelStock = MyStocks.Mvc.Models.Stock; // mvc model

namespace MyStocks.Mvc.Models
{
    static class Mapper
    {
        public static ModelStock MapToUI(this ContractStock stock)
        {
            return new ModelStock
            {
                Date = stock.Date,
                ID = stock.ID,
                Isin = stock.Isin,
                Name = stock.Name,
                Price = stock.Price
            };
        }
        
        public static ContractStock MapToContract(this ModelStock stock)
        {
            return new ContractStock
            {
                Date = stock.Date,
                ID = stock.ID,
                Isin = stock.Isin,
                Name = stock.Name,
                Price = stock.Price
            };
        }
    }
}

And call these methods as appropriate in the controller:

public class StockController : Controller
{
    protected MyStocks.BusinessContract.IStockService StockService { get; set; }
    
    public StockController()
    {
        this.StockService = new MyStocks.BusinessService.StockService();
    }
    
    //
    // GET: /Stock/
    public ActionResult Index()
    {
        var model = StockService.GetAllStocks().Select(Mapper.MapToUI);
        return View(model);
    }

(Of course, in a real-word application this is the place to employ DI, e.g. Unity.)

The (mostly generated) view picks the information up using extension methods:

@model IEnumerable<MyStocks.Mvc.Models.Stock>
@{
   
 ViewBag.Title = Labels.Navigation_Stock_Index;
}
<h2>@ViewBag.Title</h2>
<table>
    <tr>
        <th>@Html.DisplayNameFor(model => model.Isin)</th>
        <th>@Html.DisplayNameFor(model => model.Name)</th>
        <th>@Html.DisplayNameFor(model => model.Price)</th>
        <th>@Html.DisplayNameFor(model => model.Date)</th>
    </tr>
    @foreach (var item in Model)
    {
        <tr>
            <td>@Html.ActionLink(item.Isin, "Details", new { id = item.ID })</td>
            <td>@Html.DisplayFor(modelItem => item.Name)</td>
            <td style="text-align:right">@Html.DisplayFor(modelItem => item.Price) </td>
            <td>@Html.DisplayFor(modelItem => item.Date)</td>
        </tr>
    }
</table>
<br />
<br />
@Html.ActionLink(Labels.Navigation_Back, "Index", "Home")

If you run the application, the usual outcome is something like this:

As you can see, property names are used for field labels. Jumping to details and edit view, we will also get validation messages based on our attributes from above:

You might already see the respective validation messages properly translated. This depends on the language version of the installed .NET Framework. Generally I would be careful to rely on the fact that all necessary languages are installed in production. Latest when custom feedback is required you need to provide the message in any case.

Providing localized validation messages, as well as display names for labels, can be done providing the „link“ to the respective resources, using attributes in the MVC model. DisplayAttribute for the display name, and the validation attributes for respective error messages:

[DebuggerDisplay("Models.Stock: {ID} {Isin} {Name} {Price} {Date}")]
public class Stock
{
    public int ID { get; set; }
    
    [Required(ErrorMessageResourceType = typeof(Localizations.Models), ErrorMessageResourceName = "Validation_Required")]
    [RegularExpression(@"[a-zA-Z]{2}[\w]{9}[\d]{1}", ErrorMessageResourceType = typeof(Localizations.Models), ErrorMessageResourceName = "Stock_Isin_RegEx")]
    [Display(ResourceType = typeof(Localizations.Models), Name = "Stock_Isin")]
    public string Isin { get; set; }
    
    [Required(ErrorMessageResourceType = typeof(Localizations.Models), ErrorMessageResourceName = "Validation_Required")]
    [Display(ResourceType = typeof(Localizations.Models), Name = "Stock_Name")]
    public string Name { get; set; }
    
    [Required(ErrorMessageResourceType = typeof(Localizations.Models), ErrorMessageResourceName = "Validation_Required")]
    [Display(ResourceType = typeof(Localizations.Models), Name = "Stock_Price")]
    [DisplayFormat(DataFormatString = "{0:f2}")] // currency would include currency sign
    public decimal Price { get; set; }
    
    [Required(ErrorMessageResourceType = typeof(Localizations.Models), ErrorMessageResourceName = "Validation_Required")]
    [Display(ResourceType = typeof(Localizations.Models), Name = "Stock_Date")]
    [DataType(DataType.Date)]
    public DateTime Date { get; set; }
}

That much can be found in any tutorial. What is also rarely mentioned though (much less documented), is that these localized messages can use string.Format placeholders. Let’s state that clearly: You do not have to provide „field XY is required“ messages for each and every field! (as some posts imply…).

All validation attributes support at least a placeholder for the field name, most also their additional parameters, such as the length in MinLengthAttribute. In other words, those resources would be sufficient for all respective fields:

Validation_Required= „{0} is a required field!“
Validation_MaxLength= „{0} must be shorter or equal to {1} characters!“

BTW: In case you do not like to clutter your source with all these addition parameters in your attributes, Phil uses the MVC infrastructure to resolve and inject localized messages using a convention based approach.

 

Once this is done, we get localized validation messages – with one final hole to patch: Look at all those nicely localized labels and messages… and the one culprit refusing to cooperate, the error message on the date (“Datum”) field:

The reason this does not work is that this message does not come from validation, but from binding. Binding is what turns the text into the appropriate data type (which implies „syntactic correctness“), while semantic validation checks the resulting value against some rules. See here for background.

The immediate consequence is that localization is done differently. Actually there are only two possible cases, numbers and dates, and there is only one way to supply them:

  • Define a resource file in App_GlobalResources – yes, I said, don‘t use it. This is the exception that proves the rule!
  • Define two messages with exactly these names:

  • Tell the model binder to use that particular resource file:

public static void InitializeLocalization()
{
    ClientDataTypeModelValidatorProvider.ResourceClassKey = "GlobalResources";
}

You might have seen DefaultModelBinder.ResourceClassKey for a similar purpose. Apparently this property is not used any more…

And now it works:

Wonder why it works in that twisted way?

“Aside from the standard localization of MVC, this message isn’t easily changeable (it was added at the last minute, and we didn’t offer an override unfortunately).” see here, one of Brad Wils‘ comments.

LOL!

 

Just for completeness: All this is based on the fact that MVC uses jQuery unobtrusive validation. Should you be using jQuery validation directly, you have to adjust $.validator.messages. See here for a complete example.

That’s all for now folks,
AJ.NET

January 13, 2014

ASP.NET MVC I18n – Part 6: Using .NET Resources

Filed under: .NET, .NET Framework, ASP.NET MVC, HTML5, Internationalization — ajdotnet @ 6:47 pm

The last part addressed larger chunks of HTML content. In that case, imperative coding works nicely. For labels, menu links and similar texts however, the standard localization mechanism in .NET is resources, based on manifest resources, and respective satellite assemblies.

Note: This post is part of a series

Access is handled via the ResourceManager class, additionally Visual Studio will generate an accessor class for each .resx file, that offers members for each key. This eliminates the possibility of typos and provides more convenience through intellisense. For MVC applications it is necessary to change the access modifier for that class to public, as shown here:

The reason is that ASP.NET works with code generation for views (WebForms as well as Razor), thus the generated code lives in another temporary assembly, not the one associated with the web project, and won’t have access to an internal class.

Please note that this applies to the generated accessor class, not the resource itself. One can always use ResourceManager and access a resource in any other assembly.

And to get another point out of the way: With ASP.NET WebForms you might have used the special folders App_GlobalResources and App_LocalResources to place your .resx files in. Don‘t do that with ASP.NET MVC!

The problem is that resources files in these folders are treated differently by Visual Studio and the ASP.NET runtime. There are workarounds to make it work, but why rely on quirks if another approach works without.

If you are interested in the background, Scott has further information.

 

I chose to place my resources in my own special folder. Since the obvious name for that folder (“Resources”) is already taken (by the default resource file generated from the project settings as well as as namespace by App_GlobalRessources), I named it Localizations:

The resource file Labels.resx and its localized version Label.de-DE.resx contains all navigation labels used in menus and other links.

Adding this folder to the web.configs (the one in the Views folder)…

…makes the view code even more convenient:

<ol class="round">
    <li class="one">
        @Html.ActionLink(Labels.Navigation_Stock_Index + "…", "Index", "Stock")
    </li>

Proper localized information in respective .resx files available, and we have the next localization step solved:

 

Final hint: I can live with that approach, i.e.maintain resources in arbitrary files, that I organize as I like. Should you want to “attach” resources to you views and have them used automatically, you might want to have a look at Matt’s post. It should also be possible to implement a simple fallback scheme that allows keeping general labels automatically in one centralized resource file, while maintaining specific labels in view specific resource files.

That’s all for now folks,
AJ.NET

Create a free website or blog at WordPress.com.