DEV Community

Ade Adeola
Ade Adeola

Posted on

Formatting Strings in JavaScript: Sorting, Plurals & Lists

Ever struggled with sorting words in different languages, handling tricky pluralization rules, or formatting lists in a way that sounds natural? The final parts of the Intl API has some hidden gems that focuses on handling strings and lists.

TLDR

We will be focusing on three powerful but often overlooked features:

Intl.Collator for sorting and comparing strings correctly.
Intl.PluralRules for handling pluralization dynamically.
Intl.ListFormat to format lists naturally and conjugation.


Intl.Collator

Sorting strings isn’t as straightforward as it seems. Different languages have different rules for alphabetical order, case sensitivity, and accents. That’s where Intl.Collator comes in!

Basic Sorting

const collator = new Intl.Collator("en", { sensitivity: "base" });

console.log(collator.compare("apple", "Banana")); // -1 (apple comes before Banana)
console.log(collator.compare("banana", "apple")); // 1 (banana comes after apple)
console.log(collator.compare("apple", "apple")); // 0 (they are equal)
Enter fullscreen mode Exit fullscreen mode

Here, { sensitivity: 'base' } makes the comparison case-insensitive, so "apple" and "Apple" are treated the same.

Sorting an Array with Intl.Collator

const fruits = ["grape", "Banana", "apple", "Mango"];
fruits.sort(new Intl.Collator("en").compare);
console.log(fruits); // ['apple', 'Banana', 'grape', 'Mango']
Enter fullscreen mode Exit fullscreen mode

Passing collator.compare to .sort() ensures correct ordering based on the locale.

String.localeCompare vs Intl.Collator

Both of them can be used to sort but there are some important difference

  • Intl by default supports localisation while localeCompare uses system or default locale has has limited customisation
  • localeCompare is easier or more straightforward to use 'apple'.localeCompare('banana')
  • Intl.Collator is faster
  • Intl.Collator supports multi-language sorting

Options

  • localeMatcher: algorithm to use when comparing strings, 'best fit' (default) or 'lookup', controls how locale is determined
  • usage: how the strings are compared, 'sort' (default) for sorting, 'search' for matching text
  • sensitivity: things to consider when comparing, 'base', 'accent', 'case', 'variant' (most strict)
  • ignorePunctuation: true ignores punctuation when comparing

Intl.PluralRules: Smart Pluralization

Handling plurals correctly is trickier than just adding an "s." Different languages have complex rules for singular, dual, paucal, and plural forms. Intl.PluralRules helps us determine the right plural form dynamically.

Basic Usage

const pluralRules = new Intl.PluralRules("en");

console.log(pluralRules.select(1)); // "one"
console.log(pluralRules.select(2)); // "other"
console.log(pluralRules.select(0)); // "other"
Enter fullscreen mode Exit fullscreen mode

For English, "one" is used for 1, and "other" is used for everything else.

Different Languages, Different Rules

For example, French considers 0 singular, while English treats it as plural.

const pluralRulesFr = new Intl.PluralRules("fr");

console.log(pluralRulesFr.select(1)); // "one"
console.log(pluralRulesFr.select(0)); // "one" (French treats 0 as singular!)
console.log(pluralRulesFr.select(2)); // "other"
Enter fullscreen mode Exit fullscreen mode

Making Plural Messages Dynamic

function getItemMessage(count) {
  const pluralRule = new Intl.PluralRules("en").select(count);
  const messages = {
    one: `You have ${count} message.`,
    other: `You have ${count} messages.`,
  };
  return messages[pluralRule];
}

console.log(getItemMessage(1)); // "You have 1 message."
console.log(getItemMessage(3)); // "You have 3 messages."
Enter fullscreen mode Exit fullscreen mode

This technique allows your app to automatically use the correct plural form.

Options

  • localeMatcher
  • type: 'cardinal' (default) for counting objects, 'ordinal' for rankings (1st, 2nd, etc.)
Option Description
type 'cardinal' (default) for counting objects, 'ordinal' for rankings (1st, 2nd, etc.)
localeMatcher 'best fit' or 'lookup', controls locale selection

Let’s see some examples with ordinal numbers:

const ordinalRules = new Intl.PluralRules("en", { type: "ordinal" });

console.log(ordinalRules.select(1)); // "one" (1st)
console.log(ordinalRules.select(2)); // "two" (2nd)
console.log(ordinalRules.select(3)); // "few" (3rd)
console.log(ordinalRules.select(4)); // "other" (4th, 5th, etc.)
Enter fullscreen mode Exit fullscreen mode

Intl.ListFormat: Formatting Lists Naturally

Writing a list of items (e.g., "apples, bananas, and oranges") is different across languages. Some use commas, others use "and" or "et," and some don't use conjunctions at all. Intl.ListFormat makes it easy to format lists properly!

Basic Usage

const listFormatter = new Intl.ListFormat("en", {
  style: "long",
  type: "conjunction",
});

console.log(listFormatter.format(["apple", "banana", "cherry"]));
// "apple, banana, and cherry"
Enter fullscreen mode Exit fullscreen mode

It automatically adds "and" in the right place!

Different Locales and Styles

const listFormatterFr = new Intl.ListFormat("fr", {
  style: "long",
  type: "conjunction",
});

console.log(listFormatterFr.format(["pomme", "banane", "cerise"]));
// "pomme, banane et cerise" (French uses "et" instead of "and")

const shortFormatter = new Intl.ListFormat("en", {
  style: "short",
  type: "disjunction",
});

console.log(shortFormatter.format(["tea", "coffee", "milk"]));
// "tea, coffee, or milk" (disjunction uses "or" instead of "and")
Enter fullscreen mode Exit fullscreen mode

Options

  • type: 'conjunction' ("and"), 'disjunction' ("or"), or 'unit' (e.g., "5 hours, 30 minutes")
  • style: 'long' (default, full words), 'short' (abbreviated), 'narrow' (minimal formatting)

Conclusion

That's how Intl.Collator, Intl.PluralRules, and Intl.ListFormat can help make your JavaScript applications more international-friendly and user-friendly!

These APIs can save you a ton of work and help your app support multiple languages effortlessly.

Have you tried these Intl features before?

Top comments (0)