JavaScript for detecting browser language preference

609,626

Solution 1

I think the main problem here is that the browser settings don't actually affect the navigator.language property that is obtained via javascript.

What they do affect is the HTTP 'Accept-Language' header, but it appears this value is not available through javascript at all. (Probably why @anddoutoi states he can't find a reference for it that doesn't involve server side.)

I have coded a workaround: I've knocked up a google app engine script at http://ajaxhttpheaders.appspot.com that will return you the HTTP request headers via JSONP.

(Note: this is a hack only to be used if you do not have a back end available that can do this for you. In general you should not be making calls to third party hosted javascript files in your pages unless you have a very high level of trust in the host.)

I intend to leave it there in perpetuity so feel free to use it in your code.

Here's some example code (in jQuery) for how you might use it

$.ajax({ 
    url: "http://ajaxhttpheaders.appspot.com", 
    dataType: 'jsonp', 
    success: function(headers) {
        language = headers['Accept-Language'];
        nowDoSomethingWithIt(language);
    }
});

Hope someone finds this useful.

Edit: I have written a small jQuery plugin on github that wraps this functionality: https://github.com/dansingerman/jQuery-Browser-Language

Edit 2: As requested here is the code that is running on AppEngine (super trivial really):

class MainPage(webapp.RequestHandler):
    def get(self):
        headers = self.request.headers
        callback = self.request.get('callback')

        if callback:
          self.response.headers['Content-Type'] = 'application/javascript'
          self.response.out.write(callback + "(")
          self.response.out.write(headers)
          self.response.out.write(")")
        else:
          self.response.headers['Content-Type'] = 'text/plain'
          self.response.out.write("I need a callback=")

application = webapp.WSGIApplication(
                                     [('/', MainPage)],
                                     debug=False)

def main():
    run_wsgi_app(application)

if __name__ == "__main__":
    main()

Edit3: Have open sourced the app engine code here: https://github.com/dansingerman/app-engine-headers

Solution 2

var language = window.navigator.userLanguage || window.navigator.language;
alert(language); //works IE/SAFARI/CHROME/FF

window.navigator.userLanguage is IE only and it's the language set in Windows Control Panel - Regional Options and NOT browser language, but you could suppose that a user using a machine with Window Regional settings set to France is probably a French user.

navigator.language is FireFox and all other browser.

Some language code: 'it' = italy, 'en-US' = english US, etc.


As pointed out by rcoup and The WebMacheter in comments below, this workaround won't let you discriminate among English dialects when users are viewing website in browsers other than IE.

window.navigator.language (Chrome/FF/Safari) returns always browser language and not browser's preferred language, but: "it's pretty common for English speakers (gb, au, nz, etc) to have an en-us version of Firefox/Chrome/Safari." Hence window.navigator.language will still return en-US even if the user preferred language is en-GB.

Solution 3

Update of year 2014.

Now there is a way to get Accept-Languages in Firefox and Chrome using navigator.languages (works in Chrome >= 32 and Firefox >= 32)

Also, navigator.language in Firefox these years reflects most preferred language of content, not language of UI. But since this notion is yet to be supported by other browsers, it is not very useful.

So, to get most preferred content language when possible, and use UI language as fallback:

navigator.languages
    ? navigator.languages[0]
    : (navigator.language || navigator.userLanguage)

Solution 4

I came across this piece of code to detect browser's language in Angular Translate module, which you can find the source here. I slightly modified the code by replacing angular.isArray with Array.isArray to make it independent of Angular library.

var getFirstBrowserLanguage = function () {
    var nav = window.navigator,
    browserLanguagePropertyKeys = ['language', 'browserLanguage', 'systemLanguage', 'userLanguage'],
    i,
    language;

    // support for HTML 5.1 "navigator.languages"
    if (Array.isArray(nav.languages)) {
      for (i = 0; i < nav.languages.length; i++) {
        language = nav.languages[i];
        if (language && language.length) {
          return language;
        }
      }
    }

    // support for other well known properties in browsers
    for (i = 0; i < browserLanguagePropertyKeys.length; i++) {
      language = nav[browserLanguagePropertyKeys[i]];
      if (language && language.length) {
        return language;
      }
    }

    return null;
  };

console.log(getFirstBrowserLanguage());

Solution 5

let lang = window.navigator.languages ? window.navigator.languages[0] : null;
    lang = lang || window.navigator.language || window.navigator.browserLanguage || window.navigator.userLanguage;

let shortLang = lang;
if (shortLang.indexOf('-') !== -1)
    shortLang = shortLang.split('-')[0];

if (shortLang.indexOf('_') !== -1)
    shortLang = shortLang.split('_')[0];

console.log(lang, shortLang);

I only needed the primary component for my needs, but you can easily just use the full string. Works with latest Chrome, Firefox, Safari and IE10+.

Share:
609,626
Abhishek Habbu
Author by

Abhishek Habbu

Learning a lot of stuff

Updated on July 29, 2022

Comments

  • Abhishek Habbu
    Abhishek Habbu almost 2 years

    I have been trying to detect the browser language preference using JavaScript.

    If I set the browser language in IE in Tools>Internet Options>General>Languages, how do I read this value using JavaScript?

    Same problem for Firefox. I'm not able to detect the setting for tools>options>content>languages using navigator.language.

    Using navigator.userLanguage , it detects the setting done thru Start>ControlPanel>RegionalandLanguageOptions>Regional Options tab.

    I have tested with navigator.browserLanguage and navigator.systemLanguage but neither returns the value for the first setting(Tools>InternetOptions>General>Languages)

    I found a link which discusses this in detail, but the question remains unanswered :(

  • Paul McMillan
    Paul McMillan over 14 years
    Keep in mind that this is not a particularly reliable way of serving the "correct" language to the user. Many users will want an alternate option - don't leave them stranded!
  • Mihai Nita
    Mihai Nita over 14 years
    100% agree. Using that info it a best guess. You should allow the user to override it, if you get it wrong. And if there is a possibility that the user returns, you might remember that choice in a cookie. If the site requires authentication, you might have that info in a user profile.
  • DanSingerman
    DanSingerman about 13 years
    @msec I have posted the Python appengine script as requested. Note, if server side is available, this should be pretty simple in any language - this service really only needs to exist for those that don't have (or don't want to have) a server side component.
  • mate64
    mate64 almost 13 years
    hi dan, this question gots 14k views and still counting - maybe you want to release your script on github? regards, msec
  • moey
    moey almost 13 years
    Does navigator.language always return the same value for a particular language (e.g. ja-jp for Japanese)? Or, the value / format varies across browsers / OS?
  • Marco Demaio
    Marco Demaio almost 13 years
    I'm not sure, but you can check for a substring of it, i.e. if(language.indexOf('jp') !== -1) alert('this is japanese')
  • Deckard
    Deckard over 12 years
    Are there other reliable sites that return HTTP request headers like Google? What I'm trying to do is redirecting user to their browswer language settings, and my site is global. So I need to rely on the site globally available and permenant.
  • DanSingerman
    DanSingerman over 12 years
    @deckard ummm - the Internet is global. The appengine script should work fine anywhere on the Internet. However - this is really a hack for devs without a backend available - it should not be used in a 'real' site where you have control of a the back end.
  • Matthew Flaschen
    Matthew Flaschen almost 12 years
    @DanSingerman, interesting script. However,you should use application/javascript for JSONP. Also, this is not technically valid JSONP, since you're using simple quotes, but that shouldn't matter for most purposes.
  • Matthew Flaschen
    Matthew Flaschen almost 12 years
    His plugin uses JSONP, so I don't think this will be compatible.
  • DanSingerman
    DanSingerman almost 12 years
    @MatthewFlaschen you're quite right (In my defence I wrote the script in about 5 minutes 2 years ago.) I'll fix it up when I have a chance.
  • Matthew Flaschen
    Matthew Flaschen almost 12 years
    @DanSingerman, thanks. "simple quotes" should have been "single quotes".
  • thomaux
    thomaux about 11 years
    This is not correct. Calling navigator.language in Chrome will return the language Chrome is displayed in, NOT the user's preferred language (which is the language at the top of the languages list).
  • Marco Demaio
    Marco Demaio about 11 years
    @Anzeo: low are chances of having you site visited by users that speak in one language, but install a browser in another language and later they also set another preferred language. As said by others there is no decent way, my answer is a simple short workaround for a task that usually does not end up into fatal errors. If you have to be absolutely sure of what language the user uses you could always ask him by adding on your site a select list and save its choice into a cookie.
  • thomaux
    thomaux about 11 years
    @MarcoDemaio the OP asks a solution to detect the preference of the user. As Chrome is confusing in these settings, I added the note for future reference. We had the problem recently on our project and had to use the accepted answer. It's to determine the default, because our users can also set their preferred language on the platform. However this setting might be missing, hence the need of a default.
  • jezzarax
    jezzarax over 10 years
    @MarcoDemaio it's pretty common for English speakers (gb, au, nz, etc) to have an en-us version of Firefox/Chrome/Safari. Sometimes en-gb builds exist but they're not popular, and there's certainly none for other en variants.
  • Marco Demaio
    Marco Demaio over 10 years
    @rcoup: sorry, but i don't unsertsand youyr comment. Brits, Aussies, etc. they do all speak in English, and the 'Browser Language Preference' detected by my code will be exactly 'English'.
  • AGamePlayer
    AGamePlayer over 10 years
    Just wondering, what is the different between navigator.language and Accept-Language...
  • Ronny
    Ronny over 10 years
    @AwQiruiGuo the former is usually the browser's UI language, while the latter is an ordered list of languages the user prefers to view content in.
  • Endless
    Endless almost 10 years
    If the third party service should be more safe it should enable CORS ;)
  • Marco Demaio
    Marco Demaio over 9 years
    +1, but where did you read that navigator.browserLanguage is deprecated? It's not written in the links to the MSDN articles in you answer, and I just tested it in IE11 and it works! I don't have IE8 anymore, but I tested it with the IE8 simulator and it works (I know the IE8 simulator is not the best option to test things).
  • anddoutoi
    anddoutoi over 9 years
    Hmm, yeah that seems weird. But I have no clue how I came to that conclusion 5 years ago >.<
  • Austin Thompson
    Austin Thompson over 9 years
    In case it helps anyone else, I had thought this would work in Safari in iOS 8 since I thought it fell in the modern category. I was wrong.
  • Dave
    Dave over 9 years
    An alternative implementation which gets the preferred list of languages, or falls back to the UI language (both as arrays) is: window.navigator.languages || [window.navigator.language || window.navigator.userLanguage]
  • Yaba
    Yaba over 9 years
    Note that this does not work in our 'most-favorite' browser, IE (tested with IE 11).
  • user959690
    user959690 over 9 years
    I think you might be confusing the browser's configured language with the browser's language preference. The first controls the menus, etc on the browser, the later affects the languages that the browser sends in the header to servers asking for those languages in response. So if the server denies your language and resorts to English you'd want to use English, not the browser's language.
  • Justin
    Justin almost 9 years
    His answer works fine in IE (tested 8-11). This should be the accepted answer.
  • Evan Lin
    Evan Lin almost 9 years
    Please note header['Accept-Language'] will get different result base on your browser.
  • Mousey
    Mousey almost 9 years
    a much better cross-browser solution is now available, see @MarcoDemaio's answer, explained here gu.illau.me/posts/…
  • Styx
    Styx over 8 years
    Chrome has window.navigator.languages with array of user preferred languages.
  • Oleksandr
    Oleksandr over 8 years
    Getting language with navigator.languages[0] is bad idea. My system's default language is Russian and navigator.language returns correct lang code "ru". But navigator.languages returns ["en-US", "en", "ru", "uk"]. So getting language with 0 index will give you "en-US" which is incorrect for my system. Please don't use navigator.languages to detect current system's language.
  • Oleksandr
    Oleksandr over 8 years
    And one more thing: navigator.languages represents alphabetically(!) sorted list of languages available for text input in user's system. They are not sorted in order of user's preference.
  • Oleksandr
    Oleksandr over 8 years
    @Styx window.navigator.languages represents alphabetically sorted list of languages available in system for text input. For example: my system's UI and Chrome language is "ru". Also my native languages are "ru" (Russian) and "uk" (Ukrainian), so I have enabled keyboard layouts for these languages (as well as English layout) and Chrome for window.navigator.languages returns ["en-US", "en", "ru", "uk"]. Absolutely different order than my preference and language of my UI.
  • Oleksandr
    Oleksandr over 8 years
    Due to said above, navigator.languages can't be used for any kind of strict detection for user's language preference order. It can be used to obtain generic information about user, not his preferences.
  • Tim Babych
    Tim Babych over 8 years
    @stunpix this is not about system's language, but about in-browser preference. Which might or might not coincide with system's language, depending on which browser was installed. I suggest you take a look at your browser's settings. Firefox: Preferences-Content-Languages, Chrome: Settings (advanced) - Languages
  • Styx
    Styx over 8 years
    @stunpix - maybe Chrome auto add keybord layouts to preferred languages. But you can edit this list and window.navigator.languages has ordered list of languages from preferences: chrome://settings/languages
  • Oleksandr
    Oleksandr over 8 years
    @Styx yep, seems Chrome do this at install time, since any changes in keyboard layout settings later do not change that Chrome setting. Anyway, average user do not change these language settings in browser, so no one should rely on them as on user's languages that are sorted by user preference. That's not true.
  • Oleksandr
    Oleksandr over 8 years
    Another observation with Chrome: for pages with foreign languages browser asks to translate these pages to one of languages defined in settings, but once you click in popped bar "never translate this language" – this language will be added to you language list. So when user is getting annoyed with this translation bar, he can press "never translate" button to just stop it popping. Is this preferred user language in such case? I think no.
  • elQueFaltaba
    elQueFaltaba almost 8 years
    Just run your script, got en-US, on osX El Capitan in Spanish, Chrome set in Spanish, from Barcelona .. The list of allowed languages, and picking up the first of it navigator.languages[0], is not the actual language the user is using.
  • StanE
    StanE over 7 years
    @stunpix No, this is not correct. The order of prefered languages in the array reflects the order set by user: developer.mozilla.org/en-US/docs/Web/API/NavigatorLanguage/…
  • Oleksandr
    Oleksandr over 7 years
    @StanE For some reason this was correct on moment of writing, because I had setup that always returned me sorted array, despite of my settings (I double checked that). Now it's no longer correct and I can't reproduce my previous statement, so it's no longer correct.
  • Oleksandr
    Oleksandr over 7 years
    BTW, navigator.language[s] have unstable behavior across browsers and you should consider this behavior: navigator.language is a browser's UI lang. Chrome automatically sets its UI lang to system's, but FF doesn't do that automatically, so FF's lang could be != system's lang (for example freshly installed Ubuntu has FF with only english UI and other langs must be installed manually). navigator.languages – user's order preference. Chrome changes this list by pushing current system's lang to top of list, but FF still has a static list which should be sorted manually.
  • MrCroft
    MrCroft over 7 years
    All good. I've just checked myself (hate to do that, as you have to restart the browser - at least chrome). 1. navigator.languages - array is in the order you arrange your languages in browser settings (chrome is less intuitive that you can order them, as there is no indicator that you can drag the items in the list; Firefox has move up/down buttons) 2. navigator.language - returns the current language (the one set as default) Of course, I've tried in Chrome and Firefox, not sure about IE and what navigator.userLanguage returns... but I'm not even opening "that" (ie IE) :)))
  • EamonnM
    EamonnM about 7 years
    This seems more reliable than other JS answers, e.g. the check on language.length means it will skip empty items in the languages array (if that even happens?)
  • Jaume Mussons Abad
    Jaume Mussons Abad over 6 years
    To support older browsers when detecting that nav.languages is an array, you should use: Object.prototype.toString.call(nav.languages) === '[object Array]'
  • Patrick Oscity
    Patrick Oscity about 6 years
    I have my OS language (i.e. Chrome's UI language) set to en_US, preferred browser languages set to de, en_US, en_GB, en in that order. navigator.language returns "de" for me, so it seems to be equivalent to navigator.languages[0], at least in more recent versions of Chrome.
  • Tim Babych
    Tim Babych about 6 years
    Yes, can confirm it works now in Chrome 65 on Mac. Interestingly the bug about navigator.language is not closed yet bugs.chromium.org/p/chromium/issues/detail?id=101138
  • Armin Šupuk
    Armin Šupuk almost 6 years
    @anddoutoi It is not available in IE 8, because it was introduced in IE 9, so it's exactly the opposite of what you though.
  • Stefan Steiger
    Stefan Steiger almost 6 years
    To clarify: older browsers = ( IE <=8.0 )
  • EamonnM
    EamonnM almost 6 years
    It works on every browser I've tested - desktop and mobile. Which browser are you having problems with?
  • Jry9972
    Jry9972 almost 6 years
    'browserLanguage', 'systemLanguage and, 'userLanguage' are not properties of chrome and firefox.. but 'language' is present in all browsers.. My bad that I didn't see that it would return first short code which answers the question.. Deleting my previous comment..
  • 9ilsdx 9rvj 0lo
    9ilsdx 9rvj 0lo over 5 years
    It is pretty common for aware non-native-english speakers to install browser in English language to be able to troubleshoot easier, and still prefer another language for non-technical stuff.
  • Vitim.us
    Vitim.us over 5 years
    One liner is const langs = [].concat(navigator.languages, navigator.language, navigator.userLanguage, navigator.browserLanguage, navigator.systemLanguage).filter(Boolean);
  • Null
    Null over 5 years
    EamonnM's edit of this code is probably more optimal, see: stackoverflow.com/a/46514247/9314312
  • MrMesees
    MrMesees about 5 years
    I like this. It's chrome specific, but doesn't electron use chromium under the hood? Does this work for just chrome, or any chromium project
  • warrickh
    warrickh about 5 years
    This is only available from within a Chrome Extension. Its not available in Chrome on a regular web page. I'm not sure about Electron.
  • Marcel Waldvogel
    Marcel Waldvogel about 5 years
    @CodeGust: The link no longer works. Can you find a replacement?
  • CodeGust
    CodeGust about 5 years
  • Berkyjay
    Berkyjay over 4 years
    @thomaux, no longer true. Now navigator.language in Chrome returns the first element of the list navigator.languages. This change makes it now impossible to know for sure the language of the browser's UI.
  • Brent
    Brent about 4 years
    How common is it a problem people have preferences in the order ["en", "en-GB"] but want the preferences the other way around. I thought the languages were meant to be in the order of user preference. if it were ["fr", "en-GB"], wouldn't the preference be for French?
  • EamonnM
    EamonnM about 4 years
    @Brent That's a good point. It would depend on what you need from it. For ["en", "en-GB"] I would want to get "en-GB" as it's a more specific version of the same language. ["fr", "en-GB"] is different as they are different languages, and "fr" could be a better result.
  • Miguel
    Miguel almost 4 years
    The Accept-Language HTTP header in every HTTP request from the user's browser uses the same value for the navigator.languages property except for the extra qvalues (quality values) field (e.g. en-US;q=0.8). from developer.mozilla.org/en-US/docs/Web/API/NavigatorLanguage/…
  • RollingInTheDeep
    RollingInTheDeep over 2 years
    I guess this might be out of date, because changing the language definitely did change the navigation.languages value. Also if you add a new language and move it to the top of the list, then you will get that value when you query on navigation.language. Also note that IE / Edge have been using the same engine as chrome for quite some time now, so it should be enough to use navigator.langauge (primary language) or navigator.languages (array of all languages)
  • RollingInTheDeep
    RollingInTheDeep over 2 years
    developer.mozilla.org/en-US/docs/Web/API/Navigator/languages Clearly states that navigator.language is the first element in the navigator.languages array.
  • Khom Nazid
    Khom Nazid about 2 years
    Is there a non Jquery version of this?