Как отображать названия систем письма, например латиница, кириллица, арабское письмо?
Используйте Intl.DisplayNames, чтобы преобразовать коды систем письма в понятные человеку названия на любом языке.
Введение
Система письма — это способ записи языка. Латиница используется для английского, французского и испанского языков. Кириллица применяется для русского, болгарского и украинского. Арабское письмо используется для арабского, персидского и урду. Системы письма отличаются от языков, потому что один и тот же язык может быть записан разными системами письма. Например, сербский используют и кириллицу, и латиницу. Пользователи в Сербии сами выбирают, какой системой письма пользоваться.
Когда вы создаёте языковые переключатели, выбор шрифтов или поля для ввода текста, нужно показывать названия систем письма, чтобы пользователи могли выбрать нужный способ написания. API Intl.DisplayNames преобразует коды систем письма в локализованные, понятные человеку названия — не нужно поддерживать собственные таблицы переводов.
Различие между системами письма и языками
Система письма и язык — это не одно и то же. Язык — это то, на чём говорят люди. Система письма — это то, как этот язык записывают.
Английский — это язык. Латиница — это система письма. Английский использует латиницу, но так же делают десятки других языков, например испанский, французский, немецкий, вьетнамский и турецкий.
Сербский — это язык, который можно записывать двумя системами письма. Сербский на кириллице выглядит как «Српски». Сербский на латинице — как «Srpski». Это один и тот же язык, с одинаковыми словами и грамматикой. Отличие только в системе письма.
В китайском языке есть два распространённых варианта системы письма. Упрощённый китайский использует упрощённые иероглифы. Традиционный китайский — традиционные иероглифы. Одна и та же фраза будет выглядеть по-разному в зависимости от выбранной системы письма.
Эта разница важна при создании интерфейсов. Пользователь из Сербии может предпочесть кириллический текст вместо латиницы. Китайскому пользователю нужно выбрать между упрощёнными и традиционными иероглифами. Ваш интерфейс должен показывать названия письменностей, чтобы пользователи могли сделать выбор.
Проблема жёстко заданных названий письменностей
Вы можете создать таблицу соответствия, которая сопоставляет коды письменностей их названиям.
const scriptNames = {
Latn: "Latin",
Cyrl: "Cyrillic",
Arab: "Arabic",
Hans: "Simplified Chinese",
Hant: "Traditional Chinese"
};
console.log(scriptNames.Latn);
// "Latin"
Этот подход работает только для англоговорящих. Пользователи, говорящие на других языках, видят английские названия письменностей, которые могут быть им непонятны. Вам нужны переводы для каждого поддерживаемого языка.
const scriptNames = {
en: {
Latn: "Latin",
Cyrl: "Cyrillic",
Arab: "Arabic"
},
es: {
Latn: "latino",
Cyrl: "cirílico",
Arab: "árabe"
},
fr: {
Latn: "latin",
Cyrl: "cyrillique",
Arab: "arabe"
}
};
Это быстро становится неудобно для поддержки. Для каждого нового языка требуется полный набор переводов. Для каждой новой письменности нужны записи на всех языках. Нужен более удобный способ.
Использование Intl.DisplayNames для получения названий письменностей
Конструктор Intl.DisplayNames создаёт форматтер, который преобразует коды письменностей в понятные человеку названия. Вы указываете локаль и задаёте тип "script".
const names = new Intl.DisplayNames(["en"], { type: "script" });
console.log(names.of("Latn"));
// "Latin"
Первый аргумент — это массив идентификаторов локалей. Второй аргумент — объект с опциями, где type: "script" сообщает форматтеру, что нужны названия письменностей. Метод of() принимает код письменности и возвращает её название.
Коды письменностей соответствуют стандарту ISO 15924. Каждый код состоит из четырёх букв: первая — заглавная, остальные — строчные. Латиница — Latn. Кириллица — Cyrl. Арабская — Arab.
const names = new Intl.DisplayNames(["en"], { type: "script" });
console.log(names.of("Latn"));
// "Latin"
console.log(names.of("Cyrl"));
// "Cyrillic"
console.log(names.of("Arab"));
// "Arabic"
console.log(names.of("Hani"));
// "Han"
console.log(names.of("Hira"));
// "Hiragana"
console.log(names.of("Kana"));
// "Katakana"
Форматтер берёт на себя всю сложность поддержки переводов названий письменностей. Вам нужно только указать код письменности.
Основные коды письменностей
Стандарт ISO 15924 определяет коды для более чем 160 письменностей. Вот наиболее часто используемые коды.
const names = new Intl.DisplayNames(["en"], { type: "script" });
console.log(names.of("Latn"));
// "Latin"
console.log(names.of("Cyrl"));
// "Cyrillic"
console.log(names.of("Arab"));
// "Arabic"
console.log(names.of("Hebr"));
// "Hebrew"
console.log(names.of("Deva"));
// "Devanagari"
console.log(names.of("Thai"));
// "Thai"
console.log(names.of("Hani"));
// "Han"
console.log(names.of("Hans"));
// "Simplified Han"
console.log(names.of("Hant"));
// "Traditional Han"
console.log(names.of("Hang"));
// "Hangul"
console.log(names.of("Hira"));
// "Hiragana"
console.log(names.of("Kana"));
// "Katakana"
console.log(names.of("Beng"));
// "Bengali"
console.log(names.of("Grek"));
// "Greek"
Латиница используется для большинства западноевропейских языков. Кириллица охватывает русский, болгарский, украинский и другие славянские языки. Арабское письмо используется для арабского, персидского и урду. Иероглифы (хан) — для китайского, при этом Hans — для упрощённого китайского, а Hant — для традиционного. Хангыль — для корейского. Хирагана и катакана — японские системы письма.
Отображение названий письменностей в разных локалях
Названия письменностей локализуются в зависимости от языка интерфейса. Создайте форматтеры с разными локалями, чтобы увидеть названия на разных языках.
const enNames = new Intl.DisplayNames(["en"], { type: "script" });
const esNames = new Intl.DisplayNames(["es"], { type: "script" });
const frNames = new Intl.DisplayNames(["fr"], { type: "script" });
const jaNames = new Intl.DisplayNames(["ja"], { type: "script" });
console.log(enNames.of("Latn"));
// "Latin"
console.log(esNames.of("Latn"));
// "latino"
console.log(frNames.of("Latn"));
// "latin"
console.log(jaNames.of("Latn"));
// "ラテン文字"
Каждый форматтер возвращает название письменности на языке интерфейса. Это избавляет от необходимости вручную поддерживать переводы названий письменностей.
Такая схема работает для любого кода письменности.
const enNames = new Intl.DisplayNames(["en"], { type: "script" });
const deNames = new Intl.DisplayNames(["de"], { type: "script" });
const zhNames = new Intl.DisplayNames(["zh"], { type: "script" });
console.log(enNames.of("Cyrl"));
// "Cyrillic"
console.log(deNames.of("Cyrl"));
// "Kyrillisch"
console.log(zhNames.of("Cyrl"));
// "西里尔文"
console.log(enNames.of("Arab"));
// "Arabic"
console.log(deNames.of("Arab"));
// "Arabisch"
console.log(zhNames.of("Arab"));
// "阿拉伯文"
Форматтер автоматически применяет правильные языковые нормы для каждого языка.
Создание селектора письменности для языковых вариантов
В некоторых языках пользователи могут выбирать письменность. Например, сербский можно писать кириллицей или латиницей. Китайский — упрощёнными или традиционными иероглифами. Необходимо показывать эти варианты, чтобы пользователь мог выбрать.
function getScriptOptions(language, userLocale) {
const names = new Intl.DisplayNames([userLocale], { type: "script" });
if (language === "sr") {
return [
{ code: "Cyrl", name: names.of("Cyrl") },
{ code: "Latn", name: names.of("Latn") }
];
}
if (language === "zh") {
return [
{ code: "Hans", name: names.of("Hans") },
{ code: "Hant", name: names.of("Hant") }
];
}
return [];
}
console.log(getScriptOptions("sr", "en"));
// [
// { code: "Cyrl", name: "Cyrillic" },
// { code: "Latn", name: "Latin" }
// ]
console.log(getScriptOptions("zh", "en"));
// [
// { code: "Hans", name: "Simplified Han" },
// { code: "Hant", name: "Traditional Han" }
// ]
console.log(getScriptOptions("zh", "es"));
// [
// { code: "Hans", name: "han simplificado" },
// { code: "Hant", name: "han tradicional" }
// ]
Эта функция возвращает варианты письменности на языке интерфейса пользователя. Сербские пользователи увидят варианты для кириллицы и латиницы. Китайские — для упрощённых и традиционных иероглифов. Названия отображаются на понятном пользователю языке.
Отображение полных идентификаторов локалей с информацией о письменности
Идентификаторы локалей могут включать коды письменностей для различения систем письма. Формат — language-script-region, например, sr-Cyrl-RS для сербского на кириллице в Сербии или zh-Hans-CN для упрощённого китайского в Китае.
Когда вы отображаете эти идентификаторы локалей, извлекайте код письма и преобразуйте его в читаемое название.
function parseLocaleWithScript(locale) {
const parts = locale.split("-");
if (parts.length < 2) {
return null;
}
const [language, script] = parts;
if (script.length === 4) {
return {
language,
script: script.charAt(0).toUpperCase() + script.slice(1).toLowerCase()
};
}
return null;
}
function formatLocaleWithScriptName(locale, displayLocale) {
const parsed = parseLocaleWithScript(locale);
if (!parsed) {
return locale;
}
const languageNames = new Intl.DisplayNames([displayLocale], {
type: "language"
});
const scriptNames = new Intl.DisplayNames([displayLocale], { type: "script" });
const languageName = languageNames.of(parsed.language);
const scriptName = scriptNames.of(parsed.script);
return `${languageName} (${scriptName})`;
}
console.log(formatLocaleWithScriptName("sr-Cyrl", "en"));
// "Serbian (Cyrillic)"
console.log(formatLocaleWithScriptName("sr-Latn", "en"));
// "Serbian (Latin)"
console.log(formatLocaleWithScriptName("zh-Hans", "en"));
// "Chinese (Simplified Han)"
console.log(formatLocaleWithScriptName("zh-Hant", "en"));
// "Chinese (Traditional Han)"
console.log(formatLocaleWithScriptName("sr-Cyrl", "es"));
// "serbio (cirílico)"
Этот шаблон делает идентификаторы локалей понятными, объединяя названия языков с названиями систем письма. Пользователь видит «сербский (кириллица)» вместо «sr-Cyrl».
Создание селектора шрифтов с названиями систем письма
В интерфейсах выбора шрифтов часто группируют шрифты по поддерживаемым системам письма. Нужно показывать названия систем письма, чтобы пользователи понимали, какие шрифты поддерживают какие алфавиты.
function createFontOptions() {
const fonts = [
{
name: "Arial",
scripts: ["Latn", "Cyrl", "Grek", "Hebr", "Arab"]
},
{
name: "Noto Sans CJK",
scripts: ["Hans", "Hant", "Hira", "Kana", "Hang"]
},
{
name: "Noto Sans Devanagari",
scripts: ["Deva"]
}
];
const names = new Intl.DisplayNames(["en"], { type: "script" });
return fonts.map((font) => ({
name: font.name,
scripts: font.scripts.map((code) => names.of(code))
}));
}
console.log(createFontOptions());
// [
// {
// name: "Arial",
// scripts: ["Latin", "Cyrillic", "Greek", "Hebrew", "Arabic"]
// },
// {
// name: "Noto Sans CJK",
// scripts: ["Simplified Han", "Traditional Han", "Hiragana", "Katakana", "Hangul"]
// },
// {
// name: "Noto Sans Devanagari",
// scripts: ["Devanagari"]
// }
// ]
Это создаёт список шрифтов с поддерживаемыми системами письма в удобном для чтения виде. Пользователь может выбрать шрифт по нужной системе письма.
Отображение доступных методов ввода по системе письма
Операционные системы и браузеры предоставляют методы ввода для разных систем письма. Японский метод ввода преобразует латинские символы в хирагану, катакану или кандзи. Китайский метод ввода преобразует пиньинь в упрощённые или традиционные китайские иероглифы. Можно показывать доступные методы ввода с их названиями систем письма.
function getInputMethods(userLocale) {
const inputMethods = [
{ id: "latin-ime", script: "Latn" },
{ id: "japanese-ime", script: "Hira" },
{ id: "chinese-pinyin-simplified", script: "Hans" },
{ id: "chinese-pinyin-traditional", script: "Hant" },
{ id: "korean-ime", script: "Hang" },
{ id: "arabic-ime", script: "Arab" },
{ id: "hebrew-ime", script: "Hebr" }
];
const names = new Intl.DisplayNames([userLocale], { type: "script" });
return inputMethods.map((method) => ({
id: method.id,
name: names.of(method.script)
}));
}
console.log(getInputMethods("en"));
// [
// { id: "latin-ime", name: "Latin" },
// { id: "japanese-ime", name: "Hiragana" },
// { id: "chinese-pinyin-simplified", name: "Simplified Han" },
// { id: "chinese-pinyin-traditional", name: "Traditional Han" },
// { id: "korean-ime", name: "Hangul" },
// { id: "arabic-ime", name: "Arabic" },
// { id: "hebrew-ime", name: "Hebrew" }
// ]
console.log(getInputMethods("ja"));
// [
// { id: "latin-ime", name: "ラテン文字" },
// { id: "japanese-ime", name: "ひらがな" },
// { id: "chinese-pinyin-simplified", name: "簡体字" },
// { id: "chinese-pinyin-traditional", name: "繁体字" },
// { id: "korean-ime", name: "ハングル" },
// { id: "arabic-ime", name: "アラビア文字" },
// { id: "hebrew-ime", name: "ヘブライ文字" }
// ]
Это отображает названия методов ввода на языке пользователя. Например, пользователь видит «Hiragana» при английском интерфейсе и «ひらがな» при японском.
Как пишутся коды систем письма
Коды систем письма пишутся по определённому шаблону: первая буква — заглавная, остальные три — строчные. Latn — правильно. LATN, latn и LaTn — не по стандарту.
Метод of() принимает коды систем письма в любом регистре.
const names = new Intl.DisplayNames(["en"], { type: "script" });
console.log(names.of("Latn"));
// "Latin"
console.log(names.of("LATN"));
// "Latin"
console.log(names.of("latn"));
// "Latin"
console.log(names.of("LaTn"));
// "Latin"
Форматтер корректно обрабатывает все варианты. Но если использовать стандартный шаблон написания, код будет более читаемым и соответствовать ISO 15924.
Работа с резервными локалями
Конструктор Intl.DisplayNames принимает массив локалей. Если первая локаль недоступна, форматтер переходит к следующей в списке.
const names = new Intl.DisplayNames(["xx-XX", "en"], { type: "script" });
console.log(names.of("Latn"));
// "Latin"
Сначала форматтер пробует "xx-XX", которой нет, затем переходит к "en". Это гарантирует, что ваш код будет работать даже если нужная локаль недоступна.
Вы можете проверить, какую локаль фактически использует форматтер, с помощью метода resolvedOptions().
const names = new Intl.DisplayNames(["xx-XX", "en"], { type: "script" });
console.log(names.resolvedOptions().locale);
// "en"
Это показывает, что после перехода к резервной локали форматтер выбрал английский.
Создание многоязычной системы управления контентом
Системы управления контентом с поддержкой нескольких письменностей должны показывать, какие варианты доступны для каждого материала. Вы можете отображать названия письменностей, чтобы редакторы могли выбрать нужную версию.
function getContentVersions(contentId, userLocale) {
const versions = [
{ script: "Latn", url: `/content/${contentId}/latn` },
{ script: "Cyrl", url: `/content/${contentId}/cyrl` },
{ script: "Arab", url: `/content/${contentId}/arab` }
];
const names = new Intl.DisplayNames([userLocale], { type: "script" });
return versions.map((version) => ({
script: version.script,
name: names.of(version.script),
url: version.url
}));
}
console.log(getContentVersions("article-123", "en"));
// [
// { script: "Latn", name: "Latin", url: "/content/article-123/latn" },
// { script: "Cyrl", name: "Cyrillic", url: "/content/article-123/cyrl" },
// { script: "Arab", name: "Arabic", url: "/content/article-123/arab" }
// ]
Такой подход помогает редакторам видеть, какие версии по письменности существуют, и быстро между ними переключаться.
Поддержка браузеров
API Intl.DisplayNames с поддержкой типа письменности доступен во всех современных браузерах. С марта 2021 года его поддерживают основные браузеры: Chrome, Firefox, Safari и Edge.
Вы можете проверить доступность API перед его использованием.
if (typeof Intl.DisplayNames !== "undefined") {
const names = new Intl.DisplayNames(["en"], { type: "script" });
console.log(names.of("Latn"));
} else {
console.log("Intl.DisplayNames is not supported");
}
Для старых браузеров потребуется реализовать резервный вариант или использовать polyfill. Простой вариант — это жёстко заданное сопоставление кодов письменностей с их названиями.
function getScriptName(code, locale) {
if (typeof Intl.DisplayNames !== "undefined") {
const names = new Intl.DisplayNames([locale], { type: "script" });
return names.of(code);
}
const fallbackNames = {
Latn: "Latin",
Cyrl: "Cyrillic",
Arab: "Arabic",
Hans: "Simplified Han",
Hant: "Traditional Han"
};
return fallbackNames[code] || code;
}
console.log(getScriptName("Latn", "en"));
// "Latin"
Это позволит вашему коду работать даже в браузерах без поддержки Intl.DisplayNames, хотя автоматическая локализация будет недоступна.