Настройка локали в браузере

Обновлено: 07.07.2024

Общая проблема строк, дат, чисел в JavaScript – они «не в курсе» языка и особенностей стран, где находится посетитель.

Строки При сравнении сравниваются коды символов, а это неправильно, к примеру, в русском языке оказывается, что "ё" > "я" и "а" > "Я" , хотя всем известно, что я – последняя буква алфавита и это она должна быть больше любой другой. Даты В разных странах принята разная запись дат. Где-то пишут 31.12.2014 (Россия), а где-то 12/31/2014 (США), где-то иначе. Числа В одних странах выводятся цифрами, в других – иероглифами, длинные числа разделяются где-то пробелом, где-то запятой.

Все современные браузеры, кроме IE10 (но есть библиотеки и для него) поддерживают стандарт ECMA 402, предназначенный решить эти проблемы навсегда.

Основные объекты

Intl.Collator Умеет правильно сравнивать и сортировать строки. Intl.DateTimeFormat Умеет форматировать дату и время в соответствии с нужным языком. Intl.NumberFormat Умеет форматировать числа в соответствии с нужным языком.

Локаль

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

Локаль описывается строкой из трёх компонентов, которые разделяются дефисом:

  1. Код языка.
  2. Код способа записи.
  3. Код страны.

На практике не всегда указаны три, обычно меньше:

  1. ru – русский язык, без уточнений.
  2. en-GB – английский язык, используемый в Англии ( GB ).
  3. en-US – английский язык, используемый в США ( US ).
  4. zh-Hans-CN – китайский язык ( zh ), записываемый упрощённой иероглифической письменностью ( Hans ), используемый в Китае.

Также через суффикс -u-* можно указать расширения локалей, например "th-TH-u-nu-thai" – тайский язык ( th ), используемый в Таиланде ( TH ), с записью чисел тайскими буквами (๐, ๑, ๒, ๓, ๔, ๕, ๖, ๗, ๘, ๙) .

Стандарт, который описывает локали – RFC 5464, языки описаны в IANA language registry.

Все методы принимают локаль в виде строки или массива, содержащего несколько локалей в порядке предпочтения.

Если локаль не указана или undefined – берётся локаль по умолчанию, установленная в окружении (браузере).

Подбор локали localeMatcher

localeMatcher – вспомогательная настройка, которую тоже можно везде указать, она определяет способ подбора локали, если желаемая недоступна.

У него два значения:

  • "lookup" – означает простейший порядок поиска путём обрезания суффикса, например zh-Hans-CN → zh-Hans → zh → локаль по умолчанию.
  • "best fit" – использует встроенные алгоритмы и предпочтения браузера (или другого окружения) для выбора подходящей локали.

По умолчанию стоит "best fit" .

Если локалей несколько, например ["zh-Hans-CN", "ru-RU"] то localeMatcher пытается подобрать наиболее подходящую локаль для первой из списка (китайская), если не получается – переходит ко второй (русской) и так далее. Если ни одной не нашёл, например на компьютере не совсем поддерживается ни китайский ни русский, то используется локаль по умолчанию.

Как правило, "best fit" является здесь наилучшим выбором.

Строки, Intl.Collator

Локаль, одна или массив в порядке предпочтения.

Объект с дополнительными настройками:

localeMatcher – алгоритм выбора подходящей локали.

usage – цель сравнения: сортировка "sort" или поиск "search" , по умолчанию "sort" .

sensitivity – чувствительность: какие различия в символах учитывать, а какие – нет, варианты:

  • base – учитывать только разные символы, без диакритических знаков и регистра, например: а ≠ б , е = ё , а = А .
  • accent – учитывать символы и диакритические знаки, например: а ≠ б , е ≠ ё , а = А .
  • case – учитывать символы и регистр, например: а ≠ б , е = ё , а ≠ А .
  • variant – учитывать всё: символ, диакритические знаки, регистр, например: а ≠ б , е ≠ ё , а ≠ А , используется по умолчанию.

ignorePunctuation – игнорировать знаки пунктуации: true/false , по умолчанию false .

numeric – использовать ли численное сравнение: true/false , если true , то будет 12 > 2 , иначе 12 < 2 .

caseFirst – в сортировке должны идти первыми прописные или строчные буквы, варианты: "upper" (прописные), "lower" (строчные) или "false" (стандартное для локали, также является значением по умолчанию). Не поддерживается IE11.

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

Результат compare имеет значение 1 (больше), 0 (равно) или -1 (меньше).

Выше были использованы полностью стандартные настройки. Они различают регистр символа, но это различие можно убрать, если настроить чувствительность sensitivity :

Даты, Intl.DateTimeFormat

Первый аргумент – такой же, как и в Collator , а в объекте options мы можем определить, какие именно части даты показывать (часы, месяц, год…) и в каком формате.

Полный список свойств options :

Свойство Описание Возможные значения По умолчанию
localeMatcher Алгоритм подбора локали lookup , best fit best fit
formatMatcher Алгоритм подбора формата basic , best fit best fit
hour12 Включать ли время в 12-часовом формате true -- 12-часовой формат, false -- 24-часовой
timeZone Временная зона Временная зона, например Europe/Moscow UTC
weekday День недели narrow , short , long
era Эра narrow , short , long
year Год 2-digit , numeric undefined или numeric
month Месяц 2-digit , numeric , narrow , short , long undefined или numeric
day День 2-digit , numeric undefined или numeric
hour Час 2-digit , numeric
minute Минуты 2-digit , numeric
second Секунды 2-digit , numeric
timeZoneName Название таймзоны (нет в IE11) short , long

Все локали обязаны поддерживать следующие наборы настроек:

  • weekday, year, month, day, hour, minute, second
  • weekday, year, month, day
  • year, month, day
  • year, month
  • month, day
  • hour, minute, second

Если указанный формат не поддерживается, то настройка formatMatcher задаёт алгоритм подбора наиболее близкого формата: basic – по стандартным правилам и best fit – по умолчанию, на усмотрение окружения (браузера).

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

К вашему сведению, я не могу использовать серверные сценарии из-за ограничений прокси, поэтому я думаю, что для решения проблемы подойдет JavaScript или ActionScript.

Вопросы:

Какой лучший способ «угадать» локаль пользователя?

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

До какой степени я могу доверять такому решению?

Любые другие обходные пути или предложения?

К сожалению, этот заголовок недоступен для чтения внутри JavaScript; все, что вы получите, это то navigator.language , что говорит вам, какая локализованная версия веб-браузера была установлена. Это не обязательно то же самое, что предпочтительный язык (и) пользователя. В IE вместо этого вы получаете systemLanguage (язык установленной ОС), browserLanguage (так же, как language ) и userLanguage (настраиваемый пользователем регион ОС), которые все также бесполезны.

Если бы мне пришлось выбирать между этими свойствами, я бы userLanguage сначала понюхал , а затем вернулся к ( language и только после этого (если они не соответствуют ни одному из доступных языков) взгляду browserLanguage и, наконец, systemLanguage .

Если вы можете поместить серверный скрипт где-нибудь еще в сети, который просто читает заголовок Accept-Language и выплевывает его обратно в виде файла JavaScript со значением заголовка в строке, например:

тогда вы можете включить <script src>, указывающий на эту внешнюю службу в HTML, и использовать JavaScript для анализа заголовка языка. Однако я не знаю ни одного существующего библиотечного кода для этого, поскольку синтаксический анализ Accept-Language почти всегда выполняется на стороне сервера.

При разработке веб-приложений есть три важных момента, связанных с кодировками: информация в файлах-сценариях, информация в базе данных и браузер пользователя. Если выставить хотя бы одну кодировку неверно, то, в лучшем случае, данные отобразятся неверно, в худшем, безвозвратно потеряются. Чтобы этого не произошло, а приложение работало корректно при любых настройках сервера, нужно правильно выставить кодировки.

Работа с локалями в PHP

Работа с локалями в PHP выглядит одинаково и в UNIX, и в Windows, и в любой другой платформе. Для установки значений локали служит всего одна функция setlocale() . Чтобы выставить локаль, нужно передать функции первым аргументом категорию, на которую эта локаль распространяется, последующими список возможных локалей. Результатом будет название первой подходящей локали, которая и была установлена.

Локали в Windows

Для того, чтобы узнать, какие локали доступны в Windows, нужно зайти в панель управления, "Язык и региональные стандарты".

На вкладке "Дополнительно", в разделе "Кодовые страницы таблиц преобразования" показан список всех возможных локалей для Windows, которые можно использовать в PHP.

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

В общем случае, использование выглядит по следующей схеме: Язык_Регион.Номер_кодовой_страницы

Для Украины - Ukrainian_Ukraine.1251 (cp1251).

Вместо длинных названий можно использовать сокращённые russian , american , ukrainian и так далее. При этом кодовая страница выставится с учётом региональных настроек, для России и Украины - 1251, для Америки - 1252.

Пока это можно списать на внутренний механизм PHP работы со строками. С шестой версии PHP вся обработка строк должна будет вестись в UTF-8, но до тех пор надо просто знать об этом и делать поправку.

Локали в UNIX

Выше я описал работу с локалями в Windows, теперь можно заострить внимание на UNIX-like системах. Для простоты, я буду их называть UNIX, а подразумевать FreeBSD :). В контексте данной статьи это не особо важно.

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

Так может выглядеть работа системной команды locale , которая выводит текущие настройки локали для пользователя. А так, обычно, выглядят настройки локали для пользователя, под которым работает PHP:

Функция ucwords() должна была сделать заглавными первые буквы всех слов. А перед этим strtolower() должна была предварительно все заглавные буквы сделать строчными. Но ничего не произошло. Так же не будет работать следующий код:

Здесь первый аргумент - это категория, на которую будет распространяться локаль (константа LC_*), второй - название локали. Начиная с версии 4.3.0 можно указывать несколько имён локалей в виде массива или в качестве дополнительных аргументов. После вызова функция установит первую подходящую локаль и вернёт её имя:

С помощью команды grep я отобрал локали, которые поддерживают русский язык. Любую из них можно использовать, однако следует понимать, что данные должны быть в кодировке, на которую рассчитана локаль. Если же это правило не будет соблюдено, то результат может оказаться весьма неожиданным:

После установки правильной локали все примеры, которые не работали выше, будут работать как нужно!

По-русски заговорит и функция strftime(), которая корректно работает с локалями, а также и всё остальное, что зависит от локали.

Кодировки в MySQL

Напомню, что возможность задавать кодировки появилась только в MySQL 4.1.11 и выше.

В отличие от php, проблемы с кодировками базы данных проявляют себя гораздо быстрее, чем проблемы с локалью. И связано это прежде всего с хранением и выборкой данных, поскольку от этого зависит информация на сайте. Я не буду подробно расписывать все тонкости, поскольку есть отдельная статья, остановлюсь на самых важных моментах.

Первое, чему необходимо научиться, смотреть текущие настройки соединения с mysql:

Критичными для пользователя являются character_set_client и character_set_results, которые отвечают за кодировку, в которой данные поступают в базу, и кодировку, в которой данные поступают из базы к пользователю. Если эти две кодировки отличаются от той, в которой работает клиент, в нашем случае php-скрипты, то неминуемо будут "странности", например, при сортировке выборки или внесении данных в базу.

Второе, что необходимо знать, как правильно сообщить mysql о кодировках. Самый простой и правильный способ, это использовать запрос set names:

После этого три переменные character_set_client, character_set_connection и character_set_results примут значение cp1251. Это будет означать - клиент работает в кодировке windows-1251 (cp1251).

Помимо этого можно устанавливать непосредственно серверные переменные:

Теперь данные поступают и извлекаются в разных кодировках.

Список доступных кодировок можно просмотреть так:

И третье, что необходимо знать, - правила создания таблиц для хранения данных в нужной кодировке. К слову, данные можно хранить в любой кодировке, а работать с ними в кодировке клиента. Однако, важно понимать, что кодировки носят национальный характер и должны соответствовать вносимым данным. Иначе будут потери. Для русского языка есть три национальных кодировки koi8r, cp866, cp1251, которые могут конвертироваться друг в друга без потерь. Также можно использовать интернациональную кодировку UTF8.

Кодировку можно выставить на базу данных, таблицу и поле таблицы. Так, например, можно создать базу данных в кодировке koi8r:

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

Следующим шагом я создам таблицу в cp1251 и одним полем в utf8:

После того, как таблица создана с нужными параметрами кодировки, mysql автоматически начинает переводить данные при внесении и выборке.

Данные хранятся в разном виде, но поступают к пользователю именно так, как надо!

Кодировка HTML-страниц

Объявить кодировку html-страницы можно двумя способами: через заголовки и мета-тег в самой странице. Мета-тег используется только в статичных страницах.

Но браузер отобразит страницу корректно только в том случае, когда php-файлы сами были созданы в кодировке cp1251. Также нужно понимать, что заголовки должны быть отправлены до любого вывода на экран.

При необходимости перекодировать страницы "на лету", достаточно воспользоваться буферизацией и iconv:

Надпись "Привет, мир!" будет выведена в юникоде, при этом браузер получит информацию о кодировке через заголовки и правильно отобразит страницу. Но важно понимать, что внутри скрипта и при соединении с базой данных надо использовать windows-1251 (cp1251), поскольку страница должна быть сформирована в одной кодировке.

Важно помнить, что функции iconv доступны не всегда, и проверка на доступность этих функций не будет лишней.

Заключение

Для безопасной разработки русскоязычных веб-проектов необходимо включать в файл с общими настройками следующие команды:

Как ни странно, но эти три строчки кода значительно повышают портируемость веб-проектов.

© 2021 Антон Прибора. При копировании материалов с сайта, пожалуйста, указывайте ссылку на источник.


Поле заголовка запроса Accept-Language сходно с полем Accept, но регламентирует набор естественных языков, которые предпочтительны в отклике на запрос.

Каждому набору языков может быть поставлено в соответствие значение качества, которое представляет собой оценку предпочтений пользователя для языков, специфицированных в диапазоне. По умолчанию значение качества "q=1". Например:

будет означать: "Я предпочитаю датский, но восприму британский английский и другие типы английского". Список языков согласуется с языковой меткой, если он в точности равен метке или, если он в точности равен префиксу метки, такому как первый символ метки, за которым следует символ "-". Специальный список "*", если он присутствует в поле Accept-Language, согласуется с любой меткой.

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

Фактор качества, присваиваемый языковой метке с помощью поля Accept-Language, равен значению качества самого длинного списка языков в поле. Если в поле отсутствует список языков, фактору качества присваивается значение нуль. Если в запросе отсутствует заголовок Accept-Language, серверу следует предполагать, что все языки приемлемы в равной мере. Если заголовок Accept-Language имеется, тогда все языки, которым присвоен фактор качества больше нуля, приемлемы.

Посылка заголовка Accept-Language с полным списком языковых предпочтений пользователя в каждом запросе может восприниматься как нарушение принципов конфиденциальности.

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

Говоря просто: ru_UA - язык_СТРАНА.

ru_UA - русская локаль для Украины.

uk_UA - украинская локаль для Украины. Это локаль для украинского языка. Но для этой локали нужно сделать нормальную украинскую локализацию. При её выборе большинство приложений будут на английском из-за отсутствия украинских mo- файлов.

Локаль ru_UA отличается от локали ru_RU всего тремя буквами, а именно і - русское и, є -русское е, и ї - нечто среднее между й и ё. В полноценной локали ua_UA буквы (фонетические звуки) г, х, з, c, ф, они правильно пишутся с черточками над верхом буквы.

Читайте также: