2015-09-15 18 views
21

Sto tentando di creare un'applicazione MVC multilingue. Ho un modulo nella mia domanda e ho campo per inserire un costo. Sono in grado di creare un disco usando la cultura spagnola.Qual è il modo migliore per gestire la convalida con culture diverse

Ma nel tentativo di aggiornare il record sto ricevendo la convalida jquery falsa. e ricevo un messaggio di errore predefinito come:

The field must be numeric.

Nel mio modello di vista ho impostato i seguenti attributi.

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

ho posto nel mio file Gobal.asax con i seguenti

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

ed il metodo di cui sopra funziona come previsto sul lato server nel cambiare la cultura. Ma la convalida del lato client si interrompe sulle culture non inglesi poiché javascript riconosce solo i letterali decimali. Mi piacerebbe conoscere il modo migliore per estendere la convalida del lato client mvc con la convalida specifica della cultura.

EDIT

Con riferimento alla url di Mike Ho fatto seguito delle variazioni di Js fascio. Js bundle è la seguente

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

Nella pagina di layout ho implementato come segue

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

Non sono personalmente bravo con le informazioni di cultura, ma potresti voler prendere quel 'catch', espanderlo per includere l'eccezione generale e scriverlo alla console (per scopi di debug):' catch (Exception err) {Console.WriteLine (err); } 'Buona fortuna! – jp2code

+0

Hai esaminato la soluzione fornita in questa domanda simile: http://stackoverflow.com/questions/5199835/mvc-3-jquery-validation-globalizing-of-number-decimal-field –

+0

Perché mai vorresti- cattura le eccezioni e poi chiedi a SO di risolvere il problema? –

risposta

23

Ci sono 2 jQuery Globalizzare plugin.

La vecchia versione è v0.0.1 contiene uno script di globalize.js e ha una sottocartella cultures dove si possono trovare tutte le culture di script come ad esempio:

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

quegli script permettono di aggiungere come molte culture come si desidera quindi sarebbe perfettamente bene di avere il vostro pacco costruito in questo modo:

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 avrà una raccolta di script di localizzazione che si può impostare semplicemente usando:

Globalize.culture('en-AU'); 

o

Globalize.culture('es-AR'); 

È possibile utilizzare una sorta di prossimità per capire quale sia la cultura più vicina che si desidera utilizzare. Se è stato caricato nel pacchetto globalize.culture.es-AR.js è possibile impostare Globalize.culture('es'); e Globalize sarebbe in grado di capire che si desidera utilizzare la cultura 'es-AR'; ovviamente se hai aggiunto globalize.culture.es.js il loader avrebbe scelto quest'ultimo.

La nuova versione di jQuery Globalize (stabile) è v1.0.0 e funziona in un modo completamente diverso.

Ha ancora il file di script principale chiamato globalize.js ma è necessario aggiungere molti più script per farlo funzionare.

Qualcuno ha creato un tool che indica esattamente quale script è necessario, a seconda del tipo di modulo (numero, date, valute) che si desidera utilizzare.

Se stai optando per utilizzare v1.0.0 vedrete che lo strumento suggerisce di inserire gli script di base (solo numeri):

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

, più alcuni script CLDR JSON:

  • CLDR/supplementare/likelySubtags.json
  • CLDR/main/{locale} /numbers.json
  • CLDR/supplementare/numberingSystems.json

È possibile trovare questi file nel pacchetto core e nel pacchetto numbers.
Se si desidera convalidare le date, questo è il package. Ulteriori informazioni here.

Questi sono tutti file json e non è possibile raggrupparli. È possibile caricarli in fase di esecuzione facendo qualcosa del genere:

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

In ogni caso; diciamo che vuoi restare fedele alla vecchia v0.0.1 che è ancora la migliore.
Il vostro pacco avrà lo script globalizzare e quelli della cultura:

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 offre qualche altra estensione aggiuntivo che si potrebbe prendere in considerazione:

  • ulteriore-methods.js
  • localizzazione/messages_es_AR.js (messaggi di errore per la cultura)

Ho visto che stai impostando il tuo cultura nel Application_AcquireRequestState. Qualcuno suggerisce che è meglio farlo in Application_BeginRequest come viene elaborato in precedenza nel tubo:

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

Sembra che si sta utilizzando questo jQuery plugin per la validazione. Quello che normalmente vorrei fare è, non appena ho caricare lo script, configurare la cultura e impostare la convalida personalizzato:

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

Una cosa che ti manca è un modello legante per decimali:

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

che può essere impostata nel Global.asax Application_Start:

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

questo è praticamente tutto il necessario.

C'è solo un fastidioso problema con questo approccio.
Diciamo che stai usando la cultura en-AU e inserisci nel tuo campo numerico un valore: 10,4. Questo numero è perfettamente valido in es-AR ma non dovrebbe essere valido per la coltura en-AU.

jQuery Globalize lo considererà valido comunque come sarebbe bisognava tradurre a 104 qui:

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

Globalize.parseFloat('10,4') per la cultura en-AU avrebbe trasformato il numero a 104.

Stessa cosa accadrebbe se fai lo stesso per Globalize.parseFloat('10.4') per la cultura es-AR; diventerebbe, ancora, 104.

È possibile verificare questo comportamento eseguendo questo fiddle.

Entrambi , e . sono simboli validi in quanto sarebbero utilizzati come separatore decimale e separatore di migliaia.

Ci sono alcuni problemi aperti su questo argomento su github e credo che sarebbe difficile da risolvere poiché ora stanno lavorando sulla nuova versione, dove lo stesso problema persiste, tra l'altro.

si sta andando ad affrontare lo stesso problema sul lato server con il nostro decimal model binder:

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

dove il CultureInfo.CurrentCulture è "en-AU", di nuovo, produrrebbe lo stesso risultato: .

Può posizionare un punto di interruzione e vedere come converte il valore.

Suppongo che probabilmente sarebbe più semplice da risolvere, magari utilizzando alcune espressioni regolari.

Se si desidera giocare con la soluzione con jQuery Validator v.0.1.1 o jQuery Validator v.1.0.0 ho creato due repository here e here.

1

Hai aggiunto fasci in registerBundles ma non li uso nel layout di pagina. Hai anche aggiunto il file jqueryui ridondante in RegisterBundles. Aggiornamento metodo tuoi registerBundles come questo:

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

e poi aggiornamento layout di pagina come questa:

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

Spero che questo vi aiuterà :)

+0

In realtà ho dimenticato di postare globalisationEN "e globalisationES". L'avevo incluso nella mia domanda e ho rinnovato il mio post. Dopo aver aggiunto globalisation.culture js, ricevo un errore di console "Uncaught TypeError: t.addCultureInfo non è una funzione" –

+0

indicherà come stai usando t.addCultureInfo? –

+0

L'errore si verifica effettivamente da jquery.gloablisation.js. –