O Intl é uma API do Javascript que fornece funcionalidades para criação de textos internacionalizados. Entretanto, ela pode ser usada em qualquer caso que você precise formatar informações de Javascript em textos, removendo a necessidade de bibliotecas externas.

Uma das funcionalidades mais conhecidas é o Intl.DateTimeFormat, que está disponível desde 2017. Ele é usado para formatar datas e horas:

const portugueseFormatter = new Intl.DateTimeFormat("pt-BR", {
  dateStyle: "full",
  timeStyle: "short",
});

const englishFormatter = new Intl.DateTimeFormat("en-US", {
  dateStyle: "full",
  timeStyle: "short",
});

const date = new Date("2025-05-01T12:00:00Z");
console.log(portugueseFormatter.format(date)); // Thursday, May 1, 2025 at 9:00 AM (in PT)
console.log(englishFormatter.format(date)); // Thursday, May 1, 2025 at 9:00 AM

Entretanto, existem outras APIs menos conhecidas (mas muito úteis) que também estão disponíveis através do Intl.

Intl.NumberFormat

O NumberFormat é muito útil para aplicativos em português, já que utilizamos vírgula para representar casas decimais.

const conversionRate = 3.7;
const portugueseNumberFormatter = Intl.NumberFormat("pt-BR");

console.log(portugueseNumberFormatter.format(conversionRate)); // 3,7

Também é possível definir um valor mínimo ou máximo de dígitos depois da vírgula (com a possibilidade de definir a estratégia de arredondamento):

const winningRate = 1.2345653;
const zero = 0;

const portugueseNumberFormatter = Intl.NumberFormat("pt-BR", {
  maximumFractionDigits: 3,
  minimumFractionDigits: 3,
});

console.log(portugueseNumberFormatter.format(winningRate)); // 1,235
console.log(portugueseNumberFormatter.format(zero)); // 0,000

E se você pensou que essa é a solução para manter preços sempre dentro do estilo necessário, existe algo melhor, uma opção específica para formatar preços e moedas:

const ticketPrice = 5.9;

const realFormatter = new Intl.NumberFormat("pt-BR", {
  style: "currency",
  currency: "BRL",
});
console.log(realFormatter.format(ticketPrice)); // R$ 5,90

E até unidades podem ser utilizadas:

const distanceKilometers = 90;
const timeInHours = 0.4;

const velocity = distanceKilometers / timeInHours;

const velocityFormatter = new Intl.NumberFormat("pt-BR", {
  style: "unit",
  unit: "kilometer-per-hour",
});

console.log(velocityFormatter.format(velocity)); // 225 km/h

Outras opções e exemplos estão disponíveis no MDN.

Intl.PluralRules

1 usuário online

É muito fácil de esquecer de incluir regras de plural. E verificar manualmente pode ser repetitivo.

`${userCount} ${ userCount > 1 ? "users" : "user" } online`;

O Intl.PluralRules fornece ajuda retornando a categoria cardinal baseado no número:

const enCardinalRules = new Intl.PluralRules("en-US");
const enNumberFormatter = new Intl.NumberFormat("en-US", {
  notation: "compact",
  compactDisplay: "long",
});

function userCountText(userCount: number) {
  const category = enCardinalRules.select(userCount);
  const userCountDisplay = enNumberFormatter.format(userCount);

  if (category === "one") {
    return `${userCountDisplay} user online`;
  }

  if (category === "other") {
    return `${userCountDisplay} users online`;
  }

  // Would you remember this case?
  if (category === "many") {
    return `${userCountDisplay} of users online`;
  }
}

console.log(userCountText(1)); // 1 user online
console.log(userCountText(2)); // 2 users online
console.log(userCountText(2000000)); // 2 million users online

Entretanto, algo estranho acontece quando usamos o zero:

console.log(userCountText(0)); // 0 users online

A realidade é que a especificação do CLDR sobre regras de plurais, em que o Intl.PluralRules se baseia, considera que 0 está dentro de one para o locale pt (como pt-BR está ausente na lista, os navegadores utilizam pt de "fallback").

Existe uma discussão na língua portuguesa a respeito do uso do plural com o 0, mas ela está fora do escopo desse artigo. Para fazer o 0 ser parte da categoria other, você pode utilizar o locale pt-PT.

const ptBRplural = new Intl.PluralRules("pt-BR");
const ptPTplural = new Intl.PluralRules("pt-PT");

console.log(ptBRplural.select(0)); // one
console.log(ptPTplural.select(0)); // other

Intl.ListFormat

Formata uma lista, seguindo as regras da língua.

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

const users = ["John"];
console.log(formatter.format(users)); // John

users.push("Lennon");
console.log(formatter.format(users)); // John and Lennon

users.push("Ringo");
users.push("George");
console.log(formatter.format(users)); // John, Lennon, Ringo, and George

Para utilizar "ou", utilizamos type: "disjunction":

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

const meansOfTransport = ["Car", "Bicycle", "Bus"];
console.log(formatter.format(meansOfTransport)); // Car, Bicycle, or Bus

Intl.RelativeTimeFormat

O RelativeTimeFormat permite converter uma unidade em tempo relativo.

const relativeTime = new Intl.RelativeTimeFormat("en");

console.log(relativeTime.format(2, "day")); // in 2 days
console.log(relativeTime.format(12, "years")); // in 12 years
console.log(relativeTime.format(-1, "week")); // 1 week ago

As unidades possíveis são: year, quarter, month, week, day, hour, minute ou second.

Por padrão, o resultado é sempre númerico, mas você pode permitir o uso de frases idiomáticas:

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

console.log(relativeTime.format(2, "day")); // day after tomorrow
console.log(relativeTime.format(0, "years")); // this year
console.log(relativeTime.format(1, "week")); // next week
console.log(relativeTime.format(0, "second")); // now

Entretanto, eu acho a API do RelativeTimeFormat pequena em relação à quantidade de possibilidades. Eu acho sempre muito difícil trabalhar com Tempo e Duração no Javascript com bibliotecas externas. Novas APIs estão por vir para facilitar essas tarefas, mas isso é assunto para outro post.