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

Jay*_*j.K 21 c# globalization validation asp.net-mvc asp.net-mvc-4

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

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

该字段必须为数字.

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

[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; }
Run Code Online (Sandbox Code Playgroud)

我已经在我的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
    }
}
Run Code Online (Sandbox Code Playgroud)

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

编辑

参考Mike的网址,我在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"));
}
Run Code Online (Sandbox Code Playgroud)

在布局页面中我实现如下

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))
Run Code Online (Sandbox Code Playgroud)

Lef*_*tyX 24

有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"
));
Run Code Online (Sandbox Code Playgroud)

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

Globalize.culture('en-AU');
Run Code Online (Sandbox Code Playgroud)

要么

Globalize.culture('es-AR');
Run Code Online (Sandbox Code Playgroud)

它可以使用某种接近来找出哪个是你想要使用的最接近的文化.如果您已加载了捆绑包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

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

这些都是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);
    });
};
Run Code Online (Sandbox Code Playgroud)

无论如何; 让我们说你想坚持旧的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"
));
Run Code Online (Sandbox Code Playgroud)

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

  • 另外,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;
    }
Run Code Online (Sandbox Code Playgroud)

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

    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]);
        }
    });
Run Code Online (Sandbox Code Playgroud)

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

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

可以在Global.asax中设置Application_Start:

ModelBinders.Binders.Add(typeof(decimal), new DecimalModelBinder());
ModelBinders.Binders.Add(typeof(decimal?), new DecimalModelBinder());
Run Code Online (Sandbox Code Playgroud)

这几乎是你需要的一切.

这种方法只有一个恼人的问题.
假设您正在使用文化,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));
};
Run Code Online (Sandbox Code Playgroud)

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

如果你Globalize.parseFloat('10.4')为文化es-AR 做同样的事情也会发生同样的事情; 它将再次成为104.

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

这两个,.有效符号,它们将被用作小数点分隔符和千位分隔符.

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

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

decimal.Parse('10,4', NumberStyles.Any, CultureInfo.CurrentCulture);
Run Code Online (Sandbox Code Playgroud)

其中CultureInfo.CurrentCulture为'en-AU'的地方同样会产生相同的结果:104.

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

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

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