|Labs
Book a DemoPlatform
React (Lingo Compiler)
Alpha
React (MCP)React (i18n)
CLI

Overview

  • @lingo.dev/react

Getting started

  • Quickstart

Reference

  • LingoProvider
  • useLingo
  • Plurals and select
  • Formatting

Plurals and select

Max PrilutskiyMax Prilutskiy·Updated 8 days ago·3 min read

Plurals and select forms are the cases where one source string isn't enough — the translation depends on a number or a category. @lingo.dev/react exposes two friendly helpers (l.plural and l.select) that compile to ICU MessageFormat under the hood, so translators see the standard syntax and runtime stays the same.

Plurals#

l.plural(count, forms, { context }) picks the right form based on count and the locale's CLDR plural rules.

tsx
const l = useLingo();

l.plural(items.length, {
  one: "1 item",
  other: "{count} items",
}, { context: "Cart summary" });
// → "1 item" (en, count=1) / "5 items" (en, count=5)
// → "1 Eintrag" / "5 Einträge" (de, after translation)

Forms by locale#

The forms map accepts every CLDR plural category — zero, one, two, few, many, other. Locales pick what they need:

  • English uses one + other (1 vs everything else)
  • Russian uses one + few + many + other (1; 2-4; 5-20; 21, 31, ...)
  • Arabic uses all six
  • Japanese uses only other (no plural distinction)

You only need to provide the forms the source locale uses — translators add the rest per target locale.

{count} is interpolated automatically inside any plural form. You don't pass it via values — it comes from the first argument.

Combining with other placeholders#

For sentences with both a count and other variables, write the variables into the form strings; they'll be passed through to ICU.

tsx
l.plural(notifications.length, {
  one: "1 message from {sender}",
  other: "{count} messages from {sender}",
}, { context: "Inbox header" });

Then pass the values when you call — but wait, l.plural's signature only has { context }. For mixed cases, use l.text directly with ICU plural syntax:

tsx
l.text(`{count, plural, one {1 message from {sender}} other {# messages from {sender}}}`, {
  values: { count: notifications.length, sender: user.name },
  context: "Inbox header",
});

The # token is replaced with the count value verbatim — useful when you want it without the curly-brace interpolation form.

Select#

l.select(value, forms, { context }) picks a form based on a string key (gender, role, content type — anything categorical).

tsx
l.select(user.gender, {
  male: "He uploaded a photo",
  female: "She uploaded a photo",
  other: "They uploaded a photo",
}, { context: "Activity feed" });

other is required as a fallback. The match is exact — there's no fuzzy or case-insensitive matching.

Selectordinal#

For ordinal numbers (1st, 2nd, 3rd) use ICU selectordinal directly via l.text:

tsx
l.text(`You finished in {place, selectordinal, one {#st} two {#nd} few {#rd} other {#th}} place`, {
  values: { place: rank },
  context: "Leaderboard",
});
// → "You finished in 1st place" / "2nd" / "3rd" / "4th, 5th, ..."

What this compiles to#

Both l.plural and l.select build an ICU MessageFormat string and pass it to l.text. The compiled form is what gets extracted by lingo extract and stored in your locale files — translators edit ICU syntax directly, not the JS object literal.

Example: l.plural(n, { one: "1 item", other: "{count} items" }, { context: "Cart" }) extracts as:

text
{count, plural, one {1 item} other {{count} items}}

This means translators can adapt the categories per locale, including ones the source doesn't have. Russian becomes {count, plural, one {...} few {...} many {...} other {...}} without any code change.

When not to use these#

  • A simple "1 or many" boolean. Two l.text calls under an if are fine and easier for translators to spot.
  • Programmatic enum that's not user-facing. Plural / select are for translation of categorical messages, not for routing app logic.

Where to next#

  • useLingo — base l.text and l.rich semantics.
  • Formatting — number, currency, date, list formatting via native Intl.

Was this page helpful?