Подтвердить что ты не робот

Каков наилучший способ обработки валидации с другой культурой

Я пытаюсь создать многоязычное приложение MVC. У меня есть форма в моем приложении, и у меня есть поле для ввода стоимости. Я могу создать запись, используя испанскую культуру.

Но при попытке обновить запись я получаю jquery validation false. и я получаю сообщение об ошибке по умолчанию:

Поле должно быть числовым.

В моей модели модели я установил следующие атрибуты.

[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
    }
}

и вышеуказанный метод работает так, как ожидалось, на стороне сервера при изменении культуры. Но проверка на стороне клиента ломается на неанглийских культурах, поскольку javascript распознает только десятичные литералы. Я хотел бы знать лучший способ продлить проверку на стороне клиента mvc с помощью проверки подлинности, связанной с культурой.

ИЗМЕНИТЬ

Со ссылкой на Mike url я сделал следующие изменения в Js bundle. 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))
4b9b3361

Ответ 1

Есть 2 плагина jQuery для глобализации.

Старая версия v0.0.1 содержит один script globalize.js и имеет подпапку cultures, где вы можете найти все культуры script, такие как:

  • 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');

Он может использовать какую-то близость, чтобы выяснить, какая ближайшая культура вы хотите использовать. Если вы загрузили свой пакет globalize.culture.es-AR.js, вы можете установить Globalize.culture('es'); и Globalize, чтобы понять, что вы хотите использовать культуру "es-AR"; конечно, если вы добавили globalize.culture.es.js, то загрузчик выбрал бы этот последний.

Новая версия jQuery Globalize (stable) - это v1.0.0, и она работает совершенно по-другому.

У него по-прежнему есть основной файл script с именем globalize.js, но вам нужно добавить намного больше скриптов, чтобы он работал.

Кто-то создал инструмент, который сообщает вам, что именно вам нужно script, в зависимости от того, какой тип модуля (число, даты, валюты ) вы хотите использовать.

Если вы предпочитаете использовать v1.0.0, вы увидите, что инструмент предложит включить базовые скрипты (только числа):

  • cldr.js
  • CLDR/event.js
  • CLDR/supplemental.js
  • globalize.js
  • Globalize/number.js

плюс некоторые скрипты CLDR JSON:

  • CLDR/дополнительные/likelySubtags.json
  • CLDR/Основной/{} локаль /numbers.json
  • CLDR/дополнительные/numberingSystems.json

Эти файлы можно найти в пакете core и numbers.
Если вы хотите проверить даты, это пакет. Подробнее здесь.

Это все 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, который по-прежнему остается лучшим.
Ваш пакет будет иметь глобализацию script и культурные:

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

jQuery validation предлагает другое дополнительное расширение, которое вы можете рассмотреть:

  • дополнительные-methods.js
  • localization/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 для проверки. То, что я обычно делаю, - как только я загружу script, настрою культуру и настрою пользовательскую проверку:

    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. Глобализация будет считать его действительным в любом случае, поскольку он будет преобразовывать его в 104 здесь:

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

Globalize.parseFloat('10,4') для культуры en-AU преобразует это число в 104.

То же самое произойдет, если вы сделаете то же самое для Globalize.parseFloat('10.4') для культуры es-AR; он снова станет 104.

Вы можете проверить это поведение, выполнив этот fiddle.

Оба , и . являются допустимыми символами, поскольку они будут использоваться в качестве разделителя десятичных чисел и разделителя тысяч.

Есть несколько вопросов, открытых по этой теме на 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, я создал два репозитория здесь и здесь.

Ответ 2

Вы добавили пакеты в RegisterBundles, но не использовали их на странице макета. Вы также добавили избыточный файл jqueryui в RegisterBundles. Обновить ваш метод 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"))

   <script type="text/javascript">
    $.validator.methods.number = function (value, element) {
        return this.optional(element) ||
            !isNaN(Globalize.parseFloat(value));
    }
    $(document).ready(function () {
        Globalize.culture('es-AR'); //set spanish culture
    });

   </script>
}

Надеюсь, это поможет:)