How do I use the "best fit" locale in JavaScript? - javascript

My understanding of the problem so far:
I'm reading this MDN document which says:
The locales argument, after stripping off all Unicode extensions, is interpreted as a prioritized request from the application. The runtime compares it against the locales it has available and picks the best one available. Two matching algorithms exist: the "lookup" matcher follows the Lookup algorithm specified in BCP 47; the "best fit" matcher lets the runtime provide a locale that's at least, but possibly more, suited for the request than the result of the Lookup algorithm. If the application doesn't provide a locales argument, or the runtime doesn't have a locale that matches the request, then the runtime's default locale is used. The matcher can be selected using a property of the options argument (see below).
When I read this, it appears that I can use "lookup" and "best fit" the same way, sometimes getting different results.
However
I can do:
new Intl.DateTimeFormat('lookup', options).format(date)
But not:
new Intl.DateTimeFormat('best fit', options).format(date)
I'm using a current version of Firefox (65.0.1 64-bit on Ubuntu), and Firefox doesn't have any open bugs on the issue as far as I can tell, but I get a RangeError when I use 'best fit'.
Remaining questions:
Am I supposed to be able to give the 'best fit' as the locale argument, or can I only use it as the localeMatcher in the options argument? If I can only use 'best fit' inside options, then what should I specify as the locale, given that locale is required when options is present?
Or should I just open a ticket in Bugzilla?

In your first example, a string is passed to the Intl.DateTimeFormat so it appears to work. But all it does is not recognize the string as a valid locale and then uses the default.
The second example doesn't work because the string is not formatted in accordance with BCP 47.
You've misinterpreted the API documentation. This:
The locales argument must be either a string holding a BCP 47 language
tag, or an array of such language tags. If the locales argument is not
provided or is undefined, the runtime's default locale is used.
clearly indicates the first argument should be a string or (array of strings) that is/are a locale identifiers.
Then this:
The options argument must be an object with properties that vary
between constructors and functions.
So your call to the should look like this:
new Intl.DateTimeFormat([],{localeMatcher: "best fit"}).format(date)

Related

Using Python to parse date/time strings in *exactly* the same way as Javascript?

I have some client-side JavaScript code which reads a string and tries to parse it into a Date() object via new Date(theString), displaying the resulting Date as a UTC string to the user. If it's a string that can't be turned into a Date, of course, it becomes an Invalid Date, in which case instead it displays "Not a date/time."
I also have some server-side Python code which essentially does the same thing: takes the user-submitted maybe-a-date, and stores it as either a UTC string or as "not a date".
The trick is, I need the two pieces of code to always behave exactly the same on every single string. I could certainly just make a tiny Python endpoint that uses the existing Python code to send the appropriate response back to the client instead of using Date() client-side, but for various reasons that's an undesirable solution.
So is there a way to translate strings into dates in Python that's guaranteed to work exactly the same way as new Date(myString) does in JavaScript?
So is there a way to translate strings into dates in Python that's guaranteed to work exactly the same way as new Date(myString) does in JavaScript?
No.
It's impossible, because:
Parsing of strings other than the two formats specified in ECMA-262 is implementation dependent and it is easily demonstrated that different implemetations parse the same string differently (e.g. Why does Date.parse('COVINGTONOFFICE-2') return a real date?)
How various implementations parse unsupported formats is not documented, so you can only determine the rules through observing behaviour of every possible string, including those that look nothing like a date, in every implementation, then knowing which implementation you were trying to imitate

JavaScript's standard API to get the runtime's default locale?

In order to determine the current locale I found different approaches:
Being in the browser, most people suggest looking at the HTTP headers (Accept-Language)
Some people suggest consulting navigator.language
Being in the backend (Node.js) and apart of HTTP, it is suggested to consult the (system dependent) process.env
On the other side, the ECMAScript Internationalization API defines the locales argument to each of the Intl constructors as optional:
If the locales argument is not provided or is undefined, the runtime's
default locale is used.
So, it seems as if there should be a browser-independent and OS independent way to get "the runtime's default locale".
Is there a more straight forward way to get the runtime's default locale than
new Intl.NumberFormat().resolvedOptions().locale
?
The question How/Where does JavaScript detect the default locale? is different as it asks for the implementation of detecting the default locale (in a browser host). In contrast to that, my question is not about implementation but about the existence of a standard API.
I'm not aware of a more straight-forward approach, but as you have already pointed out,
new Intl.NumberFormat().resolvedOptions().locale is a solution
DefaultLocale is an abstract operation, which might or might not be implemented in the future.
Hence, I would argue for polyfilling this function as:
if (typeof DefaultLocale === "undefined") {
function DefaultLocale() {
return new Intl.NumberFormat().resolvedOptions().locale;
}
}
So, your code will reasonably assume the existence and call this function. In some point in the future, when this function is implemented in a standard manner, you will be able to clean up by removing the chunk from above.
Just adding this as a choice, doesn't seem to be much better than the shim from Lajos because I guess one would need to have a request and to interpret the q (see the MDN excerpt below).
If you can get hold of a request's headers, most browsers inject:
Accept-Language
en-US,en;q=0.5
Accept-Language
en-US,en;q=0.9,ro;q=0.8,en-GB;q=0.7
From MDN docs https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language :
A language tag (which is sometimes referred to as a "locale identifier"). This consists of a 2-3 letter base language tag
representing the language, optionally followed by additional subtags
separated by '-'. The most common extra information is the country or
region variant (like 'en-US' or 'fr-CA') or the type of alphabet to
use (like 'sr-Latn'). Other variants like the type of orthography
('de-DE-1996') are usually not used in the context of this header.
Any language; '*' is used as a wildcard.
;q= (q-factor weighting) Any value placed in an order of preference expressed using a relative quality value called weight.
The way I understand it, the highest weight (q-value) should be prefered, if available

MomentJs output Date of toDate() is incorrect

I've started using momentJs in an Angular/Typescript project. (Included incase it's relevant in any way although I very much doubt it)
In the run method of my module I call
moment.locale(window.navigator.language);
which correctly sets the locale to en-GB in my instance. Further down the line I use moment to parse a GB time.
when doing the following:
var mom = moment("24/11/2015 00:00:00");
for example. This populates a new moment object using the defaults set on the moment global (If i understand how it should work correctly). moms date is set to 2016-12-11T00:00:00.000Z. This clearly means it's parsed the given string in en-US instead of en-GB which was set via Locale in a default setting prior to this call. Is there anything I've missed in configuration/setup of moment which would make this not work?
I've also inspected the _locale property of my variable. mom._locale is set to en-gb and I can see the L,LL,LLL etc etc formats are all en-GB formatted values (as they should be).
running mom.toDate(); unsurprizingly returns the 2016 date stored internally by the moment object.
Some misc information I forgot to include:
I am using the latest release of momentjs from NuGet (Version 2.10.6 at time of writing) and I've included moment-with-locales.js in my HTML
Using any recent version of MomentJS, you should see why in the console:
Deprecation warning: moment construction falls back to js Date. This is discouraged and will be removed in upcoming major release. Please refer to https://github.com/moment/moment/issues/1407 for more info.
Unless you specify a format string, MomentJS relies on the Date object's parsing, and unfortunately, regardless of locale the Date object will, with a string using /, assume U.S. format. One of the many, many things that aren't quite right with Date.
You'll need to use a format string, or supply the string in the simplified ISO-8601 format used by Date. From Parse > String:
When creating a moment from a string, we first check if the string matches known ISO 8601 formats, then fall back to new Date(string) if a known format is not found.
var day = moment("1995-12-25");
Warning: Browser support for parsing strings is inconsistent. Because there is no specification on which formats should be supported, what works in some browsers will not work in other browsers.
For consistent results parsing anything other than ISO 8601 strings, you should use String + Format.
So I got around this by fetching the locale data from moment and just passing it into the format parameter. Considering the example input of "24/11/2015 00:00:00" I would structure my format as below:
var format = moment.localeData().longDateFormat('L') + " " + moment.localeData().longDateFormat("LTS");
this generates the format mask of "DD/MM/YYYY HH:mm:ss".
You can mix and match whatever formats you want and this will be locale specific to whatever you set moment.locale("") to be (presuming you have the locale information setup in moment already)
This is a crazy workaround and I'm surprised that moment doesn't presume locale information as default when parsing. TJCrowder has raised an issue on Github with the moment guys which I suggest anyone who cares should comment on. https://github.com/moment/moment/issues/2770
You're probably better off passing the format to moment directly and validating the string before hand. This will ultimately reduce the amount of debugging you'll need to do and get you up and running straight away.
var mom = moment("24/11/2015 00:00:00", "DD/MM/YYYY HH:mm:ss");
You could try the new(ish) Intl API but browser support is limited (IE11+), so I would recommend having a user select the month in a dropdown or something to force them to input a certain way.

Using moment.js, how to display the current date format for the user?

Given a text field, I want to have a suitable placeholder. A typical placeholder will be something like: "mm/dd/yyyy".
However, I would like to use locale-aware dates using moment.js.
This means that I will be specifying "l" as the moment.js date format, howe do I determine the date format that moment.js will be using in this case?
The user will not understand what "l" means, so using this value in the placeholder text makes very little sense.
Specifically, I am hoping to be able to access something like moment's internal "defaultLongDateFormat". (Though that is merely a default - moment.js probably updates it or has some other mapping at runtime for locale-aware date formats - I would like to access that mapping.)
EDIT:
There are multiple downvotes (who aren't explaining why they're downvoting it).
I think this is because they arent' understanding the question, so here are some examples:
I want a function such that:
getFormat("l") -> "mm/dd/yyyy", or equivalent for the US locales.
getFormat("l") -> "dd/mm/yyyy", or equivalent, for the AU locales.
I do not want to format a given date, or to parse a given date - I merely want to determine it's user-friendly format given an arbitruary moment.js format, specifically, for 'l'.
I don't think it's exposed nicely, but if the browser has its language configured correctly you can do something like this:
var lang = navigator.languages ? navigator.languages : navigator.language;
moment().locale(lang).localeData()._longDateFormat['L']
Languages behave slightly differently depending on which browser you're using, so don't know how reliable this is.
Follow up to Adam R's answer:
Seems to have been exposed by now:
localeData.longDateFormat(dateFormat);
returns the full format of abbreviated date-time formats LT, L, LL and so on
(source: http://momentjs.com/docs/)
Get the currently used locale data by moment.localeData()

Javascript: data format, akin to Spring Property Editors

Is there any library already available for javascript data format / transformation?
Example: Date, Time, Numbers, etc., can be converted to text with predefined formats.
These formats are chiefly locale based. But some how the mechanism to convert value (data, date, time..) should be there, that accepts custom format too to translate into desired formatted text.
On the contrary, the same library should be capable of parsing the text to value, with the source format knowledge.
Some thing similar to Spring's Property Editors or Converter.
Quick Searches; http://numeraljs.com/ http://www.datejs.com/
I should have mentioned the fact that, I am using DOJO as our JS Widget Library and with it comes methods and classes to address formatting / parsing, bundled.
Now that we have DOJO in place, I will be putting the code in the abstract layer (wrapper) over the library to keep the solution Library-Agnostic.
Find the code below - for brevity only the snippet is mentioned, could have done JSFiddle (may be in a day or two).
// get the DOJO handle, either from global "dojo" variable or via AMD
// var dojo = require('dojo');
............
// for date
dojo.date.locale.format(new Date(), {selector: "date",datePattern:"dd/mm/yyyy"});
// output: 31/06/2013
// for number
dojo.number.format(9999999.99,{type: "decimal",pattern:"#,##,####.##"});
// output: 9,99,9999.99
............
NOTE: An important feature it addresses is that it understands locale and can do the formatting automatically w.r.t the locale configured. DOJO library staunchly follows Java style of data formatting. If you are a Java Geek, you would find the library and format semantics easy to follow.
Find the DOJO link in this respect ://dojotoolkit.org/reference-guide/1.7/quickstart/numbersDates.html (could not post the complete link as I do not have enough reputations, please prefix "http" to the link)
Still, I would like to welcome better solution if any, for the given context.
As part of libraries used, we are using jQuery, Underscore, Backbone and DOJO primarily , all across for specific purposes in the application.
I use Moment.js for all my JS date formatting and manipulations. http://momentjs.com/

Categories

Resources