处理不同文化validation的最佳方法是什么?

我正在尝试构建一个多语言MVC应用程序。 我的应用程序中有一个表单,我有字段输入成本。 我能够使用西class牙文化创建一个记录。

但是在尝试更新记录时,我得到了jqueryvalidation错误。 我收到一条默认错误消息:

该字段必须是数字。

在我的视图模型中,我设置了以下属性。

[LocalizedDisplayName("Label_Cost")] [RegularExpression("^[^,]+$", ErrorMessage = null, ErrorMessageResourceName = "Error_Message_Html_Tags_Prevented", ErrorMessageResourceType = typeof(Resources))] [Range(0, 9999.99, ErrorMessage = null, ErrorMessageResourceName = "Error_Message_Cost_Not_Valid", ErrorMessageResourceType = typeof(Resources))] public decimal? Cost { get; set; } 

我已经在我的Gobal.asax文件中设置了以下内容

 protected void Application_AcquireRequestState(object sender, EventArgs e) { try { HttpCookie cookie = HttpContext.Current.Request.Cookies.Get("CurrentCulture"); string culutureCode = cookie != null && !string.IsNullOrEmpty(cookie.Value) ? cookie.Value : "en"; CultureInfo ci = new CultureInfo(culutureCode); System.Threading.Thread.CurrentThread.CurrentUICulture = ci; System.Threading.Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(ci.Name); } catch(Exception ex) { // Code } } 

并且上述方法在服务器端按预期工作以改变文化。 但是客户端validation打破了非英语文化,因为javascript只识别十进制文字。 我想知道使用特定于文化的validation来扩展mvc客户端validation的最佳方法。

编辑

参考Mike的url,我在Js包中进行了以下更改。 Js包如下

 public static void RegisterBundles(BundleCollection bundles) { BundleTable.EnableOptimizations = true; bundles.Add(new ScriptBundle("~/bundles/jquery").Include( "~/Scripts/jquery-{version}.js")); bundles.Add(new ScriptBundle("~/bundles/globalisation").Include( "~/Scripts/globalize.js", "~/Scripts/globalize/currency.js", "~/Scripts/globalize/date.js", "~/Scripts/globalize/message.js", "~/Scripts/globalize/number.js", "~/Scripts/globalize/plural.js", "~/Scripts/globalize/relative-time.js")); bundles.Add(new ScriptBundle("~/bundles/globalisationEN").Include( "~/Scripts/GlobalisationCulture/globalize.culture.en-AU.js")); bundles.Add(new ScriptBundle("~/bundles/globalisationES").Include( "~/Scripts/GlobalisationCulture/globalize.culture.es-AR.js")); bundles.Add(new ScriptBundle("~/bundles/jqueryuiEN").Include( "~/Scripts/jquery-ui-1.10.3.js")); bundles.Add(new ScriptBundle("~/bundles/jqueryuiES").Include( "~/Scripts/jquery-ui-1.10.3.js")); bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include( "~/Scripts/jquery.validate.js", "~/Scripts/jquery.validate.unobtrusive.js", "~/Scripts/jquery.unobtrusive-ajax.js", "~/Scripts/jquery.validate.globalize.js")); } 

在布局页面中,我实现如下

 HttpCookie cookie = HttpContext.Current.Request.Cookies.Get("CurrentCulture"); string culutureCode = cookie != null && !string.IsNullOrEmpty(cookie.Value) ? cookie.Value : "en"; if (culutureCode.Equals("en-AU", StringComparison.OrdinalIgnoreCase)) { culutureCode = "EN"; } else if (culutureCode.Equals("es-AR", StringComparison.OrdinalIgnoreCase)) { culutureCode = "ES"; } else { culutureCode = "EN"; } @Scripts.Render("~/bundles/jquery", "~/bundles/globalisation", string.Format("~/bundles/globalisation{0}", culutureCode), "~/bundles/jqueryval", string.Format("~/bundles/jqueryui{0}", culutureCode)) 

有2个jQuery Globalize插件。

旧版本v0.0.1包含一个脚本globalize.js ,它有一个子文件夹cultures ,您可以在其中找到所有脚本文化,例如:

  • globalize.culture.en-AU.js
  • globalize.culture.es-AR.js

这些脚本允许您添加任意数量的文化,因此以这种方式构建捆绑包是完全可以的:

 bundles.Add(new ScriptBundle("~/bundles/globalisation").Include( "~/Scripts/globalize.js", "~/Scripts/cultures/globalize.culture.en-AU.js", "~/Scripts/cultures/globalize.culture.es-AR.js" )); 

Globalize将有一组本地化脚本,您可以使用以下方法设置:

 Globalize.culture('en-AU'); 

要么

 Globalize.culture('es-AR'); 

它可以使用某种接近来找出你想要使用的最接近的文化。 如果你已经加载了你的bundle globalize.culture.es-AR.js你可以设置Globalize.culture('es');Globalize可以弄清楚你想要使用’es-AR’文化; 当然,如果你添加了globalize.culture.es.js ,加载器会选择最后一个。

新版本的jQuery Globalize(稳定版)是v1.0.0 ,它的工作方式完全不同。

它仍然有一个名为globalize.js的主脚本文件,但您必须添加更多脚本才能使其正常工作。

有人已经构建了一个工具 ,可以准确地告诉您需要什么脚本,具体取决于您要使用的模块类型(数量,日期,货币)。

如果您选择使用v1.0.0,您将看到该工具将建议包含基本脚本(仅限数字):

  • cldr.js
  • CLDR / event.js
  • CLDR / supplemental.js
  • globalize.js
  • 全球化/ number.js

加上一些CLDR JSON脚本:

  • CLDR /补充/ likelySubtags.json
  • CLDR /主/ {区域设置} /numbers.json
  • CLDR /补充/ numberingSystems.json

您可以在核心包和数字包中找到这些文件。
如果您想validation日期,这是包 。 更多信息在这里 。

这些都是json文件,你不能捆绑它们。 您可以在运行时加载它们,执行以下操作:

 Application.loadCulture = function (culture) { $.when( $.get(Application.CldrFetch + '/' + culture + '/' + encodeURIComponent("likelySubtags.json")), $.get(Application.CldrFetch + '/' + culture + '/' + "numberingSystems.json"), $.get(Application.CldrFetch + '/' + culture + '/' + "plurals.json"), $.get(Application.CldrFetch + '/' + culture + '/' + "ordinals.json"), $.get(Application.CldrFetch + '/' + culture + '/' + "currencyData.json"), $.get(Application.CldrFetch + '/' + culture + '/' + "timeData.json"), $.get(Application.CldrFetch + '/' + culture + '/' + "weekData.json"), $.get(Application.CldrFetch + '/' + culture + '/' + "ca-gregorian.json"), $.get(Application.CldrFetch + '/' + culture + '/' + "timeZoneNames.json"), $.get(Application.CldrFetch + '/' + culture + '/' + "numbers.json"), $.get(Application.CldrFetch + '/' + culture + '/' + "currencies.json") ) .then(function () { // Normalize $.get results, we only need the JSON, not the request statuses. return [].slice.apply(arguments, [0]).map(function (result) { return result[0]; }); }).then(Globalize.load).then(function () { Globalize.locale(culture); }); }; 

无论如何; 让我们说你想坚持旧的v0.0.1,这仍然是最好的。
您的捆绑包将具有全球化脚本和文化脚本:

 bundles.Add(new ScriptBundle("~/bundles/globalisation").Include( "~/Scripts/globalize.js", "~/Scripts/cultures/globalize.culture.en-AU.js", "~/Scripts/cultures/globalize.culture.es-AR.js" )); 

jQueryvalidation提供了一些您可能需要考虑的其他扩展:

  • 另外,methods.js
  • 本地化/ messages_es_AR.js(文化的错误消息)

我已经看到你在Application_AcquireRequestState设置你的文化。 有人建议最好在Application_BeginRequest执行它,因为它在管道中先前处理过:

  protected void Application_BeginRequest(object sender, EventArgs e) { HttpCookie cookie = HttpContext.Current.Request.Cookies.Get("CurrentCulture"); string cultureCode = cookie != null && !string.IsNullOrEmpty(cookie.Value) ? cookie.Value : "en"; CultureInfo ci = new CultureInfo(cultureCode); System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(cultureCode); System.Threading.Thread.CurrentThread.CurrentUICulture = System.Threading.Thread.CurrentThread.CurrentCulture; } 

看来你正在使用这个jQuery插件进行validation。 我通常会做的是,只要我加载脚本,配置文化并设置自定义validation:

  Globalize.culture(this.culture); $.validator.methods.number = function (value, element) { return this.optional(element) || jQuery.isNumeric(Globalize.parseFloat(value)); }; $.validator.methods.date = function (value, element) { return (this.optional(element) || Globalize.parseDate(value)); }; jQuery.extend(jQuery.validator.methods, { range: function (value, element, param) { var val = Globalize.parseFloat(value); return this.optional(element) || (val >= param[0] && val <= param[1]); } }); 

你缺少的一件事是小数的模型绑定器:

 using System; using System.Web.Mvc; using System.Globalization; public class DecimalModelBinder : IModelBinder { public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { ValueProviderResult valueResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); ModelState modelState = new ModelState { Value = valueResult }; object actualValue = null; try { //Check if this is a nullable decimal and a null or empty string has been passed var isNullableAndNull = (bindingContext.ModelMetadata.IsNullableValueType && string.IsNullOrEmpty(valueResult.AttemptedValue)); //If not nullable and null then we should try and parse the decimal if (!isNullableAndNull) { actualValue = decimal.Parse(valueResult.AttemptedValue, NumberStyles.Any, CultureInfo.CurrentCulture); } } catch (FormatException e) { modelState.Errors.Add(e); } bindingContext.ModelState.Add(bindingContext.ModelName, modelState); return actualValue; } } 

可以在Global.asax Application_Start设置:

 ModelBinders.Binders.Add(typeof(decimal), new DecimalModelBinder()); ModelBinders.Binders.Add(typeof(decimal?), new DecimalModelBinder()); 

这几乎是你需要的一切。

这种方法只有一个恼人的问题。
假设你正在使用文化en-AU ,你在数字字段中输入一个值:10,4。 这个数字在es-AR完全有效,但它对于en-AU文化应该是无效的。

jQuery Globalize无论如何都会认为它有效,因为它会将它转换为104:

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

文化en-AU的Globalize.parseFloat('10,4')会将该数字转换为104。

如果您对文化es-AR的Globalize.parseFloat('10.4')执行相同操作,也会发生同样的事情; 它将再次成为104。

您可以检查运行此小提琴的此行为。

两者,. 是有效的符号,因为它们将用作小数分隔符和千位分隔符。

在github上有一些关于这个主题的问题,我想这很难解决,因为他们现在正在开发新版本,顺便说一下同样的问题仍然存在。

使用我们的十进制模型绑定器,您将在服务器端面临同样的问题:

 decimal.Parse('10,4', NumberStyles.Any, CultureInfo.CurrentCulture); 

CultureInfo.CurrentCulture是'en-AU'的地方再次产生相同的结果: 104

它可以在那里放置一个断点,看看它是如何转换价值的。

我想这可能更容易修复,也许使用一些正则表达式。

如果你想使用jQuery Validator v.0.1.1或jQuery Validator v.1.0.0来解决这个问题,我在这里和这里创建了两个存储库。

您已在RegisterBundles中添加了捆绑包,但未在布局页面中使用它们。 您还在RegisterBundles中添加了冗余的jqueryui文件。 像这样更新 RegisterBundles方法:

 public static void RegisterBundles(BundleCollection bundles) { BundleTable.EnableOptimizations = true; bundles.Add(new ScriptBundle("~/bundles/jquery").Include( "~/Scripts/jquery-{version}.js")); bundles.Add(new ScriptBundle("~/bundles/globalisation").Include( "~/Scripts/globalize.js", "~/Scripts/globalize/currency.js", "~/Scripts/globalize/date.js", "~/Scripts/globalize/message.js", "~/Scripts/globalize/number.js", "~/Scripts/globalize/plural.js", "~/Scripts/globalize/relative-time.js")); bundles.Add(new ScriptBundle("~/bundles/globalisationEN").Include( "~/Scripts/GlobalisationCulture/globalize.culture.en-AU.js")); bundles.Add(new ScriptBundle("~/bundles/globalisationES").Include( "~/Scripts/GlobalisationCulture/globalize.culture.es-AR.js")); bundles.Add(new ScriptBundle("~/bundles/jqueryui").Include( "~/Scripts/jquery-ui-1.10.3.js")); bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include( "~/Scripts/jquery.validate.js", "~/Scripts/jquery.validate.unobtrusive.js", "~/Scripts/jquery.unobtrusive-ajax.js", "~/Scripts/jquery.validate.globalize.js")); } 

然后像这样更新布局页面:

 @section Scripts { @Scripts.Render("~/bundles/jquery", "~/bundles/globalisation", "~/bundles/globalisationEN", "~/bundles/globalisationES", "~/bundles/jqueryval", "~/bundles/jqueryui"))  } 

希望这会有所帮助:)