Деление на предложения

Общая модель сегментации текста

Современные задачи обработки текста в JavaScript требуют учета языковых особенностей, выходящих за рамки простого разбиения строки по символу точки. Пунктуация, сокращения, многоточия, аббревиатуры и различия между языками делают наивные алгоритмы нестабильными. Intl API предоставляет стандартизированный механизм сегментации текста через Intl.Segmenter, где одним из ключевых режимов является разбиение на предложения.

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

Ключевой механизм: Intl.Segmenter с параметром granularity: "sentence"


Intl.Segmenter и режим sentence

Базовая инициализация

Сегментатор создаётся с указанием локали и уровня детализации:

const segmenter = new Intl.Segmenter("ru", { granularity: "sentence" });
  • "ru" — локаль, влияющая на правила определения границ предложений
  • granularity: "sentence" — режим разбиения текста на предложения

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


Механизм работы сегментации предложений

Сегментатор не возвращает готовый массив строк. Вместо этого он выдаёт итерируемую последовательность сегментов, где каждый элемент содержит:

  • сам фрагмент текста
  • индекс начала сегмента
  • индекс конца сегмента
const text = "Привет! Как дела? Всё работает корректно.";

const segments = segmenter.segment(text);

for (const s of segments) {
  console.log(s.segment, s.index, s.input);
}

Каждый элемент представляет одно предложение или его логически завершённую часть.


Особенности определения границ предложений

Пунктуация и контекст

Разбиение не сводится к поиску символов ., !, ?. Используются правила Unicode Text Segmentation:

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

Пример неоднозначного случая:

const text = "Он сказал: «Привет. Как дела?» и ушёл.";

Результат корректной сегментации:

  • «Он сказал: «Привет. Как дела?» и ушёл.» или
  • разделение на два предложения с учетом кавычек и контекста (в зависимости от реализации и версии ICU)

Локализация и различия языков

Разные языки используют разные правила завершения предложений.

Русский язык

  • точка, восклицательный и вопросительный знак считаются основными разделителями
  • сокращения (например, «т.д.», «и т.п.») не всегда разрывают предложение

Английский язык

const segmenterEn = new Intl.Segmenter("en", { granularity: "sentence" });

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

  • более строгая обработка сокращений (Mr., Dr., etc.)
  • иное поведение кавычек и цитирования

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

const segmenterJa = new Intl.Segmenter("ja", { granularity: "sentence" });
  • предложения могут не содержать пробелов
  • используется специфическая пунктуация (。!?)

Итерация по сегментам

Результат работы сегментера является ленивым итератором:

const text = "Первое предложение. Второе предложение. Третье.";

const segments = [...segmenter.segment(text)];

console.log(segments);

Каждый элемент:

{
  segment: "Первое предложение.",
  index: 0,
  input: "Первое предложение. Второе предложение. Третье."
}

Извлечение только предложений

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

const sentences = [];

for (const { segment } of segmenter.segment(text)) {
  sentences.push(segment);
}

Итоговый массив содержит список предложений, корректно выделенных по языковым правилам.


Работа с индексами

Индексы позволяют выполнять точное позиционирование в исходной строке:

for (const s of segmenter.segment(text)) {
  console.log(text.slice(s.index, s.index + s.segment.length));
}

Это важно при:

  • подсветке текста
  • разметке интерфейсов
  • синхронизации с редакторами

Обработка сложных конструкций

Многоточия

const text = "Я не уверен... возможно, это так.";

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


Аббревиатуры

const text = "Это случилось в г. Москве. Потом всё изменилось.";

Сокращение «г.» не должно разрывать предложение.


Цитаты

const text = "Он сказал: \"Я приду завтра.\" И ушёл.";

Закрывающие кавычки влияют на определение конца предложения, особенно в английской локали.


Отличие от наивных методов разбиения

Типичный подход:

text.split(".")

Проблемы:

  • ломает аббревиатуры
  • не учитывает ! и ?
  • игнорирует локализацию
  • не работает с многоточиями

Intl Segmenter решает эти проблемы за счёт использования ICU правил.


Производительность и внутренние особенности

Intl.Segmenter основан на ICU (International Components for Unicode), что обеспечивает:

  • оптимизированные алгоритмы сегментации
  • единое поведение в разных средах (Node.js, браузеры)
  • минимизацию ошибок ручной логики

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


Композиция с другими возможностями Intl

Сегментация предложений часто используется вместе с:

  • Intl.DateTimeFormat для локализованных текстов с датами
  • Intl.NumberFormat для вставки чисел в текст
  • Intl.PluralRules для грамматически корректных форм

Пример комбинированной обработки текста:

const formatter = new Intl.NumberFormat("ru");

const text = `У вас 5 сообщений. Их нужно прочитать.`;

const segmenter = new Intl.Segmenter("ru", { granularity: "sentence" });

for (const { segment } of segmenter.segment(text)) {
  console.log(segment);
}

Ограничения модели сегментации

  • поведение зависит от версии ICU в окружении
  • возможны различия между браузерами и Node.js
  • сложные случаи с вложенными кавычками могут трактоваться по-разному
  • нет явного управления правилами — только выбор локали

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