Сложные случаи плюрализации

Механизм плюрализации в FormatJS основан на стандарте ICU MessageFormat и опирается на данные CLDR (Unicode Common Locale Data Repository), что обеспечивает корректное отображение форм множественного числа в зависимости от локали и числовых правил языка. В отличие от простых условных конструкций, здесь учитываются языковые категории, а не только арифметическая логика.

Формат сообщения строится вокруг конструкции plural, где ключом выступает числовое значение, а значениями — варианты строк для различных категорий:

{count, plural,
  one {# элемент}
  few {# элемента}
  many {# элементов}
  other {# элементов}
}

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


Категории множественного числа и языковая специфика

ICU использует набор категорий plural rules, определяемых CLDR:

  • zero
  • one
  • two
  • few
  • many
  • other

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

  • английский: one, other
  • русский: one, few, many, other
  • японский: только other

В русском языке логика распределения чисел следующая:

  • one → 1, 21, 31, 41…
  • few → 2–4, 22–24, 32–34…
  • many → 0, 5–20, 25–30…
  • other → используется для дробных чисел и исключений ICU

Эта модель исключает необходимость ручного ветвления в коде.


Точное совпадение чисел и приоритет правил

FormatJS позволяет задавать точные числовые совпадения через =:

{count, plural,
  =0 {Нет элементов}
  one {# элемент}
  few {# элемента}
  many {# элементов}
  other {# элементов}
}

Правило =0 имеет более высокий приоритет, чем языковые категории. Это позволяет переопределять стандартную логику ICU для конкретных значений.

Такая конструкция часто используется для улучшения UX при нулевых значениях, когда языковая форма становится вторичной по сравнению с семантикой сообщения.


Вложенные конструкции plural и select

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

{gender, select,
  male {{count, plural, one {Он добавил # элемент} other {Он добавил # элементов}}}
  female {{count, plural, one {Она добавила # элемент} other {Она добавила # элементов}}}
  other {{count, plural, one {Добавлено # элемент} other {Добавлено # элементов}}}
}

Такая структура демонстрирует каскадную зависимость:

  1. сначала выбирается грамматический род
  2. затем применяется правило плюрализации

Глубина вложенности может увеличиваться, однако рост сложности резко ухудшает читаемость и усложняет поддержку.


Selectordinal и порядковые числительные

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

{place, selectordinal,
  one {#-й}
  two {#-й}
  few {#-й}
  other {#-й}
}

В английском языке различие выглядит так:

  • 1st → one
  • 2nd → two
  • 3rd → few/other (в зависимости от правил)
  • 4th → other

FormatJS автоматически применяет соответствующие правила CLDR, исключая ручную обработку суффиксов.


Дробные числа и категория other

Особое поведение наблюдается при работе с дробями:

{count, plural,
  one {# элемент}
  other {# элементов}
}

Числа вроде 1.5, 2.3, 0.7 практически всегда попадают в категорию other, даже в языках с развитой системой множественных форм.

Это связано с тем, что plural rules CLDR разделяют целые и нецелые числа.


Неявные ошибки при неполном наборе категорий

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

{count, plural,
  one {# элемент}
}

При передаче значения, не соответствующего one, система автоматически использует other. Если other не определён, возникает ошибка форматирования или пустой вывод.

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


Динамические значения и интерполяция

Plural-конструкции часто содержат интерполяцию переменных:

{count, plural,
  one {Найден # результат по запросу "{query}"}
  other {Найдено # результатов по запросу "{query}"}
}

Здесь одновременно работают:

  • числовая подстановка #
  • строковая интерполяция {query}

Важно учитывать, что # автоматически заменяется на значение count, а не на произвольную переменную.


Комбинация plural с вложенными форматами

FormatJS поддерживает использование других форматтеров внутри plural-веток:

{count, plural,
  one {# элемент, обновлённый {updatedAt, date, short}}
  other {# элементов, обновлённых {updatedAt, date, short}}
}

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


Поведение при локалях с нестандартными правилами

Некоторые языки имеют нетривиальные правила, например:

  • арабский язык использует 6 категорий
  • словенский язык различает two, few, other
  • ирландский язык имеет отдельные формы для малых чисел

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


Порядок разрешения правил и приоритеты

При вычислении формы используется следующая логика:

  1. Проверка точных совпадений (=n)
  2. Вычисление категории plural rules
  3. Выбор fallback-ветки other

Если несколько веток теоретически подходят, всегда выбирается наиболее специфичная.


Использование Intl.PluralRules внутри FormatJS

Внутри реализации FormatJS применяется Intl.PluralRules, который предоставляет API уровня ECMAScript:

const rules = new Intl.PluralRules('ru-RU');
rules.select(5); // "many"

FormatJS расширяет эту логику, добавляя ICU MessageFormat синтаксис и интеграцию с шаблонами сообщений.


Ошибки при типизации и привязке данных

Типовая проблема возникает при передаче строк вместо чисел:

count: "5"

В этом случае поведение зависит от внутреннего преобразования, которое может привести к некорректной категории или fallback-ветке.

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

count: 5

Сложные композиции с несколькими plural-переменными

В реальных интерфейсах встречаются сообщения с несколькими числовыми параметрами:

{files, plural,
  one {# файл в # папке}
  other {# файлов в # папках}
}

При этом оба числа могут влиять на грамматику, что приводит к необходимости дублирования plural-конструкций или введения дополнительных переменных.


Граничные случаи и неоднозначные сценарии

Некоторые сценарии требуют особого внимания:

  • ноль как семантическое значение vs грамматическое значение
  • дробные числа в пользовательских счетчиках
  • большие числа с локализованным форматированием
  • отрицательные значения (обычно обрабатываются как other)

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


Производительность при большом количестве сообщений

При интенсивном использовании plural-конструкций в интерфейсе важную роль играет кеширование результатов Intl.PluralRules. FormatJS оптимизирует повторные вычисления, однако сложные вложенные сообщения могут увеличивать стоимость рендеринга.

Особенно это заметно при:

  • динамических списках
  • частых обновлениях счетчиков
  • реактивных интерфейсах с высокой частотой обновления состояния