Using accept language header to guess user's country, if available - javascript

I want to use Javascript/PHP to determine if a user's browser has a country (locale variant) defined in the accept-language header.
Knowing that the contents of the accept-language header value can vary, and is not always accurate, is there a way to logically determine if a country present in the accept-language header string? And then extract that via JS?
For example, if a user's accept language header contains en-US, I would want to set a local variable="US".
My intial thought was to create a very large array of all accept header language and locale values and check against that, but that did not seem like a very efficient approach.

The Accept-Language header has a couple of formats it can take, see the documentation on MDN. Getting the preferred languages as a structured list (like an array sorted by q-factor) is just a matter of parsing that list in a way that is resilient to all the possible permutations of the header values.
Instead of trying to write this logic yourself, it's simpler to use a library built for this exact purpose, which will be better at handling all kinds of weird edge-cases. For example, willdurand/Negotiation allows you to parse the Accept-Language header and match it to a list of locales supported by your site to find the best language to use.
You will have to do this on the server-side (in PHP). Once you know the language, you can pass that to client-side JavaScript in a number of ways. For example, you can use the JS tag to include a piece of JavaScript in your page template that sets a global variable with the determined language.

Related

Culture-sensitive string sorting

In my .NET c# code, I would like to sort some strings - specifically, currently my app support two locales, English and Chinese (possibly more in future). And I would like to return a list of names to the client side.
right before the list of strings is returned to the client, I would like to sort the strings.
I basically need to do what Javascript does for "localeCompare". Is that something that can be done on the C# code? I found some thing related to CultureInfo, but it seems to me that I need to set that value dynamically.
There is a Sort overload, accepting an IComparer, that can be used culture aware.
Your client should pass it‘s desired language via Accept-Language request-header when requesting the API or it could be a claim or setting for the logged in user.
Here is an example within your controller on the API side:
var requestLanguage = "zh-Hans"; // pass the correct one
namesList.Sort(StringComparer.Create(new CultureInfo(requestLanguage), true));
The true parameter indicates that it’s case sensitive. You can find the cultureinfo on MSDN.
Docs about the String Comparer.

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

How can I load padded JSONp as a non-executable, e.g. a string or data like regular JSON?

I may have a thorny problem coming up due to a complex mix up in organisational release cycles between teams and organisations. Obviously I'm working for a better long term solution to the underlying problem, but I may have to handle a brief period (between a few days and a month, depending on organisational factors out of my control) where some client-side javascript needs to access some JSON from a source that simultaneously:
Only serves it padded like for jsonp, for example:
window['callback']({ "key": "value", "more": ["json", "data" ] });
Only serves it with a json (non-executable) MIME type, with no_sniff, which means Chrome and various other browsers will block it from being executed as a javascript function call.
Is it possible for me to load it as if it was a flat text file, like I would if it was a CSV, as a string? Then I could chop off the jsonp padding with simple regexs then parse the string as JSON.
The libraries jQuery and D3 are available already for this, both of which have file loading features.
Most of the requests will be coming from the same domain, but I also need to cover some cases where a call may come from a different subdomain. I don't personally have direct access to server-side configuration but might be able to get some simple changes made by people who do in time.
As a commenter suggests, what I would do is to build a simple server-side proxy which fetches the data and serves it back with the required specs, being them CORS headers or anything else you need.
This assumes you have access to an environment capable of running this tool.
Apart from this or having the headers changed by someone else, you don't have other choices...

i18n: Access locale resolution logic in JavaScript

I'm developing and internationalized Single Page App with 2 sorts of localized text:
'static' text, typically text in my HTML templates.
'dynamic' text, i.e text that lives in the database, typically the description of a product on an e-commerce site.
It's type 2 I'm having trouble with. Say my app officially supports English, French and German, and I get from my database an object such as :
{
description: {
'en': "It's an awesome product.",
'en_UK': "This product is ace.",
'fr': "C'est un excellent produit."
// German's missing
}
}
Now the challenge is to dynamically choose what locale should be chosen for display, given the user's locale and what locales are available in this particular object.
I assume most i18n JavaScript libraries have their own 'locale resolution' logic built-in, but I haven't found one that exposes this logic for the client to use.
Does anyone know a JS library that addresses this, or a good way to solve this issue? (if it's AngularJs-compatible, it's even better).
Thanks in advance!
Disclaimer: I'm a co-author of L20n and one of the developers of
l10n.js used in Firefox OS.
The term that's commonly used to describe this logic is language negotiation.
Most localization libraries should have some sort of language negotiation algorithm included. It can be as basic as trying to match the value of navigator.language with a list of available languages. More sophisticated approaches will look at both the language tag (en in en-US) and the region tag (US in en-US) to try to find a best match.
There's a proposal to expose a language negotiation method on the ECMAScript's Intl object, but for now it's not possible to use its internal logic for this purpose.
Getting the list of languages preferred by the user is not as easy as it should be. There's navigator.language in most browsers (which is the user's preferred language in Firefox and the language of the browser UI in Chrome), navigator.userLanguage in Internet Explorer, and the new navigator.languages which is an ordered list of user's preferred languages.
A server-side alternative is to use the Accept-Language header of the HTTP request which currently is the most reliable way of finding out what the user's preferences are.
Once you have the list of user's preferred languages you can perform the language negotiation.
Here are a few examples of libraries that perform language negotiation:
in-browser-language,
l10n.js used in Firefox OS,
a non-standard extension of the ECMA-402 Intl object which adds Intl.prioritizeLocales.
For your particular use-case, you can choose to do one of the two following things:
perform the language negotiation on the client side using navigator.language || navigator.userLanguage and send a request to the database specifying which language you're interested in, or
send the request with the user's Accept-Language header and perform the language negotiation on the server side, and then query the database for the correct translation.
Both solutions have the benefit of not sending the entire set of translations to the client when only one will be eventually used.
For solution #1, given that you're using Angular, I can suggest using L20n 1.0.x which integrates with Angular via the ng-l20n module. You should be able to use the supportedLocales property to get the negotiated list of languages and use the first element of that list to query the database.
For solution #2, it all depends on your server-side setup, but if you're using node.js, you can try using one of the following modules:
the afore-mentioned in-browser-language,
locale.
Staś Małolepszy's answer is a good reference of the resources available for this problem domain.
However, I found that none of these libraries suited my needs (either because their language negotiation logic was too simplistic, or because it wasn't really exposed).
Therefore, I implemented my own custom solution for language negotiation. You can experiment with it in this Plunkr: http://plnkr.co/edit/Cfv49ZcQWJcqwOXYcjtw?p=preview
The API is a function negotiate(availableLocales, requestedLocales, defaultLocale), a bit similar to the ECMAScript proposal (with the difference that it returns a single locale value, not a list).
// simple cases
negotiate(["fr"], ["de","fr"], 'en'); // => fr
negotiate(["en"], ["de","en-US","en"], 'en'); // => en
// less natural cases, where the solution is more opinionated
negotiate(["fr-FR"], ["fr"], 'en'); // => fr-FR
negotiate(["en","en-UK","fr"], ["fr-FR"], 'en'); // => fr
negotiate(["fr","de"], ["fr-FR","de"], 'en'); // => fr
I made the choice to account for 'locale inheritance' (e.g en-US is derived from en) in a non-trivial, opinionated manner . I do not claim this is in compliance with whatever standards exist on this topic.
It surprised me that I couldn't find any library that provides this sort of functionality. My best explanation is that i18n of dynamic content isn't needed that often.
Finally, as to where I get the list of requested languages, as I was in an Single Page App configuration I chose to send an AJAX request to my server, with the content of the Accept-Language header in the response.

Locale: Browser or OS Setting?

What does it mean when a "locale" is set to e.g. Japanese? Does it mean the browser is set to be ready to recognize Japanese characters? Or, is it more related to an OS setting?
Related to i18n (internationalization and localization), how do we detect that a user who is visiting our site has a e.g. Japanese locale using JavaScript? Will the following simple check suffice?
var userLocale = navigator.language || navigator.userLanguage;
if (userLocale.toLowerCase() == 'ja-jp') { ... }
Can a Japanese locale browser return something else rather than ja-jp?
Thanks beforehand.
First we need to define what Locale is. In the context you are using it is a ISO639 Language Identifier optionally followed by ISO3166 Country Identifier. This is used to determine end user's preferences (like language of localized content or date and time formats and number formats).
Now, Locale could be set in multiple places. OS usually has multiple settings, for example keyboard layout, formatting preferences, language preferences, code pages (for non-Unicode programs), etc.
Apart from that, web browsers usually allow you to choose your own preferences (Safari is an exception here). These preferences are send along with each request to the web server, via HTTP Accept-Language header. This is something you should somehow read on the server side (which unfortunately means some server-side code in PHP, C#, Java, whatever) and maybe passed to your client-side script.
I must state here that something like navigator.language is not the way to go for two reasons:
This is not cross-browser compatible, that is other web browsers would require different code. That is if they allow reading this information in the first place.
This setting usually refers to web browser's language (the language web browser's user interface is translated to) and has nothing to do with actual user's preference.
So to answer your question: no, this check will not suffice.
That is mostly an OS/browser setting, controlling e.g. language of the OS, date/time format, decimal point/comma, etc. - in other words, settings that vary in different locales of the world. This has no direct correlation with font/character support.
As far as I know, ja-jp is the only standardized Japanese locale/language combination; but as #Siku-siku.com suggested in the comments "(based on real testings using Japanese version of IE/WinXP) also check for ja because that's one of the available options in the IE language setting."

Categories

Resources