В экосистеме FormatJS условные конструкции строятся на базе ICU
MessageFormat. Это декларативный синтаксис, позволяющий описывать
ветвления прямо внутри строк сообщений без необходимости писать
императивную логику в коде. Основные механизмы включают
select, plural, selectordinal, а
также их вложенные комбинации.
Ключевая особенность подхода заключается в том, что сообщение становится функцией от набора параметров, а выбор ветки определяется движком форматирования во время выполнения.
Конструкция select применяется для дискретного выбора
варианта строки на основе значения переменной. Чаще всего используется
для пола, статусов или категорий.
Синтаксис:
{variable, select,
option1 {текст 1}
option2 {текст 2}
other {значение по умолчанию}
}
Пример использования в FormatJS:
import { createIntl, createIntlCache } from 'react-intl';
const cache = createIntlCache();
const intl = createIntl({
locale: 'ru',
messages: {
greeting: '{gender, select, male {Он пришёл} female {Она пришла} other {Они пришли}}',
},
cache,
});
intl.formatMessage({ id: 'greeting' }, { gender: 'male' });
Поведение определяется точным совпадением ключа. Если значение не
совпадает ни с одной веткой, используется other.
Сложные сообщения часто требуют комбинации нескольких параметров. В
таких случаях допустимо вложение select внутри других
конструкций.
Пример комбинирования пола и статуса:
{gender, select,
male {
{status, select,
active {Он активен}
inactive {Он неактивен}
other {Статус неизвестен}
}
}
female {
{status, select,
active {Она активна}
inactive {Она неактивна}
other {Статус неизвестен}
}
}
other {
{status, select,
active {Активен пользователь}
inactive {Неактивен пользователь}
other {Статус неизвестен}
}
}
}
Такая структура быстро становится громоздкой, но остаётся полностью валидной в ICU MessageFormat. Основная проблема заключается не в синтаксисе, а в читаемости и поддерживаемости.
Конструкция plural учитывает правила множественных форм
языка, что особенно важно для языков с несколькими формами
множественного числа.
Синтаксис:
{count, plural,
one {# элемент}
few {# элемента}
many {# элементов}
other {# элемента}
}
Пример в FormatJS:
intl.formatMessage(
{
id: 'cart.items',
defaultMessage: '{count, plural, one {# товар} few {# товара} many {# товаров} other {# товара}}',
},
{ count: 5 }
);
ICU автоматически применяет правила CLDR для выбранной локали, что
снимает необходимость ручного определения условий вроде
count === 1.
Параметр offset используется для изменения базового
значения при формировании текста, особенно в сценариях типа «вы и ещё N
пользователей».
Пример:
{count, plural, offset:1
one {Вы присоединились к событию}
other {Вы и # человек присоединились к событию}
}
Логика работы:
count уменьшается на offset# отображает скорректированное значениеЭто позволяет избегать дополнительных вычислений в коде приложения.
Конструкция selectordinal используется для порядковых
чисел (1-й, 2-й, 3-й и т.д.).
{place, selectordinal,
one {#-й}
two {#-й}
few {#-й}
other {#-й}
}
Пример:
intl.formatMessage(
{
id: 'rating.position',
defaultMessage: '{place, selectordinal, one {#-й} two {#-й} few {#-й} other {#-й}}',
},
{ place: 3 }
);
В реальных проектах чаще применяется с локализованными правилами языка, так как порядок категорий зависит от CLDR.
Сложные интерфейсные строки часто требуют одновременного учёта чисел и категорий.
Пример уведомления:
{gender, select,
male {{
count, plural,
one {Он удалил # файл}
few {Он удалил # файла}
many {Он удалил # файлов}
other {Он удалил # файла}
}}
female {{
count, plural,
one {Она удалилa # файл}
few {Она удалилa # файла}
many {Она удалилa # файлов}
other {Она удалилa # файла}
}}
other {{
count, plural,
one {Пользователь удалил # файл}
few {Пользователь удалил # файла}
many {Пользователь удалил # файлов}
other {Пользователь удалил # файла}
}}
}
Подобные конструкции демонстрируют главную проблему ICU-подхода: экспоненциальный рост ветвлений при увеличении количества параметров.
Во всех конструкциях ICU наличие ветки other является
обязательной практикой. Она выполняет роль защиты от:
Пример:
{status, select,
success {Операция завершена}
error {Ошибка выполнения}
pending {Ожидание}
other {Неизвестный статус}
}
Отсутствие other может приводить к возврату исходного
шаблона без форматирования.
ICU MessageFormat использует фигурные скобки как синтаксическую основу, поэтому требуется экранирование в ряде случаев.
Основные правила:
{} используются для выражений'Пример:
{count, plural,
one {# элемент}
other {'# элементов в списке'}
}
Кавычки защищают содержимое от интерпретации как ICU-синтаксис.
При увеличении количества параметров возникает необходимость структурирования сообщений. Типичный пример проблемной конструкции:
{role, select,
admin {
{active, select,
true {Администратор активен}
false {Администратор неактивен}
other {Статус неизвестен}
}
}
user {
{active, select,
true {Пользователь активен}
false {Пользователь неактивен}
other {Статус неизвестен}
}
}
other {
Статус неизвестен
}
}
Такие выражения усложняют сопровождение и приводят к необходимости внешней декомпозиции логики.
FormatJS предполагает, что бизнес-логика должна быть вынесена из сообщений, а ICU используется только для отображения вариантов текста.
Пример разделения:
const key = user.role === 'admin' ? 'adminMessage' : 'userMessage';
intl.formatMessage({ id: key }, { active: user.active });
И соответствующие простые сообщения:
adminMessage: 'Администратор {active, select, true {активен} false {неактивен} other {неизвестно}}'
userMessage: 'Пользователь {active, select, true {активен} false {неактивен} other {неизвестно}}'
Такой подход снижает вложенность ICU и упрощает локализацию.
Одна из особенностей ICU заключается в возможности многократного использования одного и того же параметра в разных частях строки:
{count, plural,
one {Найден # результат}
few {Найдено # результата}
many {Найдено # результатов}
other {Найдено # результата}
}, обновлено {count, number}
Это позволяет комбинировать условные и форматирующие выражения без дублирования данных.
При использовании FormatJS с глубоко вложенными ICU-конструкциями возникают характерные ограничения:
ICU не предоставляет механизмов абстракции или повторного использования блоков внутри сообщений, поэтому масштабирование достигается через разбиение сообщений на более мелкие единицы.
FormatJS (например, через intl.formatMessage) выполняет
следующие этапы:
select и pluralПри этом дерево сообщений кэшируется, что снижает стоимость повторного форматирования.
В практических системах применяются следующие подходы:
selectПример упрощения:
const messageId = `${role}.${status}`;
intl.formatMessage({ id: messageId });
Такой подход заменяет вложенные условия плоской структурой ключей.
FormatJS позволяет сочетать условные конструкции с форматированием чисел, дат и валют:
{count, plural,
one {На балансе {amount, number, currency} и # операция}
other {На балансе {amount, number, currency} и # операций}
}
Это обеспечивает единый синтаксис для разных типов данных внутри одной строки сообщения.