Software Engineering

Internationalization API (`Intl`), sorting and aggregate functions

Internationalization API (`Intl`), sorting and aggregate functions

KUZUE
August 2, 2026
7 min read
5 views

Internationalization API (Intl)

JavaScript provides the Internationalization API, usually called Intl.

Think of it as a translator—but not for words. Instead, it formats things like:

  • Numbers
  • Dates
  • Times
  • Currencies

One part of this API is Intl.DateTimeFormat.


What is Intl.DateTimeFormat?

Intl.DateTimeFormat is a JavaScript class that formats Date objects according to a user's:

  • Language
  • Country
  • Regional preferences

It doesn't change the actual date. It only changes how the date is displayed.


Syntax

javascript
new Intl.DateTimeFormat(locales?, options?)

locales

The locales argument tells JavaScript to format the date according to a particular country or language.

Examples:

javascript
"en-US" // United States
"en-GB" // United Kingdom
"fr-FR" // France

If you don't care which locale is used, pass undefined.

javascript
new Intl.DateTimeFormat(undefined);

This tells JavaScript:

"Use the user's browser or operating system locale."


First, create a formatter

Before you can format dates, you need to create a formatter.

javascript
const formatter = new Intl.DateTimeFormat(undefined, {
  dateStyle: "long",
  timeStyle: "short",
});

You only create the formatter once, then you can reuse it for as many dates as you want.


Formatting a date

Suppose you have this date:

text
2026-07-27T13:30:45.123Z

First, convert it into a JavaScript Date object.

javascript
const date = new Date("2026-07-27T13:30:45.123Z");

Then use the formatter.

javascript
formatter.format(date);

The output depends on the user's locale, but it could look something like:

text
July 27, 2026 at 2:30 PM

Intl.NumberFormat

Background

Intl.NumberFormat formats numbers into locale-aware strings. It automatically applies the correct formatting rules for the selected locale, including thousands separators, decimal separators, currency symbols, percentages, measurement units, scientific notation, and compact number notation.


Syntax

ts
const formatter = new Intl.NumberFormat(locales, options);

formatter.format(number);

Parameters

locales

Specifies the language or region whose formatting rules should be used.

ts
"en-US"
"en-NG"
"fr-FR"
undefined // Uses the user's browser locale

options

Controls how the number should be formatted.

Common options include:

style : "decimal" (default), "currency", "percent", or "unit" currency : Currency code such as "NGN", "USD", "EUR" currencyDisplay: How the currency should appear: "symbol" (default), "narrowSymbol", "code", or "name" notation: "standard" (default), "scientific", "engineering", or "compact" compactDisplay :"short" (default, e.g. 2.5M) or "long" (e.g. 2.5 million) minimumFractionDigits : Minimum number of decimal places to display maximumFractionDigits : Maximum number of decimal places to display roundingMode : Controls how values are rounded. Examples include "trunc", "ceil", "floor", "expand", "halfExpand", and "halfEven"


Common Uses

Currency Formatting

ts
const formatter = new Intl.NumberFormat("en-NG", {
  style: "currency",
  currency: "NGN",
});

formatter.format(1250000);

Output:

text
₦1,250,000.00

Compact Number Formatting

Compact notation is commonly used in dashboards, analytics, and reporting applications.

ts
const formatter = new Intl.NumberFormat("en-NG", {
  notation: "compact",
});

formatter.format(2500000);

Output:

text
2.5M

Controlling Decimal Precision

ts
const formatter = new Intl.NumberFormat("en-NG", {
  maximumFractionDigits: 2,
});

Useful when displaying financial values or percentages that require consistent precision.


Summary

Intl.NumberFormat is commonly used for:

  • Currency formatting (₦1,250,000)
  • Percentages (25%)
  • Measurement units (120 km/h, 50 MB)
  • Compact numbers (2.5M, 18K)

Intl.RelativeTimeFormat

Background

Intl.RelativeTimeFormat converts a time difference into a natural human-readable phrase. Instead of displaying an exact date or time, it displays relative values such as:

  • Yesterday, 5 minutes ago, In 2 hours, Next week. This is commonly used in notifications, messaging applications, activity feeds, and dashboards.

Syntax

ts
const rtf = new Intl.RelativeTimeFormat(locales, options);

rtf.format(value, unit);

value

A number representing the time difference. Negative values represent the past.Positive values represent the future.


unit

The unit of time.

ts
"second","minute", "hour","day","week","month","quarter","year"

options

numeric: "always" displays exact values (e.g. "1 day ago"). "auto" uses natural phrases where possible (e.g. "yesterday"). style: "long" (default), "short", or "narrow"

Example

ts
const rtf = new Intl.RelativeTimeFormat("en", {
  numeric: "auto",
});

rtf.format(-1, "day");

Output:

text
yesterday

Another example:

ts
rtf.format(5, "minute");

Output:

text
in 5 minutes

Summary

Intl.RelativeTimeFormat is commonly used for:

  • Notifications, Chat applications, Activity feeds, Audit logs
  • Social media timestamps

Using more detailed formatting options

Instead of using dateStyle and timeStyle, you can choose exactly which parts of the date you want to display.

javascript
const formatter = new Intl.DateTimeFormat("en-US", {
  weekday: "long",
  year: "numeric",
  month: "long",
  day: "numeric",
  hour: "2-digit",
  minute: "2-digit",
  second: "2-digit",
});

This gives you more control over how the date is displayed.


Why create a formatter?

One advantage of Intl.DateTimeFormat is that you create the formatter once and reuse it whenever you need it.

javascript
formatter.format(new Date());
formatter.format(new Date("2026-10-01"));
formatter.format(new Date("2027-01-15"));

This is cleaner than creating a new formatter every time you want to format a date.


Using toLocaleString()

You can also use toLocaleString().

Instead of creating a formatter, you call it directly on a Date object.

First, create a Date.

javascript
const date = new Date("2026-07-27T13:30:45.123Z");

Then call:

javascript
date.toLocaleString(locales, options);

It accepts the same locales and options as Intl.DateTimeFormat.

Example:

javascript
date.toLocaleString("en-GB", {
  dateStyle: "long",
  timeStyle: "short",
});

Which one should you use?

Use Intl.DateTimeFormat if you're formatting many dates because you can reuse the formatter.

Use toLocaleString() if you're formatting just one date.


Sorting Arrays

The .sort() method sorts the elements of an array.

Syntax

javascript
array.sort(compareFunction?)

The compareFunction is optional.

If you don't provide one, JavaScript converts the values to strings and sorts them alphabetically.

Example:

javascript
["Orange", "Banana", "Apple"].sort();

Result:

javascript
["Apple", "Banana", "Orange"];

If you want JavaScript to sort differently, provide your own comparison function.


Sorting strings

When sorting strings, localeCompare() is preferred because it handles alphabetical ordering correctly across different languages and locales.

For an array of objects:

javascript
students.sort((a, b) => a.name.localeCompare(b.name));

For an array of strings:

javascript
array.sort((a, b) => a.localeCompare(b));

Sorting numbers

For numbers, always provide a comparison function.

Ascending

javascript
numbers.sort((a, b) => a - b);

Descending

javascript
numbers.sort((a, b) => b - a);

Without a comparison function, JavaScript compares numbers as strings.

For example:

javascript
[100, 20, 3].sort();

does not sort numerically because JavaScript compares:

text
"100"
"20"
"3"

instead of the actual numbers.


Prisma groupBy()

groupBy() groups rows that have the same value in one or more fields, then performs aggregate calculations for each group separately.

Syntax

ts
await prisma.<model>.groupBy({
  by: [],
  where: {},
  _sum: {},
  _count: {},
  _avg: {},
  _min: {},
  _max: {},
  orderBy: {},
  having: {},
  take: 10,
  skip: 0,
});

How groupBy() works

Let's look at a simple example.

ts
await prisma.sale.groupBy({
  by: ["itemName"],
});

This tells Prisma:

Group all rows that have the same itemName.

Think of it as a two-step process.

Step 1

Group the rows using the fields inside by.

Step 2

Run the aggregate calculations (_sum, _count, _avg, _min, _max) for each group.

The by field decides how the rows are grouped, while the aggregate functions decide what information should be calculated for each group.


What does groupBy() return?

groupBy() returns an array of objects.

Why?

Because there can be many groups, and each group becomes one object in the returned array.

Example:

ts
[
  {
    itemName: "Rice",
    _sum: {
      quantity: 12,
    },
  },
  {
    itemName: "Beans",
    _sum: {
      quantity: 8,
    },
  },
];

groupBy() vs aggregate()

Both perform calculations, but they answer different questions.

groupBy()

Returns an array because it summarizes each group separately.

aggregate()

Returns a single object because it summarizes all matching records together.

Example:

ts
await prisma.sale.aggregate({
  _sum: {
    quantity: true,
  },
});

Result:

ts
{
  _sum: {
    quantity: 20,
  },
}

Key takeaway

  • by decides how the rows are grouped.
  • Aggregate functions (_sum, _count, _avg, _min, _max) decide what calculations are performed for each group.
  • groupBy() returns an array of grouped summaries.
  • aggregate() returns one summary object for all matching records.

Comments (0)

No comments yet. Be the first to share your thoughts.