Довольно давно я уже писал о локализации MVC3-сайтов, уделив достаточно большое внимание способу перевода валидационных сообщений на другие языки.
Однако недавно в проекте я столкнулся с одной ошибкой валидации, перевести которую оказалось не так просто. Вот пример этой ошибки:
Как нетрудно заметить, я просто ввел в числовое поле очень большое значение. При этом сам факт возникновения ошибки меня вполне устраивал (большие значение обрабатывать и не планировалось), а вот текст на «иностранном» тестеров слегка смутил :)
Stackoverflow по этому поводу ничего не ответил, поэтому пришлось искать решение своими силами.
Более простых способов, чем перегрузить model-binder для числовых значений мне в голову с ходу не пришло. Таким образом решение выродилось в модел-байндер и регистрацию его в global.asax:
//регистрация где-нибудь в районе Application_Start: System.Web.Mvc.ModelBinders.Binders.Add(typeof(int), new NumericModelBinder()); System.Web.Mvc.ModelBinders.Binders.Add(typeof(int?), new NumericModelBinder()); System.Web.Mvc.ModelBinders.Binders.Add(typeof(byte), new NumericModelBinder()); System.Web.Mvc.ModelBinders.Binders.Add(typeof(byte?), new NumericModelBinder()); //... //Собственно байндер: /// <summary> /// выдает локализованные ошибки о переполнении (слишком большое число, введенное в поле int) /// </summary> public class NumericModelBinder : DefaultModelBinder { public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); double parsedValue; if (double.TryParse(value.AttemptedValue, out parsedValue)) { double minValue = 0; double maxValue = 0; var modelType = Nullable.GetUnderlyingType(bindingContext.ModelType) ?? bindingContext.ModelType; if (modelType == typeof(byte)) { minValue = byte.MinValue; maxValue = byte.MaxValue; } if (modelType == typeof(int)) { minValue = int.MinValue; maxValue = int.MaxValue; } if (modelType == typeof(short)) { minValue = short.MinValue; maxValue = short.MaxValue; } if (modelType == typeof(long)) { minValue = long.MinValue; maxValue = long.MaxValue; } if (modelType == typeof(Int64)) { minValue = Int64.MinValue; maxValue = Int64.MaxValue; } if ((minValue != 0 || maxValue != 0) && (parsedValue < minValue || parsedValue > maxValue)) { var error = GetUserResourceString(controllerContext, "PropertyValueInvalid") ?? "The value '{0}' is invalid."; bindingContext.ModelState.AddModelError(bindingContext.ModelName, string.Format(error, value.AttemptedValue, bindingContext.ModelMetadata.DisplayName)); } } return base.BindModel(controllerContext, bindingContext); } private static string GetUserResourceString(ControllerContext controllerContext, string resourceName) { string result = null; if (!String.IsNullOrEmpty(ResourceClassKey) && (controllerContext != null) && (controllerContext.HttpContext != null)) { result = controllerContext.HttpContext.GetGlobalResourceObject(ResourceClassKey, resourceName, CultureInfo.CurrentUICulture) as string; } return result; } }
Локализованные строки берутся, по аналогии с предыдущим постом, из соответствующего ресурса. В простых случаях можно, конечно, и просто «захардкодить» текст прямо внутри байндера, избавившись от функции `GetUserResourceString`.
Будет любопытно узнать, если кому-то еще это пригодиться :)
P.S. Чтобы добавить к этой ошибке еще и клиентскую валидацию, придется подправлять дефолтные адаптеры для jquery.validation. Мне это в данном случае показалось излишним, и для простоты последующих обновлений я решил не модифицировать jquery.validation а ограничиться в данном случае серверной валидацией.