Как работать с языковыми тегами, такими как en-US и fr-CA
Разберитесь в структуре и значении языковых тегов BCP 47, чтобы принимать лучшие решения по интернационализации
Введение
Языковые теги — это стандартизированные коды, которые определяют конкретные языки и их региональные варианты. Эти теги встречаются повсюду в работе по интернационализации. Когда вы определяете предпочитаемый язык пользователя, браузер возвращает языковые теги. При форматировании дат или чисел вы передаёте языковые теги в Intl API. При загрузке переводов вы используете языковые теги, чтобы определить, какой контент показывать.
Понимание того, как работают эти теги, помогает принимать более грамотные решения о выборе языка, поведении при отсутствии перевода и организации контента. В этом уроке объясняется структура языковых тегов и показано, как работать с ними в JavaScript.
Что такое языковые теги
Языковой тег — это строка вроде en, en-US или zh-Hans-CN, которая определяет язык и, при необходимости, указывает скрипт и регион. Эти теги соответствуют стандарту BCP 47, который поддерживается Internet Engineering Task Force и Internet Assigned Numbers Authority.
BCP 47 расшифровывается как Best Current Practice 47. Этот стандарт определяет, как составлять языковые теги из более мелких компонентов — подтегов. Каждый подтег отражает определённый аспект языка: какой язык используется, какая письменность применяется или с какой страной он ассоциируется.
Все языки программирования и библиотеки для интернационализации используют теги BCP 47. Благодаря этому вы можете использовать одни и те же языковые идентификаторы во всём приложении: от определения языка в браузере до серверного форматирования и имён файлов с переводами.
Структура языковых тегов
Языковые теги состоят из подметок, разделённых дефисами. Три самых распространённых подметки — это язык, скрипт и регион. Если они присутствуют, то всегда идут именно в таком порядке.
Сначала идёт подметка языка — это единственный обязательный компонент. Она использует двух- или трёхбуквенный код из ISO 639. Например, en обозначает английский, fr — французский, а zh — китайский.
Второй идёт подметка скрипта, если она есть. Она использует четырёхбуквенный код из ISO 15924, который определяет письменность. Например, Latn обозначает латиницу, Cyrl — кириллицу, а Hans — упрощённые китайские иероглифы.
Последней идёт подметка региона, если она есть. Она использует двухбуквенный код из ISO 3166-1, который обычно обозначает страну. Например, US — это США, CA — Канада, а CN — Китай.
Примеры распространённых языковых тегов
Вот примеры, которые показывают разные уровни детализации, которые можно выразить с помощью языковых тегов.
Простые теги только с языком:
en— английский (без указания региона или скрипта)fr— французский (без указания региона или скрипта)es— испанский (без указания региона или скрипта)
Теги с языком и регионом:
en-US— английский, используемый в СШАen-GB— английский, используемый в Великобританииfr-CA— французский, используемый в Канадеes-MX— испанский, используемый в Мексике
Теги с языком, письмом и регионом:
zh-Hans-CN— китайский с упрощёнными иероглифами в Китаеzh-Hant-TW— китайский с традиционными иероглифами на Тайванеsr-Latn-RS— сербский на латинице в Сербииsr-Cyrl-RS— сербский на кириллице в Сербии
Степень детализации зависит от вашего приложения. Если вы просто переводите текст, может быть достаточно языка и региона. Если работаете с языками, где есть несколько систем письма, нужны подтеги письма.
Регистр в языковых тегах
Языковые теги нечувствительны к регистру. Теги en-US, EN-US, en-us и En-Us обозначают один и тот же язык. Но существуют общепринятые правила написания, которые делают теги более читаемыми.
Языковые подтеги обычно пишутся строчными буквами. Пишите en, а не EN или En.
Подтеги письма обычно пишутся с заглавной первой буквой. Пишите Latn, а не latn или LATN.
Подтеги региона обычно пишутся заглавными буквами. Пишите US, а не us или Us.
Следование этим правилам делает теги более читаемыми и соответствует формату, используемому в документации и спецификациях. Однако ваш код должен принимать языковые теги в любом регистре, так как формат официально нечувствителен к регистру.
Разбор языковых тегов в JavaScript
В JavaScript есть конструктор Intl.Locale, который разбирает языковые теги и извлекает их компоненты. Этот конструктор принимает строку с языковым тегом и возвращает объект со свойствами для каждого подтега.
const locale = new Intl.Locale("en-US");
console.log(locale.language);
// Output: "en"
console.log(locale.region);
// Output: "US"
Объект Intl.Locale содержит свойства для каждого компонента языкового тега. Эти свойства возвращают undefined, если соответствующий подтаг отсутствует в исходном теге.
const simple = new Intl.Locale("fr");
console.log(simple.language);
// Output: "fr"
console.log(simple.region);
// Output: undefined
Вы можете разбирать теги с подтагами скрипта таким же образом.
const complex = new Intl.Locale("zh-Hans-CN");
console.log(complex.language);
// Output: "zh"
console.log(complex.script);
// Output: "Hans"
console.log(complex.region);
// Output: "CN"
Эта возможность разбора полезна, когда нужно принимать решения на основе конкретных компонентов языкового тега. Например, можно загружать разные шрифты в зависимости от скрипта или показывать разный контент в зависимости от региона.
Когда использовать специфичные и общие теги
Выбор нужного уровня детализации языковых тегов зависит от того, какие аспекты языка и культуры ваше приложение должно учитывать.
Используйте только языковые теги, такие как en или fr, если у вас есть один перевод, подходящий для всех носителей этого языка. Это часто встречается в приложениях с ограниченным бюджетом на локализацию или для языков с минимальными региональными различиями.
Используйте теги с языком и регионом, такие как en-US или fr-CA, если нужно учитывать региональные различия в лексике, орфографии или культурных особенностях. Например, британский и американский английский отличаются написанием многих слов. Канадский и европейский французский имеют разную лексику и выражения.
Используйте теги с языком, скриптом и регионом, такие как zh-Hans-CN, если работаете с языками, использующими несколько систем письма. Например, китайский может быть написан упрощёнными или традиционными иероглифами. Сербский может быть написан латиницей или кириллицей. Подтаг скрипта различает эти варианты.
Извлечение языковых кодов для файлов перевода
Многие системы перевода организуют файлы по языковому коду. Можно извлечь только язык и регион из полного языкового тега, чтобы определить, какой файл перевода загрузить.
const userLanguage = "zh-Hans-CN";
const locale = new Intl.Locale(userLanguage);
const translationKey = `${locale.language}-${locale.region}`;
console.log(translationKey);
// Output: "zh-CN"
Этот подход работает даже если в языковом теге пользователя есть компоненты, которые не нужны для выбора файла.
Некоторые приложения используют только языковой код без региона.
const userLanguage = "fr-CA";
const locale = new Intl.Locale(userLanguage);
const translationKey = locale.language;
console.log(translationKey);
// Output: "fr"
Структура имён файлов перевода должна соответствовать тому, как вы извлекаете компоненты из языковых тегов.
Использование языковых тегов с Intl API
Intl API принимает языковые теги напрямую во всех своих конструкторах. Не нужно разбирать тег вручную, если не требуется проверить отдельные компоненты.
const date = new Date("2025-03-15");
const usFormat = new Intl.DateTimeFormat("en-US").format(date);
console.log(usFormat);
// Output: "3/15/2025"
const gbFormat = new Intl.DateTimeFormat("en-GB").format(date);
console.log(gbFormat);
// Output: "15/03/2025"
Intl API использует языковой тег, чтобы определить, какие правила форматирования применять. В разных регионах по-разному форматируют даты, числа и валюту, даже если говорят на одном языке.
Можно передавать языковой тег, полученный из браузера, напрямую в конструкторы Intl.
const userLanguage = navigator.language;
const formatter = new Intl.NumberFormat(userLanguage);
console.log(formatter.format(1234.5));
// Output varies by language
// For "en-US": "1,234.5"
// For "de-DE": "1.234,5"
Это самый распространённый подход в клиентской интернационализации. Определяйте язык пользователя и используйте этот тег по всему приложению для корректного форматирования контента.
Обработка некорректных языковых тегов
Конструктор Intl.Locale выбрасывает RangeError, если передан некорректный языковой тег. Нужно обрабатывать эту ошибку, если работаете с тегами из ненадёжных источников.
try {
const locale = new Intl.Locale("invalid-tag-format");
} catch (error) {
console.log(error.name);
// Output: "RangeError"
console.log(error.message);
// Output: "invalid language tag: invalid-tag-format"
}
Большинство языковых тегов из браузеров валидны, но пользовательский ввод или внешние источники могут содержать некорректные теги. Обработка ошибок в конструкторе защитит приложение от сбоев из-за таких тегов.