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."
Related
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
Today, the 11th of September, 2017, JavaScript's toLocaleDateString() method is outputting 9/11/2017 for me. But I am in the UK, so the formatting is wrong in this case. The MDN Web Docs tell me that this method returns "a formatted string in the default locale".
So, where/how is JavaScript detecting my default locale? Where is this set, or what does it depend on, and (how) can I change it?
Edited to add: I'm using Firefox 55.0.3 on Windows 10 if that makes any difference.
To summarize shortly, detecting the current locale is implementation dependent and may differ from environment to environment. Your default language may also depend on the installer you've used to install your browser.
The not so short version:
Following the ECMAScript spec, conforming browsers (and other environments, such as Node.js) should implement localization following the ECMAScript Internationalization API (ECMA-402), which only outlines the following for getting the default locale:
The DefaultLocale abstract operation returns a String value representing the [...] language tag for the host environment’s current locale.
This means that getting the default locale is implementation dependent and can differ from browser to browser. This is intentional, as it allows browser vendors to keep their current, differing implementations to stay conforming without much fuss.
While it's true that it would be nice to have this standardized as well, it's more beneficial to get everyone on board for a broad spec first and then work out the little kinks later.
Most modern browsers allow you to change your current default locale in their preferences (Chrome shown):
I checked JavaScript date and time formatting in Chrome, Firefox, and Edge on my Windows 10 PC, with interesting results. My language tag should be "en-NZ', and I thought the locale behavior would follow accordingly.
Of the three browsers, only Edge had picked up the correct language tag by default, presumably from the OS, while Chrome and Firefox both defaulted to en-US. Edge also defaults to using the correct locale settings.
It seems I can influence the locale in Chrome by changing language in Advanced Settings to en-NZ, but the default locale behavior I get is as for en-GB. Firefox doesn't seem to care what my preferred language is for locale behavior, which depends only on the language "used to display menus, messages, and notifications from Firefox". The only English language options for that are US, Canada, or UK.
In Chrome and Firefox the only way to ensure the correct locale behavior seems to be to explicitly specify it in the JavaScript code.
You can pass locale as parameter. See the example below:
var date = new Date().toLocaleDateString("en-GB");
I've been trying to get momentjs to correctly detect the browser language and localise the time displays. (as per Locale detection with Moment.js)
But I have been having a bit of trouble getting the correct locale out of chrome.
It seems like chrome has the correct list of languages configured for my browser in navigator.languages but navigator.language is returning something completely different.
I'm guessing either I have chrome setup wrong (which doesn't seem likely given the correct languages are in navigator.languages) or the method for selecting the browser language isn't quite right?
Is there a different way I should be using to get the language other than window.navigator.userLanguage || window.navigator.language ?
The W3C draft states that:
navigator.language is the user's "preferred language" or a "plausible language". This is not necessarily the same as navigator.languages[0].
navigator.languages lists the "user's preferred languages, with the most preferred language first". It is expected to have the same value as the Accept-Language header.
By the looks of quickly experimenting in a VM, navigator.languages[0] leads to the most accurate results, navigator.language being the language of the OS.
It looks as if the "en-GB" you are getting refers to the language of the user, whereas the "en-NZ" refers to the user locale (number & date/time formatting).
Since this is still a draft, the results you get may change with implementation.
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.
I want to create a javascript file for multilingual functionality
i.e. display the error message in the correct language.
If I have allot of labels for a page, error messages, etc., what is a smart way of making this so the actual output on the page isn't huge?
i guess the best way is to somehow output the labels that I need ONLY?
lang.getkey('username');
will output the correct label, depending on the language.
Language detection is usually done server-side by checking the Accept-Language HTTP header that is sent.
Browsers have limited, non-standardized means for identifying a user's language (with the exception of IE running on Windows). With IE on Windows, you can access navigator.userLanguage or navigator.systemLanguage, which will return the operating system's RFC #4646 language-COUNTRY code. Other browsers (Opera, Safari, Chrome, Firefox) provide navigator.language, which is in the same format with the exception of Opera which returns the language only. In many cases this might be good enough, but it's still recommended to use a server solution.
I achieved something like this a while ago by separating the strings into different lang.js files and added the script to the document using document.write(). The function would simply fetch the string from an array defined in that lang.js file. A basic example might be:
// Get the language-COUNTRY code, and strip it to the language part only
var lang = (navigator.language || navigator.userLanguage).substring(0,2);
var file = "lang/" + lang + ".js";
document.write('<script src="'+lang+'" type="text/javascript"><\/script>');
This would ensure that only the strings for the necessary language were loaded, although I haven't included a fallback method here, you would need one for defaulting to a language when an unsupported one was detected. You could do this by having a list of supported languages in an array, check to see if lang exists and if it doesn't, write a default script src instead.
Not to sound like a broken record, but you should probably be determining languages and including files server side, not client side.