В ICU MessageFormat механизм offset используется для
корректировки значения счётчика в plural-правилах без изменения
исходного числа в тексте. Это позволяет формировать более естественные
фразы, когда часть элементов уже явно названа в сообщении.
Ключевая идея:
offsetуменьшает значение переменной только внутри plural-логики, не влияя на отображаемое число.
Это особенно важно в сценариях, где первый элемент уже выведен отдельно, а остальные требуется корректно пересчитать.
В FormatJS (через ICU MessageFormat) offset задаётся внутри plural-блока:
{value, plural, offset:1
=0 {Нет элементов}
=1 {Только # элемент}
one {# элемент и ещё один}
other {# элемента и ещё # дополнительных}
}
Здесь:
value — исходное числоoffset:1 — вычитает 1 из значения при выборе формы# — подставляет скорректированное значение (value -
offset)Если задано:
value = 5
offset = 1
то внутри plural-блока используется:
effectiveValue = 5 - 1 = 4
Однако важно различать:
one, other, =n)
происходит на основе effectiveValue# также отображает effectiveValue, а не
исходное числоOffset применяется, когда первый элемент уже явно вынесен из списка.
Типичный пример: отображение списка участников, где один участник уже назван отдельно.
const message = `
{count, plural,
one {# участник}
few {# участника}
other {# участников}
}
`;
При count = 3:
3 участника
Но если один участник уже выведен отдельно, логика ломается: текст будет дублировать его.
const message = `
{name} и ещё {count, plural, offset:1
=0 {никого}
one {# человек}
other {# человека}
}
`;
При данных:
{
name: "Анна",
count: 3
}
Результат:
Анна и ещё 2 человека
Исходное значение: count = 3
2Выбор формы:
one → не подходит (2 ≠ 1)other → подходитПодстановка #:
# = 2Offset не отменяет возможность точных совпадений:
{count, plural, offset:1
=0 {только {name}}
=1 {{name} и ещё один человек}
other {{name} и ещё # человека}
}
При count = 1:
=0Результат:
только Анна
FormatJS сначала применяет offset, затем:
=n)Это важно учитывать, так как логика может неожиданно переключиться на точное совпадение.
Самый частый случай:
{user} и ещё {count, plural, offset:1
one {# участник}
other {# участников}
}
{leader} и ещё {members, plural, offset:1
=0 {никого}
one {# человек}
other {# человека}
}
У вас {notifications, plural, offset:0
=0 {нет новых уведомлений}
one {# новое уведомление}
few {# новых уведомления}
other {# новых уведомлений}
}
Здесь offset не используется, но сравнение полезно для понимания контраста: offset применяют только когда часть данных уже выведена.
Символ # всегда отображает уже скорректированное
значение.
Пример:
{count, plural, offset:2
other {осталось # элементов}
}
При count = 10:
осталось 8 элементов
{name} и ещё {count, plural, offset:1
one {# пользователь}
}
Проблема: отсутствует other, при значениях > 2
сообщение ломается.
Если нет вынесенного элемента, offset приводит к искажению числа.
{count, plural, offset:1
other {# элементов}
}
При count = 3 вывод будет 2, что логически
неверно.
=0 {ничего}
может перекрывать ожидаемую plural-ветку после offset.
Plural rules зависят от языка, но offset применяется одинаково — до определения формы.
Например:
one / otherone / few / many / otherOffset не изменяет правила языка, только входное значение.
Offset работает только внутри текущего plural-блока и не влияет на вложенные message-части.
{count, plural, offset:1
other {{
{name} и ещё # участников в группе
}}
}
Если после offset значение становится отрицательным, оно всё равно участвует в plural-логике:
count = 0
offset = 1
effectiveValue = -1
Выбор формы зависит от правил языка, но обычно такие ситуации требуют ручного контроля через exact-match.
other как базовый fallback#Offset можно рассматривать как трансформацию входного значения:
value → (value - offset) → plural rules → rendering
При этом исходное значение остаётся доступным вне plural-блока, а внутри используется только скорректированная версия.