Using DataAnnotations and Localization in ASP.NET Core MVC

This article shows how ASP.NET Core localization can be used together with data annotations. The data annotations are used to decorate the data model, and when HTTP POST/PUT (also PATCH) Requests are sent with model errors, the error message is returned localized in the request culture.

Code: https://github.com/damienbod/AspNetCoreLocalization

Posts in this series

2018-12-05: Updated to ASP.NET Core 2.2
2017-08-19: Updated to ASP.NET Core 2.0
2017.02.10: Updated to VS2017 msbuild
2016.05.26: Bug fix Data Annotations Localization
2016.05.16: Updated to ASP.NET Core 1.0 RC2 dotnet
2015.11.20: Updated to ASP.NET Core 1.0 rc1

Localization Setup

In the Startup class, the AddDataAnnotationsLocalization is added in the ConfigureServices method.

services.AddMvc()
   .AddViewLocalization()
   .AddDataAnnotationsLocalization();

Now a model class can be created and the data annotations can be used. The Length property in this example has a range attribute, with an ErrorMessageResourceName and an ErrorMessageResourceType property set. The ErrorMessageResourceType is used to define the resource itself using the type and the ErrorMessageResourceName is used to define the resource identifier. Only the Length property has localized error messages implemented.

using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
using System.Globalization;
using AspNet5Localization.Controllers;
using Microsoft.Extensions.Localization;
	
namespace AspNet5Localization.Model
{
    public class Box
    {
        public long Id { get; set; }

        public double Height { get; set; }

        public double Width { get; set; }

        [Required(ErrorMessage = "BoxLengthRequired")]
        [Range(1.0, 100.0, ErrorMessage = "BoxLengthRange")]
        public double Length { get; set; }
    }
}

Now a MVC 6 controller can be created which uses the model class. This controller implements POST and PUT action methods, which uses the ModelState to validate the request. If the model is invalid, the error message is returned with a localized value.

using AspNet5Localization.Model;
using Microsoft.AspNetCore.Mvc;

namespace AspNet5Localization.Controllers
{
    [Route("api/[controller]")]
    public class BoxesController : Controller
    {
        [HttpGet("{id}")]
        public IActionResult Get(int id)
        {
            if (id == 0)
            {
                return NotFound(id);
            }

            return Ok(new Box() { Id = id, Height = 10, Length = 10, Width=10 });
        }

        /// <summary>
        /// http://localhost:5000/api/boxes?culture=it-CH
        /// Content-Type: application/json
        /// 
        /// { "Id":7,"Height":10,"Width":10,"Length":1000}
        /// </summary>
        /// <param name="box"></param>
        /// <returns></returns>
        [HttpPost]
        public IActionResult Post([FromBody]Box box)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }
            else
            {           
                string url = Url.RouteUrl("api/boxes", new { id = 11111 },
                    Request.Scheme, Request.Host.ToUriComponent());

                return Created(url, box);
            }
        }

        [HttpPut("{id}")]
        public IActionResult Put(int id, [FromBody]Box box)
        {
            if(id == 0)
            {
                return NotFound(box);
            }

            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }
            else
            {
                return Ok(box);
            }
        }

        [HttpDelete("{id}")]
        public IActionResult Delete(int id)
        {
            if (id == 0)
            {
                return NotFound(id);
            }

            return new NoContentResult();
        }
    }
}

Now the POST method can be called in Fiddler or Postman. Underneath is an example of a HTTP POST Request using the it-CH culture. The length property is outside the range and will return an model state error.

http://localhost:5000/api/boxes?culture=it-CH

User-Agent: Fiddler
Host: localhost:5000
Content-Length: 46
Content-Type: application/json

{ "Id":7,"Height":10,"Width":10,"Length":1000}

HTTP Response with a it-CH localized error message:

HTTP/1.1 400 Bad Request
Date: Sat, 24 Oct 2015 17:15:28 GMT
Content-Type: application/json; charset=utf-8
Server: Kestrel
Transfer-Encoding: chunked

{"Length":["The box length should be between 1 and a 100 it-CH"]}

Localization can be used in data annotations like previous versions of MVC or Web API and provides a simple way of validating your data inputs.

Links:

Microsoft Docs: Globalization and localization

https://github.com/aspnet/Localization

https://github.com/aspnet/Localization/issues/286#issuecomment-249708911

https://github.com/aspnet/Tooling/issues/236

http://www.jerriepelser.com/blog/how-aspnet5-determines-culture-info-for-localization

http://www.vodovnik.com/2015/09/20/localization-in-asp-net-5-mvc-6/

https://github.com/WeebDo/WeebDo.CMF

https://github.com/joeaudette/cloudscribe

https://github.com/aspnet/Mvc/tree/dev/test/WebSites/LocalizationWebSite

Example of localization middleware for culture in route

http://weblogs.asp.net/jeff/beating-localization-into-submission-on-asp-net-5

13 comments

  1. […] Using DataAnnotations and Localization in ASP.NET 5 MVC 6 – damienbod continues his look at localisation in ASP.NET 5 / MVC 6 with a look at how DataAnnotations and Localization can be handled together […]

  2. rakrouki · · Reply

    Hi Damien
    thanks for your great articles 🙂 ; i have a quick question how you manage the localisation in the layout and partial pages ; by injecting IViewLocalizer , i canno’t use the layout resource files; always the views resources will be loaded
    Kind Regards

    1. Hi rakrouki

      Thanks for your comment. This doesn’t seem to work either, here’s one issue on gitHub (see the last comment)

      https://github.com/aspnet/Localization/issues/150

      There is also another issue about this in the MVC repo. Localization in aspnet5 is still in early development yet (even though its rc1…), its not in a usable state yet.

      Looks like you’ll have to implement it yourself by implementing the IStringLocalizerFactory and/or IStringLocalizer.

      https://github.com/aspnet/Mvc/issues/2767

      https://github.com/aspnet/Mvc/issues/3659

      Greetings Damien

  3. I tried using data annotation and localization as shown in Web Application not API application and it does not work. Specifically this example and approach does not work.
    [Required(ErrorMessageResourceName = “BoxLengthRequired”, ErrorMessageResourceType = typeof(AmazingResource))]
    [Range(1.0, 100.0, ErrorMessageResourceName = “BoxLengthRange”, ErrorMessageResourceType = typeof(AmazingResource))]
    public double Length { get; set; }

    I would appreciate an example also with Display attribute.

  4. Ahmed · · Reply

    “The runtime doesn’t look up localized strings for non-validation attributes”
    so how we should localize the display attribute
    Thanks a lot
    and sorry for duplicate the old replay was on the wrong post

  5. How can i do this using sql ?

  6. Helge Tveranger · · Reply

    I just took the latest version https://github.com/damienbod/AspNet5Localization, but the post mehtod on BoxesController that where supposd to return the localized string just returned “BoxLengthRange”. Sorry to tell but you sln dont work

    1. Hi Helge , This should work, I’ll check to make certain. I’ll get back to you.

      Greetings Damien

    2. Hi Helge. I see the problem. I have not added this localization to the database, only to the resource file. If you switch to use resource resx files, it will work again, or add the record to the database.

      services.AddSqlLocalization(options => options.UseTypeFullNames = true);
      //services.AddLocalization(options => options.ResourcesPath = “Resources”);

      I’ll add this to the database later.

      Greetings and thanks for the info, Damien

  7. Yan de Lima Justino · · Reply

    Grate job, man! This article save my project here in Brazil!

    1. thanks

      Greetings Damien

  8. Always nice to return to this site for localization info. Very handy, merci!

Leave a reply to David Keller (@kdaveid) Cancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.