Настройка локали в браузере
У меня есть веб-сайт (Flash), локализованный на дюжину языков, и я хочу автоматически определить значение по умолчанию в зависимости от настроек браузера пользователя, чтобы минимизировать количество шагов для доступа к контенту.
К вашему сведению, я не могу использовать серверные сценарии из-за ограничений прокси, поэтому я думаю, что для решения проблемы подойдет JavaScript или ActionScript.
Вопросы:
Какой лучший способ «угадать» локаль пользователя?
Существуют ли какие-либо простые классы / функции, которые могут мне помочь (без сложных пакетов локализации)? Специально, чтобы разбить все возможные языки на меньшее количество (у меня есть переводы) умным способом.
До какой степени я могу доверять такому решению?
Любые другие обходные пути или предложения?
К сожалению, этот заголовок недоступен для чтения внутри JavaScript; все, что вы получите, это то navigator.language , что говорит вам, какая локализованная версия веб-браузера была установлена. Это не обязательно то же самое, что предпочтительный язык (и) пользователя. В IE вместо этого вы получаете systemLanguage (язык установленной ОС), browserLanguage (так же, как language ) и userLanguage (настраиваемый пользователем регион ОС), которые все также бесполезны.
Если бы мне пришлось выбирать между этими свойствами, я бы userLanguage сначала понюхал , а затем вернулся к ( language и только после этого (если они не соответствуют ни одному из доступных языков) взгляду browserLanguage и, наконец, systemLanguage .
Если вы можете поместить серверный скрипт где-нибудь еще в сети, который просто читает заголовок Accept-Language и выплевывает его обратно в виде файла JavaScript со значением заголовка в строке, например:
тогда вы можете включить , указывающий на эту внешнюю службу в HTML, и использовать JavaScript для анализа заголовка языка. Однако я не знаю ни одного существующего библиотечного кода для этого, поскольку синтаксический анализ Accept-Language почти всегда выполняется на стороне сервера.
От MDN developer.mozilla.org/en-US/docs/Web/API/NavigatorLanguage/… "HTTP-заголовок Accept-Language в каждом HTTP-запросе от браузера пользователя использует одно и то же значение для свойства navigator.languages, за исключением дополнительного Поле qvalues (значения качества) (например, en-US; q = 0,8). "
Обновление: теперь (2020) существует экспериментальная функция, поддерживаемая всеми современными браузерами, которая возвращает массив языковых предпочтений: navigator.languages //["en-US", "zh-CN", "ja-JP"] это должно работать как минимум на 95% браузеров в 2020 году.
В Chrome и Firefox 32+ файл navigator.languages содержит массив локалей в порядке предпочтения пользователя и является более точным, чем navigator.language, однако, чтобы сделать его обратно совместимым (Tested Chrome / IE / Firefox / Safari), затем используйте этот:
В данной статье я хочу рассказать вам об Internationalization API — интерфейсе, предоставляемом браузером, позволяющем выполнять интернационализацию и локализацию веб-приложений.
Статья состоит из 2 частей: теоретической и практической. В теоретической части мы кратко рассмотрим возможности, предоставляемые Internationalization API . В практической — создадим пример локализованного приложения с помощью разработанной мной утилиты.
Теория
Internationalization API предоставляет следующие возможности:
- локализованное (далее предполагается) сравнение строк
- форматирование чисел, включая валюту, различные единицы измерения и проценты
- форматирование даты и времени, включая относительные периоды, такие как завтра, вчера, через неделю и т.д.
- форматирование названий языков, регионов, скриптов и валют
- форматирование списков с соединительным союзом И или разделительным союзом ИЛИ
- "плюрализация" — перевод во множественное число
- преобразование регистра
Несмотря на то, что в JavaScript существуют такие методы для локализации как:
их зачастую оказывается недостаточно.
Кроме того, поведение данных методов определяется конкретной реализацией ECMAScript , т.е. браузером.
Функционал, определенный в Internationalization API , инкапсулирован в объекте Intl . Данный объект не имеет внутреннего метода [[Construct]] , поэтому не может вызываться как конструктор с помощью ключевого слова new . Он также не имеет внутреннего метода [[Call]] , поэтому не может вызываться как функция.
Intl включает в себя следующие интерфейсы-конструкторы:
- Collator — сравнение строк
- DateTimeFormat — форматирование даты и времени
- DisplayNames — форматирование названий языков, регионов и т.д. на других языках
- ListFormat — форматирование списков
- Locale — определение локали
- NumberFormat — форматирование чисел
- PluralRules — плюрализация
- RelativeTimeFormat — форматирование относительных периодов времени
Пример получения текущей даты и времени в дефолтной локали
Locale
Конструктор Locale используется для создания экземпляров идентификаторов локали. Первым обязательным аргументом, передаваемым Locale , является локаль, которая может состоять из следующего:
- код языка
- код диалекта
- код региона или страны
- один или несколько уникальных вариантных подтегов (subtags)
- одна или несколько последовательностей из расширения BCP 47
- последовательность из расширения для частного использования
В большинстве случаев достаточно указать код языка или код языка и код страны через дефис:
Вторым опциональным аргументом Locale является объект с настройками:
Локаль может быть строкой или объектом. Пустой массив означает использование текущей локали пользователя:
DateTimeFormat
Конструктор DateTimeFormat используется для форматирования даты и времени. Он принимает локаль и объект с настройками.
Свойство | Описание |
---|---|
timeZone | часовой пояс: UTC , America/New_York , Europe/Paris и т.д. |
calendar | календарь: chinese , gregory , hebrew , indian , islamic и т.д. |
numberingSystem | система счисления: arab , beng , fullwide , latin и т.д. |
localeMatcher | алгоритм для поиска совпадений: lookup , best fit |
formatMatcher | алгоритм для форматирования: basic , best fit |
hour12 | если имеет значение true , используется 12-часовой формат |
hourCycle | часовой формат: h11 , h12 , h23 , h24 |
dateStyle | стиль форматирования даты: full , long , medium , short |
weekday | день недели: long , short , narrow |
day | день месяца: numeric , 2-digit |
month | месяц: numeric , 2-digit , long , short , narrow |
year | год: numeric , 2-digit |
era | эпоха: long , short , narrow |
timeStyle | стиль форматирования времени: full , long , medium , short |
hour | часы: numeric , 2-digit |
minute | минуты: numeric , 2-digit |
second | секунды: numeric , 2-digit |
dayPeriod | часть дня (утро, вечер и т.п.): narrow , short , long |
timeZoneName | название часового пояса (UTC, PTC): long , short |
Настройки localeMatcher и formatMatcher могут передаваться любому конструктору, предоставляемому Intl , но используются редко.
По умолчанию new Intl.DateTimeFormat().format() возвращает текущую дату в кратком виде ( dateStyle: short ).
- formatToParts() — возвращает массив объектов, содержащих форматированную дату в виде пар ключ/значение ( < type: 'weekday', value: 'Monday' >)
- formatRange(startDate, endDate) — возвращает диапазон, например, 01/10/2022, 10:00 AM - 12:00 PM
- formatRangeToParts()
- resolveOptions() — возвращает объект со свойствами, значениями которых являются вычисленные настройки форматирования для локали, даты и времени
RelativeTimeFormat
Конструктор RelativeTimeFormat используется для локализации относительного времени, например, вчера, завтра, на следующей неделе, в прошлом месяце и т.д. Данный метод принимает локаль и объект с настройками.
Положительное число в amount означает будущее, отрицательное — прошлое.
Свойство | Описание |
---|---|
numeric | always — "1 день назад" (дефолтное значение), auto — "вчера" |
style | long (дефолтное значение), short , narrow |
В данном случае метод format в качестве аргументов принимает число и единицу времени.
NumberFormat
Конструктор NumberFormat используется для форматирования чисел, валюты, процентов и единиц измерения, таких как длина, температура и др. Данный метод принимает локаль и объект с настройками.
Свойство | Описание |
---|---|
style | вид единиц: decimal — число с плавающей точкой, currency — валюта, percent — проценты, unit — единицы измерения. От этой настройки зависят другие |
notation | стиль форматирования: standard , scientific , engineering , compact |
numberingSystem | система счисления: arab , beng , deva , fullwide , latn и др. |
minimumIntegerDigits | минимальное количество цифр целой части числа (от 1 до 21; по умолчанию 1) |
minimumFractionDigits | минимальное количество цифр после запятой (от 0 до 20; по умолчанию 0) |
maximumFractionDigits | максимальное количество цифр после запятой (от 0 до 20; по умолчанию наибольшее значение из minimumFractionDigits и 3) |
minimumSignificantDigits | минимальное количество значащих цифр (от 1 до 21; по умолчанию 1) |
maximumSignificantDigits | минимальное количество значащих цифр (от 1 до 21; по умолчанию minimumSignificantDigits) |
signDisplay | отображение символов +/- : auto , never , always , exceptZero |
useGrouping | если имеет значение false , разделители тысяч будут игнорироваться |
compactDisplay | форматирование при использовании нотации compact |
currency | код валюты при использовании стиля currency : USD , EUR , RUB и т.д. |
currencyDisplay | отображение символа/названия валюты при использовании стиля currency : symbol , narrowSymbol , code , name |
currencySign | форматирование отрицательных значений при использовании стиля currency : standard , accounting |
unit | вид единицы измерения: centimeter , meter , minute , hour , byte и т.д. |
unitDisplay | формат отображения единицы измерения: long , short , narrow |
DisplayNames
Конструктор DisplayNames используется для форматирования названий языков, диалектов, регионов и валют на другом языке. Данный метод принимает локаль и объект с настройками.
Настройки
Свойство | Описание |
---|---|
type | тип названия: language , region , script , currency |
style | стиль форматирования: long , short , narrow |
fallback | резерв: code , none |
Обратите внимание: настройка type является обязательной. При этом хорошо поддерживается только type со значением language .
Примеры
ListFormat
Конструктор ListFormat используется для форматирования списков путем подстановки соединительного союза И или разделительного союза ИЛИ . Данный метод принимает локаль и объект с настройками.
Свойство | Описание |
---|---|
type | формат вывода: conjunction (и; дефолтное значение), disjunction (или), unit (нет) |
style | стиль форматирования: long , short , narrow |
Collator
Конструктор Collator используется для сравнения строк с учетом локали. Данный метод принимает локаль и объект с настройками.
Свойство | Описание |
---|---|
usage | sort — сортировка (дефолтное значение) или search — поиск |
sensitivity | чувствительность: base , accent , case , variant |
collation | сопоставление вариантов для нескольких языков |
numeric | true означает сравнение чисел |
ignorePunctuation | true означает игнорирование пунктуации |
caseFirst | upper — сначала идут строки, начинающиеся с большой буквы, lower — сначала идут строки, начинающиеся с маленькой буквы |
- 0 — строки равны
- -1 — первая строка "меньше" второй
- 1 — первая строка "больше" второй
PluralRules
Конструктор PluralRules используется для плюрализации (перевода во множественное число). Данный метод принимает локаль и объект с настройками.
Свойство | Описание |
---|---|
type | cardinal — количество элементов (дефолтное значение), ordinal — порядок элемента (первый, второй, третий и т.д.) |
В настоящее время поддерживается только локаль en-US .
Практика
В данном разделе мы создадим небольшое локализованное приложение с помощью разработанной мной утилиты easy-intl .
Принцип работы утилиты подробно описан в документации. Многие вещи были рассмотрены в теоретической части. Поэтому, с вашего позволения, здесь я опишу только ключевые моменты.
Создаем директорию для проекта и устанавливаем easy-intl :
Структура проекта будет следующей:
Как сказал бы Ватсон, все ЭЛЕМЕНТАРНО.
В стилях у нас не будет ничего интересного, поэтому
Что касается разметки, то в процессе локализации easy-intl ориентируется на следующие атрибуты:
- data-intl_type — тип содержимого для локализации. Возможные значения:
- date — дата или время, или дата и время
- relative — относительный период времени
- number — число, включая валюту, проценты и единицы измерения
- names — название языка, региона, валюты и т.д.
- list — список
- compare — строки для сравнения
- plural — строка для перевода во множественное число
- custom — строка, которая используется в качестве ключа словаря для кастомной локализации
Вот как будет выглядеть наша разметка:
Здесь у нас имеется следующее:
- два элемента с атрибутом data-intl_root (с идентификаторами global_root и local_root )
- переключатель локали (с идентификатором locale ) — по умолчанию easy-intl выполняет автоматическую локализацию при создании экземпляра и изменении локали. Автоматическую локализацию можно отключить, передав в конструктор EasyIntl настройку autorun со значением false
- разделы с датой и временем (один в global_root и еще один в local_root ), относительным временем, числами (число, валюта и единица измерения), названием языка (язык и валюта) и списком
- все заголовки имеют атрибут data-intl_custom — перевод заголовков содержится в словаре
- некоторые элементы имеют атрибут data-intl_map
- настройки могут содержать символы '" <>` и разделяться запятой или точкой запятой. Ключ и значение должны разделяться двоеточием
- список может содержать символы '" []` и разделяться запятой или точкой запятой
Определим некоторые настройки для глобальной локализации в intl/options.js (некоторые настройки являются дефолтными, но в данном случае большого значения это не имеет):
Определим словарь для заголовков в intl/dictionary.js для русского и американского английского языков:
Переходим к основному скрипту ( script.js ). Импортируем EasyIntl , настройки и словарь:
Создаем экземпляр EasyIntl , передавая в конструктор локаль en-US , селектор корневого элемента для глобальной локализации, словарь и настройки (обратите внимание, что объект настроек должен быть распакован):
Поскольку мы не определяем настройку autorun со значением false , создание экземпляра EasyIntl приводит к автоматической локализации приложения (за исключением содержимого дочерних элементов local_root ).
Прелесть easy-intl (и, конечно, самого Intl ) заключается в том, что дефолтная локаль пользователя определяется автоматически. Это означает, что для локализации приложения на языке пользователя достаточно создать экземпляр EasyIntl (разумеется, при условии добавления к необходимым элементам атрибутов data-intl_type и data-intl_value ). Поскольку для всех вспомогательных функций, используемых easy-intl , включая, корневой элемент, определены дефолтные настройки (многие дефолтные настройки определяются Intl ), для локализации приложения достаточно выполнить:
Посмотрим на локализуемые элементы, карту локализации, в целом, и локализацию основного заголовка, в частности:
Выполним ручную локализацию содержимого дочерних элементов local_root :
Получаем ссылку на переключатель локали и обрабатываем ее изменение:
Приоритет настроек следующий (от минимального к максимальному):
- new EasyIntl(options)
- intl.localize(options)
- data-intl_options="options"
Получаем вполне работоспособное локализованное приложение:
Пожалуй, это все, чем я хотел поделиться с вами в данной статье.
Что касается планов по дальнейшей разработке утилиты, то, кроме небольших доработок, связанных с преобразованием строковых значений атрибутов data-intl_value в пригодный для Intl формат, в них входит добавление типов для TypeScript и, возможно, адаптация утилиты под React в форме хука или связки провайдера и хука.
Как видите, Internationalization API предоставляет довольно интересные возможности, которые при правильном использовании могут существенно облегчить процесс интернационализации и локализации веб-приложений.
Вместе с тем надо понимать, что Internationalization API не предназначен для перевода (его задачи четко обозначены в спецификации), поэтому многие вещи придется делать вручную или прибегать к помощи таких интерфейсов, как Google Cloud Translation API или API Переводчика Яндекса .
Также важно учитывать, что Internationalization API является относительно новым интерфейсом, некоторые его возможности еще находятся в процессе реализации браузерами, и, вероятно, в будущем появятся новые "фичи".
У меня есть веб-сайт (Flash), локализованный на дюжину языков, и я хочу автоматически определить значение по умолчанию в зависимости от настроек браузера пользователя, чтобы минимизировать количество шагов для доступа к контенту.
К вашему сведению, я не могу использовать серверные сценарии из-за ограничений прокси-серверов, поэтому я думаю, что для решения проблемы подойдет JavaScript или ActionScript.
Какой лучший способ «угадать» локаль пользователя?
Существуют ли какие-либо простые классы / функции, которые могут мне помочь (без сложных пакетов локализации)? Специально, чтобы разбить все возможные языки на меньшее количество (у меня есть переводы) умным способом.
До какой степени я могу доверять такому решению?
Любые другие обходные пути или предложения?
К сожалению, этот заголовок недоступен для чтения внутри JavaScript; все, что вы получаете, это navigator.language , в котором рассказывается, что локализовано версия веб-браузера была установлена. Это не обязательно то же самое, что предпочтительный язык (и) пользователя. В IE вместо этого вы получаете systemLanguage (язык установленной ОС), browserLanguage (такой же, как language ) и userLanguage (настраиваемая пользователем область ОС), которые все также бесполезны.
Если бы мне пришлось выбирать между этими свойствами, я бы сначала обнаружил userLanguage , возвращаясь к language и только после этого (если они не соответствуют ни одному из доступных языков), глядя на > и наконец systemLanguage .
Если вы можете поместить серверный скрипт где-нибудь еще в сети, который просто читает заголовок Accept-Language и выплевывает его обратно в виде файла JavaScript со значением заголовка в строке, например:
Затем вы можете включить
Вы также можете попытаться получить язык из документа, который может быть вашим первым портом вызова, а затем использовать другие средства, так как часто люди хотят, чтобы их язык JS соответствовал языку документа.
Никакой реальный источник не обязательно на 100% надежен, так как люди могут просто говорить не на том языке.
Существуют библиотеки определения языка, которые могут позволить вам определить язык по содержанию.
В этой статье предлагаются следующие свойства браузера навигатор объект:
Сверните их в функцию javascript, и в большинстве случаев вы сможете угадать правильный язык. Обязательно снижайте грациозность, поэтому используйте div, содержащий ваши ссылки для выбора языка, чтобы, если нет javascript или метод не работал, пользователь все равно мог принять решение. Если это работает, просто скройте div.
Единственная проблема, связанная с выполнением этого на стороне клиента, заключается в том, что вы либо обслуживаете все языки для клиента, либо вам нужно подождать, пока сценарий запустится, и определить язык, прежде чем запрашивать правильную версию. Возможно, использование самой популярной языковой версии по умолчанию будет раздражать наименьшее количество людей.
Изменить: я бы предложил предложение куки Ивана, но убедитесь, что пользователь всегда может изменить язык позже; не все предпочитают язык, по умолчанию используемый их браузером.
Объединяя несколько способов, которые браузеры используют для хранения языка пользователя, вы получаете эту функцию:
А вот и сексуальный однострочник .
Существует разница между предпочтительными языками пользователя и языком системы / браузера.
Пользователь может настроить предпочтительные языки в браузере, и они будут использоваться для navigator.language(s) и использоваться при запросе ресурсов с сервера для запроса контента в соответствии со списком языковых приоритетов.
Тем не менее, локаль браузера решит, как отобразить число, дату, время и валюту. Этот параметр, вероятно, является языком с самым высоким рейтингом, но нет никаких гарантий. В Mac и Linux локаль определяется системой независимо от языковых предпочтений пользователя. На Windows это может быть выбран среди языков в списке предпочтений на Chrome.
Чтобы правильно извлечь этот язык, я нашел только один способ:
( Intl.NumberFormat().resolvedOptions().locale тоже работает)
Это создаст новый экземпляр NumberFormat для локали по умолчанию, а затем снова прочитает локаль этих разрешенных опций.
Вы сказали, что на вашем сайте есть Flash, а затем, в качестве другого варианта, вы можете получить язык операционной системы с помощью flash.system.Capabilities.language - см. Как определить язык ОС в браузере, чтобы угадать языковой стандарт операционной системы.
Допустим, у меня есть дата, которую я могу представить в культурно-инвариантном формате (ISO 8601).
Я выберу 6 июля 2009 года, 15:54 по времени UTC в Париже, около 5:54 вечера по местному времени в Париже, с учетом летнего времени.
Хорошо . есть ли скрытая жемчужина разметки, которая скажет браузеру преобразовать эту строку в локализованную версию?
Наиболее близким решением является использование Javascript Date.prototype.toLocaleString (). Это, безусловно, делает хорошую работу, но это может быть медленно повторять много дат, и это зависит от Javascript.
Существует ли какая-либо HTML, CSS, XSLT или иная семантическая разметка, которую браузер распознает и автоматически отобразит правильную локализованную строку?
В настоящее время я использую метод, который заменяет текст элемента HTML локализованной строкой:
Используя Javascript (с jQuery):
С практической точки зрения, он заставляет текст «мигать» к новому значению при запуске Javascript, и мне это не нравится.
С принципиальной точки зрения, я не понимаю, почему мы должны это делать - браузер должен иметь возможность локализовать стандартные вещи, такие как валюта, даты, числа, при условии, что мы помечаем их как таковые.
Следующий вопрос: почему браузеры / Интернет не имеют такой простой функции - взять стандартный элемент данных и отформатировать его в соответствии с настройками клиента?
Я использую toLocaleString () на своем сайте, и у меня никогда не было проблем с его скоростью. Как вы получаете дату сервера в объект Date? Синтаксический ?
Если у них включен JS, я использую jQuery, чтобы получить эти узлы, получить значение комментария, а затем:
Если у них не включен JS, они могут свободно выполнять преобразование в своей голове.
Если вы пытаетесь проанализировать время ISO, это может быть причиной вашей медлительности. Кроме того, сколько дат мы говорим?
Мне понравился трюк, опубликованный в комментарии выше, но он звучит как головная боль QA, так как вы можете иметь дело с большим количеством клиентов, которые используют временные метки совершенно по-разному.
Наиболее эффективное решение, которое я видел, - просто предоставить панель, позволяющую пользователям выбирать, какой формат времени им нравится. Некоторые пользователи даже **** задыхаются, как форматы ISO. Затем вы делаете преобразование формата времени на стороне сервера. Если язык вашего приложения не имеет подходящего сопоставления локали и часового пояса, проверьте базу данных. Многие базы данных также предоставляют настраиваемое форматирование часового пояса на основе локали.
В Dojo есть несколько неплохих локализаций для дат и валют. Использование этого метода также позволяет вам выбирать различные форматы (например: короткая дата против длинной даты) и форсировать локали.
К сожалению, нет.
HTML и CSS строго используются для представления, поэтому нет никаких «умов», встроенных для изменения способа отображения вещей.
Поскольку этот ответ по-прежнему всплывает в Google, я сообщаю, что теперь это можно сделать с помощью локального ввода только для даты и времени (см. Ниже), а затем вы можете оформить ввод так, как вы хотите:
Это невозможно сделать с помощью HTML, у него нет смарт-тегов, которые могут принимать любые подобные решения. Это строго наглядно. Я действительно задаюсь вопросом, хотя, если HTML5, возможно, имеет тег для чего-то вроде этого .
В любом случае, как я вижу, у вас есть 3 варианта:
Попробуйте использовать геолокацию. То есть ваш серверный скрипт запускает запрос к одной из многочисленных служб геолокации, когда пользователь посещает первую страницу, чтобы попытаться угадать, где находится пользователь. Недостатком этого является то, что это будет неправильно примерно в 10% случаев, так что это не намного лучше, чем доля рынка, которую Javascript собирается получить . (в общем, не очень хороший метод. .. )
Спросите пользователя! Вы увидите, что большинство веб-сайтов, которые хотят показывать для вас индивидуальный опыт, будут спрашивать вас об этом, потому что это просто невозможно узнать. В качестве альтернативного варианта вы можете обернуть вопрос тегами , чтобы задать только те из них, у которых отключен Javascript, и предлагать опыт работы с Javascript тем, у кого он есть.
I have a website (Flash) localized into a dozen of languages and I want to auto-define a default value depending on the user's browser settings in order to minimize the steps to access the content.
FYI, I cannot use server scripts due to proxy restrictions, so I guess JavaScript or ActionScript would be appropriate to solve the problem.
Questions:
What would be the best method to 'guess' the user's locale?
Are there any existing simple classes/functions that could help me out (no complex localization bundles)? Especially to reduce all possible languages to a smaller number (the translations I have) in a smart way.
To which point can I trust such a solution?
Any other workarounds or suggestions?
THe only way a browser can share meta data about its user and the request is via URLs and headers. Grab Firefox and take a peek at the headers being sent when a request is made. Its quite interesting to examine typical request and response headers.
10 Answers 10
Unfortunately this header is not available for reading inside JavaScript; all you get is navigator.language , which tells you what localised version of the web browser was installed. This is not necessarily the same thing as the user's preferred language(s). On IE you instead get systemLanguage (OS installed language), browserLanguage (same as language ) and userLanguage (user configured OS region), which are all similarly unhelpful.
If I had to choose between those properties, I'd sniff for userLanguage first, falling back to language and only after that (if those didn't match any available language) looking at browserLanguage and finally systemLanguage .
If you can put a server-side script somewhere else on the net that simply reads the Accept-Language header and spits it back out as a JavaScript file with the header value in the string, eg.:
then you could include a pointing at that external service in the HTML, and use JavaScript to parse the language header. I don't know of any existing library code to do this, though, since Accept-Language parsing is almost always done on the server side.
Читайте также: