Как отображать названия языков, например English, Español, 日本語?
Используйте Intl.DisplayNames, чтобы показывать названия языков на их родных письменностях для языковых переключателей и интернационализированных интерфейсов.
Введение
Когда вы создаёте языковой переключатель или отображаете список доступных языков, важно показывать названия языков так, чтобы пользователи их узнавали. Франкоговорящий ищет "Français", испаноговорящий — "Español", а японец — "日本語". Пользователи определяют свой язык по родному написанию и алфавиту.
Жёстко прописывать такие переводы не масштабируется. Вам пришлось бы поддерживать переводы каждого названия языка на все остальные языки. API Intl.DisplayNames решает эту задачу, предоставляя стандартизированные, локализованные названия языков, стран, письменностей и валют.
Проблема жёстко заданных названий языков
Вы можете создать языковой переключатель, жёстко прописав названия языков в объекте.
const languageNames = {
en: "English",
es: "Spanish",
fr: "French",
de: "German",
ja: "Japanese"
};
console.log(languageNames.en);
// "English"
У этого подхода три проблемы. Во-первых, такие названия подходят только для англоговорящих. Во-вторых, пользователи не узнают свой язык, если он отображается на английском. Японец, например, ищет японские символы, а не слово "Japanese". В-третьих, вам придётся вручную поддерживать переводы каждого языка на все остальные.
const languageNames = {
en: {
en: "English",
es: "Spanish",
fr: "French",
de: "German",
ja: "Japanese"
},
es: {
en: "Inglés",
es: "Español",
fr: "Francés",
de: "Alemán",
ja: "Japonés"
}
// ... more languages
};
Это быстро становится неудобно и непрактично. Нужен более удобный способ.
Как получить названия языков с помощью Intl.DisplayNames
Конструктор Intl.DisplayNames создаёт форматтер, который преобразует языковые коды в читаемые названия. Вы указываете локаль и тип нужных названий.
const names = new Intl.DisplayNames(["en"], { type: "language" });
console.log(names.of("en"));
// "English"
Первый аргумент — массив идентификаторов локалей. Второй аргумент — объект с опциями, где type: "language" сообщает форматтеру, что нужны именно названия языков. Метод of() принимает языковой код и возвращает его название.
Вы можете получать названия на любом языке, просто меняя локаль.
const enNames = new Intl.DisplayNames(["en"], { type: "language" });
const esNames = new Intl.DisplayNames(["es"], { type: "language" });
const frNames = new Intl.DisplayNames(["fr"], { type: "language" });
console.log(enNames.of("es"));
// "Spanish"
console.log(esNames.of("es"));
// "español"
console.log(frNames.of("es"));
// "espagnol"
Каждый форматтер возвращает название языка на языке отображения. Это избавляет от необходимости вручную поддерживать переводы названий языков.
Отображение названий языков в их родной форме
Лучше всего показывать каждый язык в его собственном написании — так пользователи быстрее узнают свой язык по знакомым символам.
const names = new Intl.DisplayNames(["ja"], { type: "language" });
console.log(names.of("ja"));
// "日本語"
Чтобы получить родное название языка, создайте форматтер с нужной локалью отображения.
function getNativeName(languageCode) {
const names = new Intl.DisplayNames([languageCode], { type: "language" });
return names.of(languageCode);
}
console.log(getNativeName("en"));
// "English"
console.log(getNativeName("es"));
// "español"
console.log(getNativeName("fr"));
// "français"
console.log(getNativeName("de"));
// "Deutsch"
console.log(getNativeName("ja"));
// "日本語"
console.log(getNativeName("ar"));
// "العربية"
console.log(getNativeName("zh"));
// "中文"
Этот подход работает для любого кода языка. Форматтер вернёт название в том написании и форме, которые используют носители.
Как устроены форматы кодов языков
Метод of() принимает коды языков в разных форматах. Можно использовать как простые коды вроде "en", так и полные идентификаторы локалей, например "en-US".
const names = new Intl.DisplayNames(["en"], { type: "language" });
console.log(names.of("en"));
// "English"
console.log(names.of("en-US"));
// "American English"
console.log(names.of("en-GB"));
// "British English"
console.log(names.of("zh"));
// "Chinese"
console.log(names.of("zh-Hans"));
// "Simplified Chinese"
console.log(names.of("zh-Hant"));
// "Traditional Chinese"
Форматтер понимает и короткие коды, и расширенные идентификаторы с регионом или скриптом. Это позволяет различать варианты языков.
Как управлять отображением названий языков
Опция languageDisplay определяет, насколько подробно будет возвращаться название. Она принимает два значения.
Значение "standard" возвращает полные названия с учётом диалектов. Это значение по умолчанию.
const names = new Intl.DisplayNames(["en"], {
type: "language",
languageDisplay: "standard"
});
console.log(names.of("en-US"));
// "American English"
console.log(names.of("en-GB"));
// "British English"
console.log(names.of("pt-BR"));
// "Brazilian Portuguese"
console.log(names.of("pt-PT"));
// "European Portuguese"
Значение "dialect" тоже возвращает полные названия с диалектами. Обычно результат такой же, как у "standard".
const names = new Intl.DisplayNames(["en"], {
type: "language",
languageDisplay: "dialect"
});
console.log(names.of("en-US"));
// "American English"
console.log(names.of("pt-BR"));
// "Brazilian Portuguese"
Для выбора языка стандартный формат помогает пользователям выбрать нужный вариант, если есть несколько диалектов.
Получение локализованных названий языков для интерфейса пользователя
Когда вы создаёте страницу настроек или селектор языка, нужно показывать названия языков на текущем языке интерфейса пользователя. Создайте форматтер, используя локаль пользователя.
function getLocalizedLanguageName(languageCode, userLocale) {
const names = new Intl.DisplayNames([userLocale], { type: "language" });
return names.of(languageCode);
}
// User interface is in English
console.log(getLocalizedLanguageName("ja", "en"));
// "Japanese"
// User interface is in French
console.log(getLocalizedLanguageName("ja", "fr"));
// "japonais"
// User interface is in Spanish
console.log(getLocalizedLanguageName("ja", "es"));
// "japonés"
Такой подход позволяет отображать понятные названия на языке, который понимает пользователь. Совмещайте это с нативными названиями, чтобы создавать гибридные метки вроде «日本語 (Japanese)», которые подходят и для носителей языка, и для других пользователей.
Создание селектора языка с нативными названиями
Частый кейс — создание выпадающего списка или списка, где пользователь выбирает предпочитаемый язык. Показывайте каждый язык в его нативном виде, чтобы пользователи могли быстро найти нужный вариант.
const supportedLanguages = ["en", "es", "fr", "de", "ja", "ar", "zh"];
function createLanguageOptions() {
return supportedLanguages.map((code) => {
const names = new Intl.DisplayNames([code], { type: "language" });
const nativeName = names.of(code);
return { code, name: nativeName };
});
}
const options = createLanguageOptions();
console.log(options);
Это создаёт массив языковых опций с нативными названиями.
[
{ code: "en", name: "English" },
{ code: "es", name: "español" },
{ code: "fr", name: "français" },
{ code: "de", name: "Deutsch" },
{ code: "ja", name: "日本語" },
{ code: "ar", name: "العربية" },
{ code: "zh", name: "中文" }
]
Вы можете отобразить эти опции в HTML, чтобы сделать селектор языка.
function renderLanguageSelector() {
const options = createLanguageOptions();
const select = document.createElement("select");
select.id = "language-selector";
options.forEach((option) => {
const optionElement = document.createElement("option");
optionElement.value = option.code;
optionElement.textContent = option.name;
select.appendChild(optionElement);
});
return select;
}
const selector = renderLanguageSelector();
document.body.appendChild(selector);
Такой выпадающий список показывает каждый язык в его родном написании, что облегчает пользователям поиск своего языка.
Создание гибридных языковых меток
В некоторых интерфейсах показывают и нативное название, и перевод на языке пользователя. Это помогает тем, кто не узнаёт все алфавиты, и делает интерфейс более доступным.
function createHybridLabel(languageCode, userLocale) {
const nativeNames = new Intl.DisplayNames([languageCode], {
type: "language"
});
const localizedNames = new Intl.DisplayNames([userLocale], {
type: "language"
});
const nativeName = nativeNames.of(languageCode);
const localizedName = localizedNames.of(languageCode);
if (nativeName === localizedName) {
return nativeName;
}
return `${nativeName} (${localizedName})`;
}
// User interface is in English
console.log(createHybridLabel("ja", "en"));
// "日本語 (Japanese)"
console.log(createHybridLabel("ar", "en"));
// "العربية (Arabic)"
console.log(createHybridLabel("en", "en"));
// "English"
// User interface is in Spanish
console.log(createHybridLabel("ja", "es"));
// "日本語 (japonés)"
Такой подход сочетает узнаваемость нативных названий с понятностью локализованных переводов.
Обработка резервных локалей
Конструктор Intl.DisplayNames принимает массив локалей. Если первая локаль недоступна, форматтер использует следующую в списке.
const names = new Intl.DisplayNames(["xx-XX", "en"], { type: "language" });
console.log(names.of("fr"));
// "French"
Форматтер сначала пробует "xx-XX", которой не существует, затем переходит к "en". Это гарантирует, что ваш код будет работать даже если нужная локаль недоступна.
Вы можете проверить, какой язык на самом деле использует форматтер, с помощью метода resolvedOptions().
const names = new Intl.DisplayNames(["xx-XX", "en"], { type: "language" });
console.log(names.resolvedOptions().locale);
// "en"
Здесь видно, что после резервного варианта форматтер выбрал английский язык.
Как разные локали форматируют названия языков
В каждой локали свои правила написания названий языков. Форматтер применяет их автоматически.
const supportedLanguages = ["en", "es", "fr", "de", "ja"];
function showLanguageNamesInLocale(locale) {
const names = new Intl.DisplayNames([locale], { type: "language" });
return supportedLanguages.map((code) => ({
code,
name: names.of(code)
}));
}
console.log(showLanguageNamesInLocale("en"));
// [
// { code: "en", name: "English" },
// { code: "es", name: "Spanish" },
// { code: "fr", name: "French" },
// { code: "de", name: "German" },
// { code: "ja", name: "Japanese" }
// ]
console.log(showLanguageNamesInLocale("es"));
// [
// { code: "en", name: "inglés" },
// { code: "es", name: "español" },
// { code: "fr", name: "francés" },
// { code: "de", name: "alemán" },
// { code: "ja", name: "japonés" }
// ]
console.log(showLanguageNamesInLocale("ja"));
// [
// { code: "en", name: "英語" },
// { code: "es", name: "スペイン語" },
// { code: "fr", name: "フランス語" },
// { code: "de", name: "ドイツ語" },
// { code: "ja", name: "日本語" }
// ]
Форматтер автоматически учитывает регистр, алфавит и языковые особенности для каждого языка.
Поддержка браузеров
API Intl.DisplayNames доступен во всех современных браузерах. С марта 2021 года его поддерживают основные браузеры, включая Chrome, Firefox, Safari и Edge.
Вы можете проверить доступность API перед его использованием.
if (typeof Intl.DisplayNames !== "undefined") {
const names = new Intl.DisplayNames(["en"], { type: "language" });
console.log(names.of("fr"));
} else {
console.log("Intl.DisplayNames is not supported");
}
Для старых браузеров нужно добавить резервный вариант или использовать polyfill. Простой вариант — это жёстко заданное сопоставление кодов языков с их названиями.
function getLanguageName(code, locale) {
if (typeof Intl.DisplayNames !== "undefined") {
const names = new Intl.DisplayNames([locale], { type: "language" });
return names.of(code);
}
// Fallback for older browsers
const fallbackNames = {
en: "English",
es: "español",
fr: "français",
de: "Deutsch",
ja: "日本語"
};
return fallbackNames[code] || code;
}
console.log(getLanguageName("es", "en"));
// "español" (or "Spanish" if you adjust the fallback for localization)
Это гарантирует работу вашего кода даже в браузерах без поддержки Intl.DisplayNames, но автоматические функции локализации будут недоступны.