"1 item" vs "2 items": Stop Writing That Ternary
Two little string problems, solved a thousand times per codebase, each with a native API that almost nobody has met.
Problem 1: pluralization is not count === 1
The English-brain ternary count === 1 ? 'item' : 'items' is wrong the moment you localize: Russian has three plural forms with rules about final digits, Arabic has six, French pluralizes zero differently. Intl.PluralRules encodes all of it:
const rules = new Intl.PluralRules('en');
rules.select(1); // "one"
rules.select(5); // "other"
const forms = {
one: '# comment',
other: '# comments',
};
function label(count, locale = navigator.language) {
const form = forms[new Intl.PluralRules(locale).select(count)];
return form.replace('#', new Intl.NumberFormat(locale).format(count));
}
label(1); // "1 comment"
label(1500); // "1,500 comments"
The categories (zero, one, two, few, many, other) are the CLDR standard - the same system every serious i18n framework uses internally. Even for an English-only site, select() beats the ternary because ordinals come free too: new Intl.PluralRules('en', { type: 'ordinal' }).select(23) gives "few" → "23rd". A ten-line suffix map replaces that cursed nth() function with the modulo checks.
Problem 2: joining lists like a person
tags.join(', '); // "php, css, javascript" - no "and", trailing awkwardness in UI copy
const list = new Intl.ListFormat('en', { style: 'long', type: 'conjunction' });
list.format(['php']); // "php"
list.format(['php', 'css']); // "php and css"
list.format(['php', 'css', 'jquery']); // "php, css, and jquery"
One, two and many items each get correct treatment - including the Oxford comma per locale convention, and proper "und"/"et"/"y" in other languages. type: 'disjunction' produces "or" lists ("Pay with card, PayPal, or bank transfer"), and style: 'narrow' gives compact joining for tight UI.
Where these show up in real interfaces
// "Anna, Ben, and 3 others liked this"
const names = list.format([...firstTwo, label(rest, 'other-person-form')]);
// Filter summaries: "Showing products in CSS, PHP, or jQuery"
summary.textContent = `Showing products in ${disjunction.format(activeFilters)}`;
The usual Intl advice applies: formatters are heavyweight to construct, cheap to use - build once per locale, reuse (same as RelativeTimeFormat and NumberFormat). Support is everywhere: PluralRules since 2018-ish, ListFormat everywhere since 2021.
Individually these are five-minute wins. Together with NumberFormat and RelativeTimeFormat, they mean the entire "assemble human-readable strings from data" layer needs no library and no hardcoded grammar - which is one less thing to rewrite the day the site gains a second language.
Full-stack web developer sharing practical tutorials and building tools that ship.
Got something on your mind?
My inbox is open - no forms disappearing into the void here.
- Just say hello Found a tutorial useful? Spotted a mistake? Tell me.
- Hire me for a project Have something custom in mind? Let's talk scope and timelines.
- Product support Bought something here? I'll help you get it running.
I usually reply within 1-2 business days.