Особенности различных алфавитов

Работа с текстом в JavaScript неизбежно опирается на Unicode — универсальную систему кодирования, в которой каждый символ любого языка представлен отдельным кодовым пунктом или последовательностью кодовых пунктов. Однако визуальное и лингвистическое поведение символов далеко не всегда совпадает с их внутренним представлением.

Разные письменности предъявляют разные требования к обработке строк: сортировка, сравнение, разбиение на слова и даже отображение зависят от языка и культурных правил. Именно для этого используется Intl API, предоставляющий стандартизированные механизмы локализованной обработки текста.


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

Встроенные операции JavaScript для строк (===, <, >, localeCompare) не учитывают полноценные лингвистические особенности. Например:

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

Ключевая проблема: строка как последовательность байтов не равна строке как лингвистической единице.


Латиница и особенности алфавитной нормализации

Латинская письменность используется во множестве языков, и даже внутри неё существуют значимые различия.

Диакритические знаки

Символы вроде é, ü, ñ могут быть представлены:

  • как единый символ (precomposed form),
  • как базовая буква + комбинирующий знак.

Например:

  • é → U+00E9
  • e + ́ → U+0065 U+0301

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

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

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

  • NFC (Canonical Composition)
  • NFD (Canonical Decomposition)

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


Кириллица и языковая специфика сортировки

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

Примеры особенностей:

  • наличие букв, отсутствующих в других алфавитах (например, «ё», «ї», «ң»);
  • различие в официальном алфавитном порядке;
  • влияние исторических и типографических традиций.

Стандартная сортировка JavaScript не учитывает эти различия, поэтому используется:

Intl.Collator

const collator = new Intl.Collator('ru');
collator.compare('ёж', 'яблоко');

Collator применяет языковые правила русского языка, а не бинарное сравнение кодов символов.


Арабская письменность и направление текста

Арабская письменность имеет фундаментальные отличия от латиницы и кириллицы:

  • направление письма справа налево (RTL);
  • контекстные формы букв (одна буква имеет несколько графических вариантов);
  • отсутствие заглавных букв.

Проблемы сравнения

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

  • лигатур;
  • управляющих символов;
  • особенностей рендеринга.

Intl.Collator и арабские локали

const collator = new Intl.Collator('ar');
collator.compare('كتاب', 'كتب');

Collator учитывает арабские правила сортировки, которые не совпадают с Unicode-порядком.


CJK-письменности: китайский, японский, корейский

Системы письма CJK представляют наибольшую сложность для алгоритмической обработки.

Китайский язык

  • тысячи иероглифов;
  • отсутствие алфавитного порядка в привычном смысле;
  • сортировка может основываться на пиньине или количестве черт.

Японский язык

  • сочетание кандзи, хираганы и катаканы;
  • несколько систем упорядочивания.

Корейский язык

  • слоговая система хангыль;
  • сложная композиция символов из джамо.

Поведение Intl.Collator

const collator = new Intl.Collator('zh');
collator.compare('北京', '上海');

Результат зависит от выбранной локали и параметров сортировки (например, stroke-based или pinyin-based сортировка в системах, поддерживающих расширенные правила).


Локаль как основа языковых правил

Intl.Locale позволяет описывать язык и региональные особенности:

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

Локаль влияет на:

  • порядок сортировки;
  • правила сравнения строк;
  • обработку регистра;
  • календарные и числовые форматы.

Intl.Collator: управление сравнением строк

Основной инструмент для работы с различными алфавитами.

Основные параметры

  • locale — язык сравнения
  • sensitivity — чувствительность сравнения
  • ignorePunctuation — игнорирование знаков препинания
  • numeric — числовая сортировка

Уровни чувствительности

  • base — игнорирует диакритику и регистр
  • accent — учитывает диакритику
  • case — учитывает регистр
  • variant — максимальная точность

Пример:

const collator = new Intl.Collator('fr', {
  sensitivity: 'base'
});

collator.compare('e', 'é'); // считается равным

Диакритика и её влияние на сравнение

Диакритические знаки особенно важны для европейских языков:

  • французский: é, è, ê
  • немецкий: ä, ö, ü
  • испанский: ñ

В зависимости от локали:

  • base игнорирует различия;
  • accent учитывает их как значимые.

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

В латинице и кириллице регистр играет важную роль, но:

  • в арабском регистр отсутствует;
  • в CJK регистр не используется;
  • в некоторых системах письма регистр имеет только типографическое значение.
const collator = new Intl.Collator('en', {
  sensitivity: 'case'
});

collator.compare('Apple', 'apple');

Разбиение текста с учётом письменности

Intl.Segmenter используется для корректного разбиения строк:

const segmenter = new Intl.Segmenter('ja', { granularity: 'word' });

Особенности:

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

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

Intl.DisplayNames позволяет получать локализованные названия языков и систем письма:

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

dn.of('ar'); // "арабский"
dn.of('ja'); // "японский"

Это важно при отображении интерфейсов, поддерживающих множество алфавитов.


Сравнение строк в мультиалфавитной среде

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

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

Пример:

const collator = new Intl.Collator(['ru', 'en'], {
  numeric: true,
  sensitivity: 'base'
});

Поведение Unicode в зависимости от языка

Unicode определяет кодовые точки, но не определяет:

  • порядок сортировки;
  • правила равенства строк;
  • языковую значимость символов.

Эти задачи полностью передаются уровню Intl API, который использует CLDR (Common Locale Data Repository) для определения правил.


Особенности сравнения в реальных сценариях

Поиск

При поиске строк важно:

  • игнорировать регистр;
  • учитывать диакритику в зависимости от языка;
  • нормализовать Unicode.
const collator = new Intl.Collator('de', {
  sensitivity: 'base'
});

Сортировка списков

const items.sort((a, b) => collator.compare(a, b));

В разных локалях один и тот же список будет упорядочен по-разному.


Комбинированные символы и визуальная эквивалентность

Unicode допускает множество способов представления одного символа:

  • precomposed
  • decomposed
  • с вариативными селекторами
  • с управляющими символами

Это приводит к тому, что:

  • строки могут выглядеть одинаково;
  • но иметь разное бинарное представление;
  • и давать разные результаты сравнения.

Закономерности поведения Intl API при работе с алфавитами

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