2015-09-15 11 views
21

Próbuję utworzyć wielojęzyczną aplikację MVC. Mam formularz w mojej aplikacji i mam pole do wpisania kosztów. Jestem w stanie stworzyć płytę wykorzystując hiszpańską kulturę.Jaki jest najlepszy sposób obsługi sprawdzania poprawności przy użyciu różnych kultur?

Ale przy próbie aktualizacji rekordu otrzymuję fałszywą walidację jQuery. i otrzymuję domyślny komunikat o błędzie:

The field must be numeric.

W moim modelu widoku ustawiłem następujące atrybuty.

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

mam ustawić w moim pliku Gobal.asax z następujących

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

i powyższa metoda działa zgodnie z oczekiwaniami po stronie serwera w zmianie kultury. Ale walidacja po stronie klienta łamie nie-angielskie kultury, ponieważ javascript rozpoznaje tylko cyfry dziesiętne. Chciałbym poznać najlepszy sposób na poszerzenie walidacji strony klienta mvc o walidację specyficzną dla kultury.

EDIT

Nawiązując do adresu Mike'a I dokonały następujących zmian w JS wiązki. Js wiązka jest następujący

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")); 
} 

Na stronie układu I wprowadziły następująco

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)) 
+0

Nie jestem osobiście dobry w informacji o kulturze, ale możesz chcieć wziąć to "catch", rozwinąć go, aby uwzględnić ogólny wyjątek, i napisać to do konsoli (w celu debugowania): 'catch (Exception err) {Console.WriteLine (err); } "Powodzenia! – jp2code

+0

Czy zapoznałeś się z rozwiązaniem podanym w tym podobnym pytaniu: http://stackoverflow.com/questions/5199835/mvc-3-jquery-validation-globalizing-of-number-decimal-field –

+0

Dlaczego, na Boga, opróżniłbyś- złapać wyjątki, a następnie poprosić o SO, aby to naprawić? –

Odpowiedz

23

Istnieją 2 jQuery Globalizacji wtyczek.

Stara wersja jest v0.0.1 zawiera jeden skrypt globalize.js i ma podfolder cultures gdzie można znaleźć wszystkie hodowle skryptów takich jak:

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

te skrypty pozwalają dodać tyle kultur, jak chcesz tak byłoby perfekcyjnie mieć swój pakiet zbudowany w ten sposób:

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 będzie miał zbiór skryptów lokalizacyjnych, które Cię można ustawić tylko przy użyciu:

Globalize.culture('en-AU'); 

lub

Globalize.culture('es-AR'); 

Może użyć jakiejś bliskości, aby dowiedzieć się, jaka jest najbliższa kultura, z której chcesz skorzystać. Jeśli załadowałeś swój pakiet globalize.culture.es-AR.js, możesz ustawić Globalize.culture('es'); i Globalize, aby dowiedzieć się, czy chcesz użyć kultury "es-AR"; oczywiście jeśli dodałeś globalize.culture.es.js program ładujący wybrałby ten ostatni.

Nowa wersja jQuery Globalize (stable) to v1.0.0 i działa w zupełnie inny sposób.

Nadal ma główny plik skryptowy o nazwie globalize.js, ale trzeba dodać dużo więcej skryptów, aby działał.

Ktoś zbudował tool, który dokładnie pokazuje, jakiego skryptu potrzebujesz, w zależności od typu modułu (liczby, dat, walut), którego chcesz użyć

Jeśli zdecydują się skorzystać v1.0.0 widać, że narzędzie będzie sugerować zawierać skrypty bazowych (tylko cyfry):

  • cldr.js
  • CLDR/event.js
  • CLDR/supplemental.js
  • globalize.js
  • zglobalizować/number.js

oraz kilka skryptów CLDR JSON:

  • CLDR/uzupełniające/likelySubtags.json
  • CLDR/główny/{narodowe} /numbers.json
  • CLDR/uzupełniające/numberingSystems.json

Pliki te można znaleźć w pakiecie core i pakiecie numbers.
Jeśli chcesz sprawdzić daty, jest to package. Więcej informacji here.

Są to wszystkie pliki json i nie można ich powiązać. Możesz je załadować w czasie wykonywania, wykonując coś takiego:

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); 
    }); 
}; 

W każdym razie; załóżmy, że chcesz pozostać przy starym v0.0.1, który wciąż jest najlepszy.
Twój pakiet będzie miał skrypt globalizacji i te Kultura:

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 oferuje pewne inne dodatkowe rozszerzenia, które warto wziąć pod uwagę:

  • dodatkowy-methods.js
  • lokalizacja/messages_es_AR.js (komunikaty o błędach dla kultury)

Widziałem, że ustawiasz swoje kultura w Application_AcquireRequestState. Ktoś sugeruje, że lepiej zrobić to w Application_BeginRequest jak jest on przetwarzany wcześniej w rurze:

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

Wydaje się, że używasz tej jQuery plugin dla walidacji. Co normalnie by zrobić to tak szybko, jak załadować skrypt, skonfigurować i ustawić kultury walidacji niestandardowe:

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]); 
     } 
    }); 

Jedno, co tracisz to model wiążący dla dziesiętnych:

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

które można ustawić w Global.asax Application_Start:

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

to prawie wszystko, czego potrzebujesz.

Jest tylko irytujący problem z tym podejściem.
Załóżmy, że używasz kultury en-AU i wprowadzasz w polu liczbowym wartość: 10,4. Ta liczba jest całkowicie ważna w es-AR, ale powinna być nieważna dla kultury en-AU.

jQuery globalizacji uzna za ważne i tak jak to traslate go do 104 tutaj:

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

Globalize.parseFloat('10,4') dla kultury en-AU by przekształcić tę liczbę do 104.

samo stałoby się, gdyby robisz to samo dla Globalize.parseFloat('10.4') dla kultury es-AR; znowu stałoby się 104.

Możesz sprawdzić to zachowanie, uruchamiając to fiddle.

Zarówno ,, jak i . są prawidłowymi symbolami, ponieważ będą używane jako separatory dziesiętne i separatory tysięcy.

Jest kilka kwestii otwartych na ten temat na temat github i myślę, że byłoby to trudne do naprawienia, ponieważ teraz pracują nad nową wersją, przy czym ten sam problem utrzymuje się, przy okazji.

Jedziesz do twarzy ten sam problem na stronie serwera z naszymi decimal model binder:

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

gdzie CultureInfo.CurrentCulture to "en-AU", ponownie dałoby taki sam wynik: .

Może tam umieścić punkt przerwania i zobaczyć, w jaki sposób konwertuje wartość.

Prawdopodobnie łatwiej byłoby to naprawić, może używając wyrażeń regularnych.

Jeśli chcesz grać z rozwiązaniem z jQuery Validator v.0.1.1 lub jQuery Validator v.1.0.0, utworzyłem dwa repozytoria: here i here.

1

Dodałeś wiązek w RegisterBundles ale nie używać ich w układ strony. Dodałeś również nadmiarowy plik Jqueryui do rejestru RegisterBundles. Aktualizacja twoi RegisterBundles metoda tak:

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")); 
    } 

a następnie aktualizacja układ strony tak:

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

nadzieję, że pomoże :)

+0

Właściwie zapomniałem opublikować globalisationEN "i globalisationES". Zawarłem to w moim wniosku i przebudowałem swój post. Po dodaniu pliku globalisation.culture js otrzymuję komunikat o błędzie konsoli "Uncaught TypeError: t.addCultureInfo nie jest funkcją" –

+0

Czy wspomniałeś, jak używasz t.addCultureInfo? –

+0

Błąd faktycznie wystrzeliwuje z jquery.gloablize.js. –

Powiązane problemy