JavaScript для определения языковых предпочтений браузера

Я пытался определить предпочтения языка браузера с помощью JavaScript.

Если я установлю язык браузера в IE в Tools>Internet Options>General>LanguagesКак я могу прочитать это значение с помощью JavaScript?

Та же проблема для Firefox. Я не могу определить настройки для tools>options>content>languages с помощью navigator.language,

С помощью navigator.userLanguage, он обнаруживает настройку, выполненную черезStart>ControlPanel>RegionalandLanguageOptions>Regional Options Вкладка.

Я проверил с navigator.browserLanguage а также navigator.systemLanguage но ни один не возвращает значение для первой настройки (Tools>InternetOptions>General>Languages)

Я нашел ссылку, которая обсуждает это подробно, но вопрос остается без ответа:(

26 ответов

Решение

Я думаю, что основная проблема заключается в том, что настройки браузера на самом деле не влияют на navigator.language свойство, которое получается с помощью JavaScript.

На что они влияют, так это на HTTP-заголовок "Accept-Language", но, похоже, это значение вообще недоступно через javascript. (Вероятно, поэтому @anddoutoi утверждает, что не может найти для него ссылку, которая не затрагивает серверную часть.)

Я кодировал обходной путь: я запустил скрипт механизма приложений Google на http://ajaxhttpheaders.appspot.com/, который вернет вам заголовки HTTP-запроса через JSONP.

(Примечание: этот хак используется только в том случае, если у вас нет доступной серверной части, которая могла бы сделать это за вас. В общем, вам не следует звонить на сторонние размещенные файлы javascript на ваших страницах, если у вас не очень высокий уровень уровень доверия к хозяину.)

Я намерен оставить его там навсегда, поэтому не стесняйтесь использовать его в своем коде.

Вот пример кода (в jQuery) о том, как вы можете его использовать

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

Надеюсь, кто-то найдет это полезным.

Изменить: я написал небольшой плагин JQuery на GitHub, который оборачивает эту функциональность: https://github.com/dansingerman/jQuery-Browser-Language

Редактировать 2: В соответствии с просьбой, вот код, который работает на AppEngine (действительно тривиально):

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: Откройте исходный код движка приложения здесь: https://github.com/dansingerman/app-engine-headers

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

window.navigator.userLanguage только для IE, и это язык, установленный в Панели управления Windows - Региональные параметры, а НЕ язык браузера, но можно предположить, что пользователь, использующий машину с региональными настройками Windows, установленными во Францию, вероятно, является французским пользователем.

navigator.language есть FireFox и все остальные браузеры.

Некоторый код языка: 'it' = италия, 'en-US' = английский США и т. д.


Как отмечено rcoup и The WebMacheter в комментариях ниже, этот обходной путь не позволит вам различать диалекты английского языка, когда пользователи просматривают веб-сайты в браузерах, отличных от IE.

window.navigator.language (Chrome / FF / Safari) всегда возвращает язык браузера, а не предпочитаемый браузером язык, но: "для носителей английского языка (gb, au, nz и т. Д.) Довольно характерно иметь версию Firefox/Chrome/Safari для en-us". следовательно window.navigator.language все еще вернется en-US даже если пользователь предпочитает язык en-GB ,

Обновление 2014 года.

Теперь есть способ получить Accept-Languages ​​в Firefox и Chrome, используя navigator.languages (работает в Chrome >= 32 и Firefox >= 32)

Кроме того, navigator.language в Firefox в эти годы отражает наиболее предпочтительный язык контента, а не язык пользовательского интерфейса. Но поскольку это понятие еще не поддерживается другими браузерами, оно не очень полезно.

Итак, чтобы получить наиболее предпочтительный язык контента, когда это возможно, и использовать язык интерфейса в качестве запасного варианта:

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

Я наткнулся на этот фрагмент кода для определения языка браузера в модуле Angular Translate, который вы можете найти здесь. Я немного изменил код, заменив angular.isArray на Array.isArray, чтобы сделать его независимым от библиотеки Angular.

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());

<script type="text/javascript">
var lang = window.navigator.languages ? window.navigator.languages[0] : null;
    lang = lang || window.navigator.language || window.navigator.browserLanguage || window.navigator.userLanguage;
if (lang.indexOf('-') !== -1)
    lang = lang.split('-')[0];

if (lang.indexOf('_') !== -1)
    lang = lang.split('_')[0];
</script>

Мне нужен только основной компонент для моих нужд, но вы можете легко использовать полную строку. Работает с последними версиями Chrome, Firefox, Safari и IE10+.

var language = navigator.languages && navigator.languages[0] || // Chrome / Firefox
               navigator.language ||   // All browsers
               navigator.userLanguage; // IE <= 10

console.log(language);

Попробуйте шаблон PWA https://github.com/StartPolymer/progressive-web-app-template

navigator.userLanguage для IE

window.navigator.language для Firefox/ Opera/ Safari

Я уже некоторое время использую ответ Хамида, но это в тех случаях, когда массив languages ​​похож на ["en", "en-GB", "en-US", "fr-FR", "fr", "en-ZA"] будет возвращать" en ", когда" en-GB "будет лучшим совпадением.

Мое обновление (ниже) вернет первый длинный форматный код, например, "en-GB", в противном случае он вернет первый короткий код, например, "en", иначе вернет ноль.

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

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

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

        return shortLanguage;
    }

console.log(getFirstBrowserLanguage());

Я только что придумал это. Он сочетает в себе новый синтаксис деструктуризации JS с несколькими стандартными операциями для извлечения языка и локали.

var [lang, locale] = (((navigator.userLanguage || navigator.language).replace('-', '_')).toLowerCase()).split('_');

Надеюсь, это поможет кому-то

Не существует приличного способа получить эту настройку, по крайней мере, не зависящей от браузера.

Но сервер имеет эту информацию, потому что он является частью заголовка HTTP-запроса (поле Accept-Language, см. http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html)

Таким образом, единственный надежный способ - получить ответ с сервера. Вам понадобится что-то, что работает на сервере (например.asp, .jsp, .php, CGI), и эта "вещь" может вернуть эту информацию. Хорошие примеры здесь: http://www.developershome.com/wap/detection/detection.asp?page=readHeader

У меня была та же проблема, и я написал следующую библиотеку только для внешнего интерфейса, которая оборачивает код для нескольких браузеров. Это не так много кода, но приятно не копировать и вставлять один и тот же код на нескольких сайтах.

Получите это: acceptlanguages.js

Используй это:

<script src="acceptedlanguages.js"></script>
<script type="text/javascript">
  console.log('Accepted Languages:  ' + acceptedlanguages.accepted);
</script>

Он всегда возвращает массив, упорядоченный по предпочтениям пользователя. В Safari & IE массив всегда имеет одну длину. В FF и Chrome это может быть несколько языков.

Прежде всего, извините за мой английский. Я хотел бы поделиться своим кодом, потому что он работает, и он отличается от других, которые дают ответы. В этом примере, если вы говорите по-французски (Франция, Бельгия или другой французский язык), вы будете перенаправлены на французскую страницу, в противном случае на английскую страницу, в зависимости от конфигурации браузера:

<script type="text/javascript">
        $(document).ready(function () {
            var userLang = navigator.language || navigator.userLanguage;
            if (userLang.startsWith("fr")) {
                    window.location.href = '../fr/index.html';
                }
            else {
                    window.location.href = '../en/index.html';
                }
            });
    </script>

Я не могу найти ни одной ссылки, в которой говорится, что это возможно без участия сервера.

MSDN на:

Из браузера Язык:

В Microsoft Internet Explorer 4.0 и более ранних версиях свойство browserLanguage отражает язык пользовательского интерфейса установленного браузера. Например, если вы установите японскую версию Windows Internet Explorer в операционной системе на английском языке, browserLanguage будет иметь значение ja.

Однако в Internet Explorer 5 и более поздних версиях свойство browserLanguage отражает язык операционной системы независимо от установленной языковой версии Internet Explorer. Тем не менее, если установлена ​​версия Microsoft Windows 2000 MultiLanguage, свойство browserLanguage указывает язык, установленный в текущих меню и диалоговых окнах операционной системы, как указано в разделе "Региональные параметры панели управления". Например, если вы установите японскую версию Internet Explorer 5 в операционной системе на английском языке (Великобритания), browserLanguage будет en-gb. Если вы установите версию для Windows 2000 MultiLanguage и установите язык меню и диалогов на французский, то browserLanguage будет французским, даже если у вас есть версия Internet Explorer на японском языке.

Примечание. Это свойство не указывает язык или языки, установленные пользователем в "Языковых настройках", расположенных в диалоговом окне "Свойства обозревателя".

Кроме того, это выглядит как browserLanguage устарела, потому что IE8 не перечисляет его

Если вы разрабатываете приложение / расширение Chrome, используйте API chrome.i18n.

chrome.i18n.getAcceptLanguages(function(languages) {
  console.log(languages);
  // ["en-AU", "en", "en-US"]
});

Javascript способ:

var language = window.navigator.userLanguage || window.navigator.language;//returns value like 'en-us'

Если вы используете плагин jQuery.i18n, вы можете использовать:

jQuery.i18n.browserLang();//returns value like '"en-US"'

Если вам нужно только поддерживать определенные современные браузеры, то теперь вы можете использовать:

navigator.languages

который возвращает массив языковых предпочтений пользователя в порядке, указанном пользователем.

На данный момент (сентябрь 2014 г.) это работает для: Chrome (v37), Firefox (v32) и Opera (v24)

Но не на: IE (v11)

Что бы это ни стоило, библиотека Wikimedia Universal Language Selector имеет хуки для этого: https://www.mediawiki.org/wiki/Extension:UniversalLanguageSelector

Смотрите функцию getFrequentLanguageList в ресурсах /js/ext.uls.init.js . Прямая ссылка: https://gerrit.wikimedia.org/r/gitweb?p=mediawiki/extensions/UniversalLanguageSelector.git;a=blob;f=resources/js/ext.uls.init.js;hb=HEAD

Это все еще зависит от сервера или, более конкретно, API MediaWiki. Причина, по которой я показываю это, заключается в том, что он может послужить хорошим примером получения всей полезной информации о языке пользователя: языке браузера, Accept-Language, геолокации (с получением информации о стране / языке из CLDR) и, конечно же, Пользовательские настройки сайта.

У DanSingerman есть очень хорошее решение для этого вопроса.

Единственный надежный источник для языка находится в заголовке HTTP-запроса. Таким образом, вам нужен серверный скрипт для ответа на заголовок запроса или, по крайней мере, Accept-Language поле обратно к вам.

Вот очень простой сервер Node.js, который должен быть совместим с плагином DanSingermans jQuery.

var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end(JSON.stringify(req.headers));
}).listen(80,'0.0.0.0');

В ответе Дана Сингермана есть проблема, заключающаяся в том, что извлеченный заголовок должен использоваться сразу же из-за асинхронного характера jjuery ajax. Однако на его сервере приложений Google я написал следующее, так что заголовок устанавливается как часть первоначальной настройки и может быть использован позднее.

<html>
<head>
<script>

    var bLocale='raw'; // can be used at any other place

    function processHeaders(headers){
        bLocale=headers['Accept-Language'];
        comma=bLocale.indexOf(',');
        if(comma>0) bLocale=bLocale.substring(0, comma);
    }

</script>

<script src="jquery-1.11.0.js"></script>

<script type="application/javascript" src="http://ajaxhttpheaders.appspot.com?callback=processHeaders"></script>

</head>
<body>

<h1 id="bLocale">Should be the browser locale here</h1>

</body>

<script>

    $("#bLocale").text(bLocale);

</script>
</html>

Если вы не хотите полагаться на внешний сервер и у вас есть собственный сервер, вы можете использовать простой сценарий PHP для достижения того же поведения, что и ответ @DanSingerman.

languageDetector.php:

<?php
$lang = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2);
echo json_encode($lang);
?>

И просто измените эти строки из скрипта jQuery:

url: "languageDetector.php",
dataType: 'json',
success: function(language) {
    nowDoSomethingWithIt(language);
}

Для тех, кто ищет решение Java Server

Здесь RestEasy

@GET
@Path("/preference-language")
@Consumes({"application/json", "application/xml"})
@Produces({"application/json", "application/xml"})
public Response getUserLanguagePreference(@Context HttpHeaders headers) {
    return Response.status(200)
            .entity(headers.getAcceptableLanguages().get(0))
            .build();
}

Если у вас есть контроль над бэкэндом и вы используете django, 4-строчная реализация идеи Дэна:

def get_browser_lang(request):
if request.META.has_key('HTTP_ACCEPT_LANGUAGE'):
    return JsonResponse({'response': request.META['HTTP_ACCEPT_LANGUAGE']})
else:
    return JsonResponse({'response': settings.DEFAULT_LANG})

тогда в urls.py:

url(r'^browserlang/$', views.get_browser_lang, name='get_browser_lang'),

и на передней части:

$.get(lg('SERVER') + 'browserlang/', function(data){
    var lang_code = data.response.split(',')[0].split(';')[0].split('-')[0];
});

(Вы должны установить DEFAULT_LANG в settings.py, конечно)

Основываясь на ответе здесь Доступ к заголовкам HTTP веб-страницы в JavaScript, я создал следующий скрипт для получения языка браузера:

var req = new XMLHttpRequest();
req.open('GET', document.location, false);
req.send(null);
var headers = req.getAllResponseHeaders().toLowerCase();
var contentLanguage = headers.match( /^content-language\:(.*)$/gm );
if(contentLanguage[0]) {
    return contentLanguage[0].split(":")[1].trim().toUpperCase();
}

У меня был другой подход, это может помочь кому-то в будущем:

клиент хотел страницу, где вы можете поменять языки. мне нужно было отформатировать числа с помощью этой настройки (не настройки браузера / каких-либо предопределенных настроек)

поэтому я установил начальную настройку в зависимости от настроек конфигурации (i18n)

$clang = $this->Session->read('Config.language');
echo "<script type='text/javascript'>var clang = '$clang'</script>";

позже в скрипте я использовал функцию, чтобы определить, какое форматирование мне нужно

function getLangsettings(){
  if(typeof clang === 'undefined') clang = navigator.language;
  //console.log(clang);
  switch(clang){
    case 'de':
    case 'de-de':
        return {precision : 2, thousand : ".", decimal : ","}
    case 'en':
    case 'en-gb':
    default:
        return {precision : 2, thousand : ",", decimal : "."}
  }
}

поэтому я использовал заданный язык страницы и в качестве запасного варианта я использовал настройки браузера.

что должно быть полезно для целей тестирования.

в зависимости от ваших клиентов вам могут не понадобиться эти настройки.

Если вы используете ASP .NET MVC и хотите получить заголовок Accepted-Languages из JavaScript, то вот пример обходного пути, который не включает никаких асинхронных запросов.

В вашем файле.cshtml надежно сохраните заголовок в атрибуте данных div:

<div data-languages="@Json.Encode(HttpContext.Current.Request.UserLanguages)"></div>

Тогда ваш код JavaScript может получить доступ к информации, например, с помощью JQuery:

<script type="text/javascript">
$('[data-languages]').each(function () {
    var languages = $(this).data("languages");
    for (var i = 0; i < languages.length; i++) {
        var regex = /[-;]/;
        console.log(languages[i].split(regex)[0]);
    }
});
</script>

Конечно, вы можете использовать аналогичный подход с другими серверными технологиями, как уже упоминали другие.

У меня есть взлом, который, я думаю, использует очень мало кода и довольно надежен.

Поместите файлы вашего сайта в подкаталог. SSL на ваш сервер и создайте символические ссылки на тот подкаталог, где хранятся ваши файлы с указанием ваших языков.

Что-то вроде этого:

ln -s /var/www/yourhtml /var/www/en
ln -s /var/www/yourhtml /var/www/sp
ln -s /var/www/yourhtml /var/www/it

Используйте свой веб-сервер, чтобы прочитать HTTP_ACCEPT_LANGUAGE и перенаправить в эти "разные подкаталоги" в соответствии с языковым значением, которое он предоставляет.

Теперь вы можете использовать window.location.href в Javascript, чтобы получить ваш URL и использовать его в условных выражениях, чтобы надежно идентифицировать предпочтительный язык.

url_string = window.location.href;
if (url_string = "http://yoursite.com/it/index.html") {
    document.getElementById("page-wrapper").className = "italian";
}
Другие вопросы по тегам