Domphy

Pluralization

Basic plural forms

Pass the count interpolation variable — @domphy/i18n maps it to the correct plural form for the active locale:

// translations/en.json
{
  "items": "{{count}} item",
  "items_other": "{{count}} items",
  "notifications": "{{count}} notification",
  "notifications_other": "{{count}} notifications"
}
import { createI18n } from "@domphy/i18n/domphy"

const { t } = createI18n({
  locale: "en",
  messages: { en: () => import("./translations/en.json") },
})

// Usage
t("items", { count: 1 })    // "1 item"
t("items", { count: 5 })    // "5 items"
t("items", { count: 0 })    // "0 items"

Plural key suffixes

Different locales use different numbers of plural forms. The count variable selects the right key:

SuffixUsed when
_zerocount = 0 (Arabic, Czech, etc.)
_onecount = 1 (English, German, etc.)
_twocount = 2 (Arabic, Hebrew)
_fewcount = 3–4 (Slavic languages)
_manylarge numbers (Russian, Polish)
_otherdefault / fallback

English only needs _one and _other:

{
  "apples_one":   "{{count}} apple",
  "apples_other": "{{count}} apples"
}

Russian needs four forms:

{
  "apples_one":   "{{count}} яблоко",
  "apples_few":   "{{count}} яблока",
  "apples_many":  "{{count}} яблок",
  "apples_other": "{{count}} яблока"
}

@domphy/i18n uses the CLDR plural rules for each locale — you do not need to implement the rules yourself.

Zero state

Show a special message when count is zero:

{
  "files_zero":  "No files",
  "files_one":   "{{count}} file",
  "files_other": "{{count}} files"
}
t("files", { count: 0 })   // "No files"
t("files", { count: 1 })   // "1 file"
t("files", { count: 99 })  // "99 files"

Ordinals

For ordinal numbers ("1st", "2nd", "3rd"), use the ordinal: true option:

{
  "rank_one":   "{{count}}st",
  "rank_two":   "{{count}}nd",
  "rank_few":   "{{count}}rd",
  "rank_other": "{{count}}th"
}
t("rank", { count: 1, ordinal: true })   // "1st"
t("rank", { count: 2, ordinal: true })   // "2nd"
t("rank", { count: 3, ordinal: true })   // "3rd"
t("rank", { count: 11, ordinal: true })  // "11th"

Combining plural with other interpolation

{
  "userFiles_one":   "{{user}} has {{count}} file",
  "userFiles_other": "{{user}} has {{count}} files"
}
t("userFiles", { count: 3, user: "Alice" })   // "Alice has 3 files"

Reactive pluralization

Plurals update reactively when count state changes:

import { toState } from "@domphy/core"

const itemCount = toState(0)

const Counter = {
  div: [
    { button: "−", onClick: () => itemCount.set((n) => Math.max(0, n - 1)) },
    { span: (l) => t("items", { count: itemCount.get(l) }) },
    { button: "+", onClick: () => itemCount.set((n) => n + 1) },
  ],
}

Intl.PluralRules directly

For edge cases, use the browser's built-in Intl.PluralRules:

const pr = new Intl.PluralRules("en")
pr.select(0)   // "other"
pr.select(1)   // "one"
pr.select(2)   // "other"

const prRu = new Intl.PluralRules("ru")
prRu.select(1)    // "one"   → яблоко
prRu.select(3)    // "few"   → яблока
prRu.select(11)   // "many"  → яблок