This is just a quick post - the tl;dr is this: jQuery Validation Globalize has been ported to Globalize 1.x. Yay! In one of those twists of fate I'm not actually using this plugin in my day job anymore but I thought it might be useful to other people. So here you go. You can read more about this plugin in an older post and you can see a demo of it in action here.
The code did not change drastically - essentially it was just a question of swapping parseFloat for parseNumber and parseDate for a slightly different parseDate. So, we went from this:
(function($, Globalize){
// Clone original methods we want to call into
var originalMethods ={
min: $.validator.methods.min,
max: $.validator.methods.max,
range: $.validator.methods.range
};
// Tell the validator that we want numbers parsed using Globalize
All of which is pretty self-explanatory. The only thing I'd like to draw out is that Globalize 0.1.x didn't force you to specify a date parsing format and, as I recall, would attempt various methods of parsing. For that reason jQuery Validation Globalize 1.0 exposes a $.validator.methods.dateGlobalizeOptions which allows you to specify the data parsing format you want to use. This means, should you be using a different format than the out of the box one then you can tweak it like so:
$.validator.methods.dateGlobalizeOptions.dateParseFormat=// your data parsing format goes here...
Theoretically, this functionality could be tweaked to allow the user to specify multiple possible date parsing formats to attempt. I'm not certain if that's a good idea though, so it remains unimplemented for now.
Globalize has hit 1.0. Anyone who reads my blog will likely be aware that I'm a long time user of Globalize 0.1.x. I've been a little daunted by the leap that the move from 0.1.x to 1.x represents. It appears to be the very definition of "breaking changes". :-) But hey, this is Semantic Versioning being used correctly so how could I complain? Either way, I've decided to write up the migration here as I'm not expecting this to be easy.
To kick things off I've set up a very simple repo that consists of a single page that depends upon Globalize 0.1.x to render a number and a date in German. It looks like this:
Or as I like to call it cldr-data. We just pulled down Globalize 1.x but we didn't pull down the data that Globalize 1.x relies upon. This is one of the differences between Globalize 0.1.x and 1.x. Globalize 1.x does not include the "culture" data. By which I mean all the globalize.culture.de-DE.js type files. Instead Globalize 1.x relies upon CLDR - Unicode Common Locale Data Repository. It does this in the form of cldr-json.
Now before you start to worry, you shouldn't actually need to go and get this by yourself, the lovely Rafael Xavier de Souza has saved you a job by putting together Bower and npm modules to do the hard work for you.
I'm using Bower for my client side package management and so I'll use that. Looking at the Bower dependencies downloaded when I upgraded my package I can see there is a cldr-data package. Yay! However it appears to be missing the associated json files. Boo!
To the documentation Batman. It says you need a .bowerrc file in your repo which contains this:
Unfortunately, because I've already upgraded to v1 adding this file alone doesn't do anything for me. To get round that I delete my bower_components folder and enter bower install. Boom!
If, like me, you were a regular user of Globalize 0.1.x then you know that you needed very little to get going. As you can see from our example you just serve up Globalize.js and the culture files you are interested in (eg globalize.culture.de-DE.js). That's it - you have all you need; job's a good'un. This is all very convenient and entirely lovely.
I realise this is a little "Who moved my cheese". I'll get over it. I do actually see the logic of this. It is certainly good that the culture date is not frozen in aspic but will evolve as the world does. But it's undeniable that in our brave new world Globalize is no longer a doddle to pick up. Or at least right now.
So. What do we actually need? Well I've consulted the documentation and I think I'm clear. Our simple demo cares about dates and numbers. So I'm going to guess that means I need:
On top of that I'm also going to need the various cldr dependencies too. That's not all. Given that I've decided which modules I will use I now need to acquire the associated cldr data. According to the docs here we're going to need:
cldr/supplemental/likelySubtags.json
cldr/main/<i>locale</i>/ca-gregorian.json
cldr/main/<i>locale</i>/timeZoneNames.json
cldr/supplemental/timeData.json
cldr/supplemental/weekData.json
cldr/main/locale/numbers.json
cldr/supplemental/numberingSystems.json
Figuring that all out felt like really hard work. But I think that now we're ready to do the actual migration.
By the way, I'm using fetch and promises to load the cldr-data. This isn't mandatory - I use it because Chrome lets me. (I'm so bleeding edge.) Some standard jQuery ajax calls would do just as well. There's an example of that approach here.
We've gone from not a lot of code to... well, more than a little. A medium amount. Almost all of that extra code relates to getting Globalize 1.x to spin up so it's ready to work. We've also gone from 2 HTTP requests to 13 which is unlucky for you. 6 of them took place via ajax after the page had loaded. It's worth noting that we're not even loading all of Globalize either. On top of that there's the old order-of-loading shenanigans to deal with. All of these can be mitigated by introducing a custom build step of your own to concatenate and minify the associated cldr / Globalize files.
Loading the data via ajax isn't mandatory by the way. If you wanted to you could create your own style of globalize.culture.de.js files which would allow you load the page without recourse to post-page load HTTP requests. Something like this Gulp task I've knocked up would do the trick:
The above is standard node/io type code by the way; just take the contents of the function and run in node and you should be fine. If you do use this approach then you're very much back to the simplicity of Globalize 0.1.x's approach.
And here is the page in all its post migration glory:
It looks exactly the same except 'de-DE' has become simply 'de' (since that's how the cldr rolls).
The migrated code is there for the taking. Make sure you remember to bower install - and you'll need to host the demo on a simple server since it makes ajax calls.
Before I finish off I wanted to say "well done!" to all the people who have worked on Globalize. It's an important project and I do apologise for my being a little critical of it here. I should say that I think it's just the getting started that's hard. Once you get over that hurdle you'll be fine. Hopefully this post will help people do just that. Pip, pip!
I’ve long used Globalize for my JavaScript number formatting / parsing needs. In a current project I’m using Knockout for the UI. When it came to data-binding numeric values none of the default binding handlers seemed appropriate. What I wanted was a binding handler that:
Was specifically purposed for dealing with numeric values
Handled the parsing / formatting for the current locale (and I naturally intended to use Globalize for this purpose)
Like so much development we start by standing on the shoulders of giants. In this case it’s the fantastic Ryan Niemeyer who put up a post on StackOverflow that got me on the right track.
Essentially his approach provides an “interceptor” mechanism that allows you to validate numeric data entry on input and format numeric data going out as well. Very nice. Into this I plugged Globalize to handle the parsing and formatting. I ended up with the “valueNumber” binding handler:
? currentValue.toString().replace(".",Globalize.findClosestCulture().numberFormat["."])// Displays correct decimal separator for the current culture (so de-DE would format 1.234 as "1,234")
Using this binding handler you just need to drop in a valueNumber into your data-bind statement where you might previously have used a value binding. The binding also has a couple of nice hooks in place which you might find useful:
numberFormat (defaults to "n2")
allows you to specify a format to display your number with. Eg, "c2" would display your number as a currency to 2 decimal places, "p1" would display your number as a percentage to 1 decimal place etc
isNullable (defaults to false)
specifies whether your number should be treated as nullable. If it's not then clearing the elements value will set the underlying observable to 0.
Finally when the element gains focus / becomes active the full underlying value is displayed. (Kind of like Excel - like many an app, the one I'm working on started life as Excel and the users want to keep some of the nice aspects of Excel's UI.) To take a scenario, let's imagine we have an input element which is applying the "n1" format. The underlying value backing this is 1.234. The valueNumber binding displays this as "1.2" when the input does not have focus and when the element gains focus the full "1.234" is displayed. Credit where it’s due, this is thanks to Robert Westerlund who was kind enough to respond to a question of mine on StackOverflow.
Finally, here’s a demo using the "de-DE" locale:
The version of Globalize used in the binding handler is Globalize v0.1.1. This has been available in various forms for quite some time but as I write this the Globalize plugin is in the process of being ported to the CLDR. As part of that work it looks like the Globalize API will change. When that gets finalized I’ll try and come back and update this.