Определение локали пользователя

Локаль определяет правила отображения данных в зависимости от языка, региона и культурных особенностей пользователя. Через локаль форматируются:

  • даты и время;
  • числа;
  • валюты;
  • единицы измерения;
  • порядок сортировки строк;
  • правила множественного числа;
  • формат адресов и имён.

В JavaScript работа с локалями реализована через встроенный объект Intl.

Примеры локалей:

Локаль Язык Регион
en-US английский США
en-GB английский Великобритания
ru-RU русский Россия
kk-KZ казахский Казахстан
fr-FR французский Франция

Структура локали обычно выглядит так:

язык-РЕГИОН

Например:

ru-RU
en-US
de-DE

Получение локали браузера

Свойство navigator.language

Самый распространённый способ определения языка пользователя — использование свойства navigator.language.

console.log(navigator.language);

Пример результата:

ru-RU

Значение зависит от языка интерфейса браузера или операционной системы.


Список предпочитаемых локалей

Свойство navigator.languages

Пользователь может указать несколько языков в настройках браузера. Для получения полного списка используется navigator.languages.

console.log(navigator.languages);

Пример:

[
  "ru-RU",
  "ru",
  "en-US",
  "en"
]

Первый элемент массива считается наиболее предпочтительным.

Получение основной локали:

const locale = navigator.languages[0];

console.log(locale);

Разница между language и languages

Свойство Тип Назначение
navigator.language строка основная локаль
navigator.languages массив список предпочитаемых локалей

Пример:

console.log(navigator.language);
console.log(navigator.languages);

Результат:

en-US

["en-US", "fr", "de"]

Использование локали по умолчанию

Большинство объектов Intl автоматически используют локаль пользователя, если она не указана явно.

Пример:

const number = 123456.78;

console.log(
  new Intl.NumberFormat().format(number)
);

Для пользователя с локалью ru-RU:

123 456,78

Для en-US:

123,456.78

Явное указание локали

Локаль можно передать первым аргументом.

const number = 123456.78;

console.log(
  new Intl.NumberFormat("de-DE").format(number)
);

Результат:

123.456,78

Проверка поддержки локали

Метод Intl.NumberFormat.supportedLocalesOf()

Некоторые окружения могут не поддерживать отдельные локали. Для проверки используется метод supportedLocalesOf.

const locales = [
  "ru-RU",
  "fr-FR",
  "xx-XX"
];

const supported =
  Intl.NumberFormat.supportedLocalesOf(locales);

console.log(supported);

Результат:

["ru-RU", "fr-FR"]

Несуществующая локаль xx-XX была отброшена.


Нормализация локали

Разные записи локалей могут означать одно и то же. Объект Intl.Locale позволяет привести их к стандартному виду.

const locale = new Intl.Locale("ru-ru");

console.log(locale.toString());

Результат:

ru-RU

Объект Intl.Locale

Создание локали

const locale = new Intl.Locale("en-US");

console.log(locale);

Получение частей локали

Язык

const locale = new Intl.Locale("ru-RU");

console.log(locale.language);

Результат:

ru

Регион

console.log(locale.region);

Результат:

RU

Скрипт

Некоторые языки используют разные системы письма.

const locale =
  new Intl.Locale("sr-Cyrl-RS");

console.log(locale.script);

Результат:

Cyrl

Использование запасной локали

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

Пример:

const supportedLocales = ["en", "ru"];
const userLocale = navigator.language;

const locale =
  supportedLocales.includes(userLocale)
    ? userLocale
    : "en";

console.log(locale);

Недостаток такого подхода — строгое сравнение.

Например:

ru-RU

не совпадает с:

ru

Более гибкая проверка локали

Часто достаточно сравнивать только язык.

const supportedLocales = ["en", "ru"];

const userLocale =
  navigator.language.split("-")[0];

const locale =
  supportedLocales.includes(userLocale)
    ? userLocale
    : "en";

console.log(locale);

Поиск подходящей локали

Использование find()

const supported = ["en", "ru", "fr"];

const locale =
  navigator.languages.find(lang => {
    return supported.includes(
      lang.split("-")[0]
    );
  });

console.log(locale);

Метод resolvedOptions()

Каждый форматтер Intl может сообщить, какая локаль была выбрана фактически.

const formatter =
  new Intl.DateTimeFormat("ru");

console.log(
  formatter.resolvedOptions()
);

Результат:

{
  locale: "ru",
  calendar: "gregory",
  numberingSystem: "latn",
  timeZone: "Europe/Moscow",
  ...
}

Определение часового пояса

Через Intl.DateTimeFormat

const timeZone =
  Intl.DateTimeFormat()
    .resolvedOptions()
    .timeZone;

console.log(timeZone);

Пример:

Asia/Almaty

Часовой пояс часто используется совместно с локалью.


Формирование пользовательских настроек

Часто приложение хранит все региональные параметры в одном объекте.

const userSettings = {
  locale: navigator.language,
  timeZone:
    Intl.DateTimeFormat()
      .resolvedOptions()
      .timeZone
};

console.log(userSettings);

Автоматический выбор языка интерфейса

Пример простой системы локализации:

const messages = {
  en: {
    greeting: "Hello"
  },

  ru: {
    greeting: "Привет"
  }
};

const locale =
  navigator.language.split("-")[0];

const message =
  messages[locale] || messages.en;

console.log(message.greeting);

Использование нескольких уровней запасных локалей

Более надёжная схема:

const locales = {
  "ru-RU": {
    currency: "RUB"
  },

  "ru": {
    currency: "RUB"
  },

  "en": {
    currency: "USD"
  }
};

const userLocale = navigator.language;

const config =
  locales[userLocale]
  || locales[userLocale.split("-")[0]]
  || locales.en;

console.log(config);

Особенности серверного JavaScript

В Node.js локаль также зависит от окружения.

Получение локали:

console.log(
  Intl.DateTimeFormat()
    .resolvedOptions()
    .locale
);

Получение переменных окружения:

console.log(process.env.LANG);

Пример:

ru_RU.UTF-8

Проблемы определения локали

Пользовательский язык не всегда совпадает со страной

Пример:

  • пользователь находится в Германии;
  • интерфейс системы установлен на английский;
  • браузер возвращает en-US.

Локаль отражает языковые предпочтения, а не географию.


Локаль не гарантирует формат данных

Даже внутри одной страны могут использоваться разные форматы.

Например:

  • en-US:

    • 12/31/2025
  • en-GB:

    • 31/12/2025

Пользователь может изменить язык браузера вручную

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


Получение региона из локали

Иногда требуется извлечь только код страны.

const locale = navigator.language;

const region =
  locale.split("-")[1];

console.log(region);

Для:

ru-RU

результат:

RU

Работа с неполными локалями

Некоторые браузеры могут возвращать только язык:

ru

Поэтому код должен учитывать отсутствие региона.

Безопасный вариант:

const locale = navigator.language;

const [language, region] =
  locale.split("-");

console.log(language);
console.log(region || "unknown");

Сравнение локалей без учёта регистра

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

const locale1 = "ru-ru";
const locale2 = "RU-RU";

console.log(
  locale1.toLowerCase() ===
  locale2.toLowerCase()
);

Канонизация локали

Для приведения к стандартному формату используется:

Intl.getCanonicalLocales()

Пример:

const locales =
  Intl.getCanonicalLocales([
    "ru-ru",
    "EN-us"
  ]);

console.log(locales);

Результат:

["ru-RU", "en-US"]

Использование локали в форматировании дат

const date = new Date();

console.log(
  new Intl.DateTimeFormat(
    navigator.language
  ).format(date)
);

Использование локали в форматировании чисел

const price = 1234567.89;

console.log(
  new Intl.NumberFormat(
    navigator.language
  ).format(price)
);

Использование локали в форматировании валют

const amount = 5000;

console.log(
  new Intl.NumberFormat(
    navigator.language,
    {
      style: "currency",
      currency: "USD"
    }
  ).format(amount)
);

Определение направления письма

Некоторые языки пишутся справа налево.

Пример:

  • арабский;
  • иврит.

Определение направления:

const locale =
  new Intl.Locale("ar-EG");

console.log(locale.textInfo);

Результат:

{
  direction: "rtl"
}

Использование локали в сортировке

const names = [
  "яблоко",
  "банан",
  "абрикос"
];

names.sort(
  new Intl.Collator("ru").compare
);

console.log(names);

Результат:

[
  "абрикос",
  "банан",
  "яблоко"
]

Получение максимальной информации о локали

const locale =
  new Intl.Locale(navigator.language);

console.log({
  baseName: locale.baseName,
  language: locale.language,
  region: locale.region,
  script: locale.script
});

Практический шаблон определения локали

function detectLocale() {
  const supported = ["en", "ru", "kk"];

  const locales = navigator.languages;

  for (const locale of locales) {
    const language =
      locale.split("-")[0];

    if (supported.includes(language)) {
      return language;
    }
  }

  return "en";
}

const locale = detectLocale();

console.log(locale);

Практический шаблон региональных настроек

function getUserConfig() {
  const locale =
    navigator.language;

  const timeZone =
    Intl.DateTimeFormat()
      .resolvedOptions()
      .timeZone;

  return {
    locale,
    timeZone
  };
}

console.log(
  getUserConfig()
);