Skip to content

Localizing Data Annotations

shiftkey edited this page Feb 29, 2012 · 1 revision

This library was started as a proof-of-concept by @haacked. You can read up on the back story here, or follow through the guide below to understand how it works.

About

Ever had the feeling when looking at a model class that the use of strings inside the DataAnnotations attributes was a code smell?

Take for example the ChangePasswordModel class inside a new MVC project:

public class ChangePasswordModel
{
    [Required]
    [DataType(DataType.Password)]
    [Display(Name = "Current password")]
    public string OldPassword { get; set; }

    [Required]
    [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
    [DataType(DataType.Password)]
    [Display(Name = "New password")]
    public string NewPassword { get; set; }

    [DataType(DataType.Password)]
    [Display(Name = "Confirm new password")]
    [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
    public string ConfirmPassword { get; set; }
}

What if you could remove the use of strings for labels and error messages?

public class ChangePasswordModel
{
    [Required]
    [DataType(DataType.Password)]
    public string OldPassword { get; set; }

    [Required]
    [StringLength(100, MinimumLength = 6)]
    [DataType(DataType.Password)]
    public string NewPassword { get; set; }

    [DataType(DataType.Password)]
    [Compare("NewPassword")]
    public string ConfirmPassword { get; set; }
}

Where did the strings go? To the resources!

At runtime, the ModelMetadataProvider defined for the app will look for a suitable resource key, matching the format outlined below:

  • First, the class name
  • Then the property
  • Finally, the attribute to render

For example, these are two resource keys for the NewPassword field:

  • ChangePasswordModel_NewPassword - replaces the [Display(Name="New Password")] attribute
  • ChangePasswordModel_NewPassword_StringLength - replaces the error resource key for the [StringLength] attribute

What resource is used? This depends on the current culture of the user. Refer to this topic for some ways you can set the culture for the current user.

Installing into your application

So how do you use this in your app?

  1. Install the package:

Install-Package ModelMetadataExtensionsWithBugfix

  1. Set the ModelMetadataProviders.Current value to the new provider. There are two constructor parameters which you can set here:
  • requireConventionAttribute - I think this is where you can mandate that classes use a specific attribute to opt-in to the conventions (i.e. ATTRIBUTE ALL THE THINGS). If you have existing error messages you don't want to migrate, you may need to set this to true.

  • defaultResourceType - this should be the resource file which contains the keys specific to your models. Your milage may vary, but the sample application here has only one resource file (and a number of translations of that file).

    protected void Application_Start() { AreaRegistration.RegisterAllAreas();

     ModelMetadataProviders.Current = new ConventionalModelMetadataProvider(false, typeof(Resources.Language));
    
     RegisterGlobalFilters(GlobalFilters.Filters);
     RegisterRoutes(RouteTable.Routes);
    

    }

After doing all that, populate the resource file with the necessary keys and text. Don't forget to include the translations too if you want to see something other than fallback behaviour!

Clone this wiki locally