ASP.NET MVC Localization using a Database ASP.NET MVC already offers support for localization through ressource files. However, if you need to read texts from a database things get more complicated. Especially if you still want to use DataAnnotations in your model for display text and validation messages.
public class LoginModel { [Required(ErrorMessage = "User name is required!")] [Display(Name = "User name")] public string UserName { get; set; } [Required(ErrorMessage = "Passwort is required!")] [Display(Name = "Password")] public string Password { get; set; } }
@Html.LabelFor(m => m.UserName) @Html.TextBoxFor(m => m.UserName) @Html.ValidationMessageFor(m => m.UserName)
[Required(ErrorMessage = "27")] [Display(Name = "42")] public string UserName { get; set; } To replace the text-id of the Display attribute with the text from the database, we need to create our own MetadataProvider: Hide Copy Code public class MetadataProvider : AssociatedMetadataProvider { protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName) { var metadata = new ModelMetadata(this, containerType, modelAccessor, modelType, propertyName); if (propertyName != null) { var displayAttribute = attributes.OfType<DisplayAttribute>().FirstOrDefault(); if (displayAttribute != null) { int textId; if (Int32.TryParse(displayAttribute.Name, out textId)) { // TODO: get text from database metadata.DisplayName = "DB Text with id " textId; } } } return metadata; } } This class must then be registered in the Application_Start() method of Global.asax.cs: Hide Copy Code ModelMetadataProviders.Current = new MetadataProvider(); For the validation attributes it is a bit more complicated. The first class we need is a ValidatorProvider, which tells the MVC validation system which class to use to perform model validation. Our implementation in the class LocalizableModelValidatorProvider returns an instance of LocalizableModelValidator. Hide Copy Code public class LocalizableModelValidatorProvider : DataAnnotationsModelValidatorProvider { protected override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes) { var validators = base.GetValidators(metadata, context, attributes); return validators.Select(validator => new LocalizableModelValidator(validator, metadata, context)).ToList(); } } Our validation provider also needs to be registered in the Application_Start() method of Global.asax.cs: Hide Copy Code var provider = ModelValidatorProviders.Providers.FirstOrDefault(p => p.GetType() == typeof(DataAnnotationsModelValidatorProvider)); if (provider != null) { ModelValidatorProviders.Providers.Remove(provider); } ModelValidatorProviders.Providers.Add(new LocalizableModelValidatorProvider()); As you can see, we remove the existing validator provider that is of type DataAnnotationsModelValidatorProvider and replace it with our own implementation in the LocalizableModelValidatorProvider class. The second class we need is the actual ModelValidatorProvider, which we implement in the class LocalizableModelValidatorProvider: Hide Shrink Copy Code public class LocalizableModelValidator : ModelValidator { private readonly ModelValidator innerValidator; public LocalizableModelValidator(ModelValidator innerValidator, ModelMetadata metadata, ControllerContext controllerContext) : base(metadata, controllerContext) { this.innerValidator = innerValidator; } public override IEnumerable<ModelClientValidationRule> GetClientValidationRules() { var rules = innerValidator.GetClientValidationRules(); var modelClientValidationRules = rules as ModelClientValidationRule[] ?? rules.ToArray(); foreach (var rule in modelClientValidationRules) { int textId; if (Int32.TryParse(rule.ErrorMessage, out textId)) { // TODO: read text from database rule.ErrorMessage = "DB_Text_" textId; } } return modelClientValidationRules; } public override IEnumerable<ModelValidationResult> Validate(object container) { // execute the inner validation which doesn't have localization var results = innerValidator.Validate(container); // convert the error message (text id) to the localized value return results.Select(result => { int textId; if (Int32.TryParse(result.Message, out textId)) { // TODO: read text from database result.Message = "DB text with id " textId; } return new ModelValidationResult() { Message = result.Message }; }); } }
Comments