Названия письменностей

Общая модель представления письменностей

В Unicode и экосистеме интернационализации письменность (script) рассматривается как отдельный уровень языковой идентификации, независимый от языка и региона. Письменность описывает систему графических символов, используемых для записи текста: латиница, кириллица, арабское письмо, деванагари и другие.

В JavaScript работа с названиями письменностей стандартизирована через Intl.DisplayNames, где тип данных script позволяет получать локализованные человекочитаемые названия письменностей по их ISO-кодам (ISO 15924).

Ключевая особенность модели:

  • язык ≠ письменность ≠ регион
  • одна письменность может использоваться множеством языков
  • один язык может использовать несколько письменностей

Пример:

  • сербский язык: латиница и кириллица
  • японский язык: хирагана, катакана, кандзи (смешанное письмо)
  • китайский язык: хань (упрощённые и традиционные формы как варианты письменности)

ISO 15924 и коды письменностей

Intl API опирается на стандарт ISO 15924, где каждая письменность обозначается четырёхбуквенным кодом:

Код Письменность
Latn латиница
Cyrl кириллица
Arab арабское письмо
Deva деванагари
Hans упрощённые китайские иероглифы
Hant традиционные китайские иероглифы
Jpan японское письмо
Kore корейское письмо

Эти коды используются как входные значения для Intl.DisplayNames.


Intl.DisplayNames и тип script

Основной механизм получения названий письменностей реализован через конструктор:

const scriptNames = new Intl.DisplayNames('ru', { type: 'script' });

Первый аргумент задаёт локаль вывода, второй — тип отображаемых данных.

Базовый принцип работы

scriptNames.of('Latn'); // "латиница"
scriptNames.of('Cyrl'); // "кириллица"
scriptNames.of('Arab'); // "арабское письмо"

Метод of() принимает код письменности ISO 15924 и возвращает локализованное название.


Локализация названий письменностей

Одним из ключевых свойств Intl.DisplayNames является зависимость результата от локали.

const ru = new Intl.DisplayNames('ru', { type: 'script' });
const en = new Intl.DisplayNames('en', { type: 'script' });

ru.of('Cyrl'); // "кириллица"
en.of('Cyrl'); // "Cyrillic"

Таким образом, один и тот же код может давать разные строковые представления в зависимости от языка интерфейса.


Поддерживаемые сценарии использования

Интерфейсы выбора языка и письма

При построении UI для выбора языка часто требуется уточнение письменности:

const dn = new Intl.DisplayNames('ru', { type: 'script' });

const scripts = ['Latn', 'Cyrl', 'Arab'];

const labels = scripts.map(code => ({
  code,
  label: dn.of(code)
}));

Результат:

[
  { "code": "Latn", "label": "латиница" },
  { "code": "Cyrl", "label": "кириллица" },
  { "code": "Arab", "label": "арабское письмо" }
]

Различение вариантов письменности в языках

Некоторые языки требуют указания письменности для корректного отображения:

const dn = new Intl.DisplayNames('en', { type: 'script' });

dn.of('Hans'); // "Simplified Han"
dn.of('Hant'); // "Traditional Han"

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


Связь с языковыми тегами Unicode

Письменность часто используется внутри языковых тегов BCP 47:

zh-Hans  → китайский (упрощённая письменность)
zh-Hant  → китайский (традиционная письменность)
sr-Cyrl  → сербский (кириллица)
sr-Latn  → сербский (латиница)

Однако Intl.DisplayNames работает только с кодом письменности, без полного языкового тега:

const dn = new Intl.DisplayNames('ru', { type: 'script' });

dn.of('Latn'); // корректно
dn.of('sr-Latn'); // некорректно (ожидается только script code)

Поведение при неизвестных значениях

Если передан неизвестный или неподдерживаемый код, поведение стандартизировано:

const dn = new Intl.DisplayNames('ru', { type: 'script' });

dn.of('XXXX'); // обычно возвращает undefined

Это требует обработки на уровне приложения:

const value = dn.of(code) ?? 'неизвестная письменность';

Кэширование экземпляров DisplayNames

Создание Intl.DisplayNames — относительно тяжёлая операция. В производственных приложениях используется кэширование:

const scriptDisplayCache = new Map();

function getScriptDisplay(locale) {
  if (!scriptDisplayCache.has(locale)) {
    scriptDisplayCache.set(
      locale,
      new Intl.DisplayNames(locale, { type: 'script' })
    );
  }
  return scriptDisplayCache.get(locale);
}

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

const dn = getScriptDisplay('ru');

dn.of('Cyrl');

Сравнение письменностей в интерфейсах

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

const dn = new Intl.DisplayNames('ru', { type: 'script' });

const data = [
  { lang: 'sr', script: 'Cyrl' },
  { lang: 'sr', script: 'Latn' }
];

data.map(item => ({
  label: `${item.lang} (${dn.of(item.script)})`
}));

Результат:

  • sr (кириллица)
  • sr (латиница)

Ограничения и особенности реализации

1. Зависимость от окружения

Поддержка конкретных письменностей зависит от реализации JavaScript-движка и ICU-библиотеки. В разных средах список доступных локализаций может отличаться.

2. Не все коды обязаны поддерживаться

Хотя ISO 15924 содержит большое количество письменностей, браузер может возвращать fallback-значения или исходный код.

3. Отсутствие морфологии

Intl.DisplayNames возвращает статическое имя без грамматических форм:

  • нет склонений
  • нет контекста (например, «на кириллице» vs «кириллица»)

Взаимодействие с другими частями Intl API

Письменности часто используются вместе с:

  • Intl.Locale — для разбора языковых тегов
  • Intl.Collator — для сортировки текста в зависимости от письменности
  • Intl.Segmenter — для сегментации текста в разных системах письма

Пример комбинирования:

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

const script = locale.script; // "Cyrl"

const dn = new Intl.DisplayNames(locale.toString(), { type: 'script' });

dn.of(script); // "кириллица"

Практическое значение письменностей в UI-архитектуре

Письменности становятся критичными в системах, где:

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

Особенно это заметно в:

  • глобальных CMS
  • переводческих платформах
  • многоязычных редакторах
  • операционных системах и клавиатурных раскладках

Структура данных и нормализация

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

function normalizeScript(code) {
  return code.trim().toUpperCase();
}

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

  • Latn (правильно)
  • LATN (не гарантируется всеми API)

Обработка списков письменностей

В реальных системах часто используется массив кодов:

const scripts = ['Latn', 'Cyrl', 'Arab', 'Deva'];

const dn = new Intl.DisplayNames('ru', { type: 'script' });

const result = scripts.reduce((acc, code) => {
  acc[code] = dn.of(code);
  return acc;
}, {});

Результат:

{
  "Latn": "латиница",
  "Cyrl": "кириллица",
  "Arab": "арабское письмо",
  "Deva": "деванагари"
}

Использование в многоуровневой локализации

Письменность часто становится третьим уровнем локализации:

  1. язык (ru, en, zh)
  2. регион (RU, US, CN)
  3. письменность (Cyrl, Latn, Hans)
const locale = new Intl.Locale('zh-Hans-CN');

locale.language; // "zh"
locale.script;    // "Hans"
locale.region;    // "CN"

В таких структурах Intl.DisplayNames используется для человекочитаемого отображения каждого уровня независимо.