Globalize

If you want to patch the jQuery Validation date and number validation to be locale specific then you can using jquery-validate-globalize.js. This demo is not strictly related to jVUN - it's really more about Globalize and jquery-validation-globalize. It's built using jVUN but if you're here strictly for information about jquery-validation-globalize then just ignore the "Model", "View" and "ASP.Net and Globalize" tabs below. Everything else is just normal JavaScript and jQuery Validation code.

jquery-validation-globalize is available on Bower. To install it you should first ensure you have a .bowerrc file in place which looks like this:

{
  "scripts": {
    "preinstall": "npm install cldr-data-downloader@0.2.x",
    "postinstall": "node ./node_modules/cldr-data-downloader/bin/download.js -i bower_components/cldr-data/index.json -o bower_components/cldr-data/"
  }
}

Then you can add the package as a dependency of your project with bower install jquery-validation-globalize --save.

To find more information about this then you could have a read of my blogpost. This library replaces the jQuery Validation date and number validation with an implementation that depends on Globalize. (By the way, the post relates to when I first created jquery-validate-globalize.js and it depended on Globalize 0.1.x - it has now been migrated to Globalize 1.x which is very different.)

Take a look at the demo below, do note that you can switch cultures as you wish.

Loading...

Here's the model, note that the Range attribute decorates the decimal property and a Required attribute decorates our nullable DateTime.:

    using System.ComponentModel.DataAnnotations;

    namespace jQuery.Validation.Unobtrusive.Native.Demos.Models
    {
        public class GlobalizeModel
        {
            [Range(10.5D, 20.3D)]
            public decimal Double { get; set; }

            [Required]
            public DateTime? DateTime { get; set; }
        }
    }
        

Here's the view (which uses the model):

    @model jQuery.Validation.Unobtrusive.Native.Demos.Models.GlobalizeModel
    @using (Html.BeginForm())
    {
        <div class="row">
            @Html.LabelFor(x => x.Double, "Double")
            @Html.TextBoxFor(x => x.Double, true)
        </div>

        <div class="row">
            @Html.LabelFor(x => x.DateTime, "DateTime")
            @Html.TextBoxFor(x => x.DateTime, true)
        </div>

        <div class="row">
            <button type="submit" class="btn btn-default">Submit</button>
            <button type="reset" class="btn btn-info">Reset</button>
        </div>
    }
    

Here's the HTML that the view generates:

    <form action="/AdvancedDemo/Globalize" method="post">
        <div class="row">
            <label for="Double">Double</label>
            <input data-msg-number="The field Double must be a number." 
                data-msg-range="The field Double must be between 10.5 and 20.3." 
                data-msg-required="The Double field is required." 
                data-rule-number="true" 
                data-rule-range="[10.5,20.3]" 
                data-rule-required="true" 
                id="Double" name="Double" type="text" value="0" />
        </div>
        <div class="row">
            <label for="DateTime">DateTime</label>
            <input data-msg-date="The field DateTime must be a date." 
                data-msg-required="The DateTime field is required." 
                data-rule-date="true" 
                data-rule-required="true" 
                id="DateTime" name="DateTime" type="text" value="" />
        </div>
        <div class="row">
            <button type="submit" class="btn btn-default">Submit</button>
            <button type="reset" class="btn btn-info">Reset</button>
        </div>
    </form>
        

Here's an example of the scripts that you might serve up if you wanted to use jquery.validate.globalize.js. Note that jquery.validate.globalize.js is last as it depends on both jquery.validate.js and globalize.js.

    <script src="/Scripts/jquery.validate.js"></script>

    <!-- cldr scripts (needed for globalize) -->
    <script src="/bower_components/cldrjs/dist/cldr.js"></script>
    <script src="/bower_components/cldrjs/dist/cldr/event.js"></script>
    <script src="/bower_components/cldrjs/dist/cldr/supplemental.js"></script>

    <!-- globalize scripts -->
    <script src="/bower_components/globalize/dist/globalize.js"></script>
    <script src="/bower_components/globalize/dist/globalize/number.js"></script>
    <script src="/bower_components/globalize/dist/globalize/date.js"></script>

    <script src="/Scripts/jquery.validate.globalize.js"></script>
        

Here's the JavaScript that loads the required cldr data via AJAX and initialises the validation - notice it also initialises Globalize to your culture:

    $.when(
        $.get("/bower_components/cldr-data/supplemental/likelySubtags.json"),
        $.get("/bower_components/cldr-data/main/en/numbers.json"),
        $.get("/bower_components/cldr-data/supplemental/numberingSystems.json"),
        $.get("/bower_components/cldr-data/main/en/ca-gregorian.json"),
        $.get("/bower_components/cldr-data/main/en/timeZoneNames.json"),
        $.get("/bower_components/cldr-data/supplemental/timeData.json"),
        $.get("/bower_components/cldr-data/supplemental/weekData.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 () {
        // Initialise Globalize to the current UI culture
        Globalize.locale("en");

        $("form").validate({
            submitHandler: function (form) {
                alert("This is a valid form!");
            }
        });
    });
        

Globalize has locales (eg "en-GB", "de" etc). ASP.Net has cultures (eg "en-GB", "de-DE" etc). There is not a 1-to-1 relationship between Globalize's locales and ASP.Net's cultures but you can make a good approximation. How? Well this simple function will do the job for you:

    /// <summary>
    /// Identifies and returns the default locale to use by mapping as close as possible from ASP.Nets culture to Globalize's locales
    /// </summary>
    /// <returns>The default locale to use for the current culture; eg "de"</returns>
    public string GetDefaultLocale()
    {
        const string localePattern = "~/bower_components/cldr-data/main/{0}"; // where cldr-data lives on disk
        var currentCulture = CultureInfo.CurrentCulture;
        var cultureToUse = "en-GB"; //Default regionalisation to use

        //Try to pick a more appropriate regionalisation
        if (Directory.Exists(HostingEnvironment.MapPath(string.Format(localePattern, currentCulture.Name)))) //First try for a en-GB style directory
            cultureToUse = currentCulture.Name;
        else if (Directory.Exists(HostingEnvironment.MapPath(string.Format(localePattern, currentCulture.TwoLetterISOLanguageName)))) //That failed; now try for a en style directory
            cultureToUse = currentCulture.TwoLetterISOLanguageName;
        return cultureToUse;
    }
                

This takes the current culture that ASP.Net is running as and then looks for a Globalize culture that is as close a match as possible (finally falling back to en-GB if it finds nothing suitable). You can use this to initialise your app to the most appropriate culture for your users. When this is running hosted in ASP.Net that's exactly how this demo page works. This page defaults to the en locale - that's right, *your* locale (or at least what ASP.Net thinks your locale ought to be). If you switch your browser to say, de-DE and reload this page then you'll see the app has switched to the closest Globalize appropriate locale it can find.

Do note that this depends on the following settings in the web.config:

<configuration>
  <system.web>
    <globalization culture="auto" uiCulture="auto" />
    <!--- Other stuff.... -->
  </system.web>

  <system.webServer>
    <staticContent>
      <remove fileExtension=".json" />
      <mimeMap fileExtension=".json" mimeType="application/json" />
    </staticContent>
  </system.webServer>
</configuration>
        

The globalization settings activate ASP.Nets auto culture feature. This switches culture per user based on the user's setup - eg German users may be switched to the "de-DE" culture, Brits to "en-GB" etc. The .json setting ensures that the web server is happy to serve up JSON files (which the culture data is stored in). Surprisingly ASP.Net doesn't offer this by default.