Comment obtenir les parties individuelles d'un nombre formaté pour un affichage personnalisé

Décomposez les nombres formatés en composants pour appliquer un style personnalisé et construire des interfaces complexes

Introduction

La méthode format() renvoie une chaîne formatée complète comme "1 234,56 €" ou "1,5M". Cela fonctionne bien pour un affichage simple, mais vous ne pouvez pas styliser différemment les parties individuelles. Vous ne pouvez pas mettre en gras le symbole monétaire, colorer différemment la partie décimale, ou appliquer un balisage personnalisé à des composants spécifiques.

JavaScript fournit la méthode formatToParts() pour résoudre ce problème. Au lieu de renvoyer une seule chaîne, elle renvoie un tableau d'objets, chacun représentant une partie du nombre formaté. Chaque partie a un type comme currency, integer, ou decimal, et une valeur comme , 1234, ou ,. Vous pouvez ensuite traiter ces parties pour appliquer un style personnalisé, construire des mises en page complexes, ou intégrer des nombres formatés dans des interfaces utilisateur riches.

Pourquoi les chaînes formatées sont difficiles à personnaliser

Lorsque vous recevez une chaîne formatée comme "1 234,56 €", vous ne pouvez pas facilement identifier où le symbole monétaire se termine et où le nombre commence. Différentes locales placent les symboles à différentes positions. Certaines locales utilisent différents séparateurs. L'analyse de ces chaînes de manière fiable nécessite une logique complexe qui duplique les règles de formatage déjà implémentées dans l'API Intl.

Considérez un tableau de bord qui affiche des montants monétaires avec le symbole de devise dans une couleur différente. Avec format(), vous devriez :

  1. Détecter quels caractères constituent le symbole monétaire
  2. Tenir compte des espaces entre le symbole et le nombre
  3. Gérer les différentes positions des symboles selon les locales
  4. Analyser la chaîne avec précaution pour éviter de casser le nombre

Cette approche est fragile et sujette aux erreurs. Tout changement dans les règles de formatage des locales brise votre logique d'analyse.

La méthode formatToParts() élimine ce problème en fournissant les composants séparément. Vous recevez des données structurées qui vous indiquent exactement quelle partie est laquelle, quelle que soit la locale.

Utilisation de formatToParts pour obtenir les composants d'un nombre

La méthode formatToParts() fonctionne de manière identique à format() à l'exception de sa valeur de retour. Vous créez un formateur avec les mêmes options, puis appelez formatToParts() au lieu de format().

const formatter = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD"
});

const parts = formatter.formatToParts(1234.56);
console.log(parts);

Cela produit un tableau d'objets :

[
  { type: "currency", value: "$" },
  { type: "integer", value: "1" },
  { type: "group", value: "," },
  { type: "integer", value: "234" },
  { type: "decimal", value: "." },
  { type: "fraction", value: "56" }
]

Chaque objet contient une propriété type identifiant ce que la partie représente et une propriété value contenant la chaîne réelle. Les parties apparaissent dans le même ordre que dans la sortie formatée.

Vous pouvez vérifier cela en joignant toutes les valeurs ensemble :

const formatted = parts.map(part => part.value).join("");
console.log(formatted);
// Sortie : "$1,234.56"

Les parties concaténées produisent exactement la même sortie que l'appel à format().

Comprendre les types de parties

La propriété type identifie chaque composant. Différentes options de formatage produisent différents types de parties.

Pour le formatage de base des nombres :

const formatter = new Intl.NumberFormat("en-US");
const parts = formatter.formatToParts(1234.56);
console.log(parts);
// [
//   { type: "integer", value: "1" },
//   { type: "group", value: "," },
//   { type: "integer", value: "234" },
//   { type: "decimal", value: "." },
//   { type: "fraction", value: "56" }
// ]

Le type integer représente la partie entière du nombre. Plusieurs parties integer apparaissent lorsque les séparateurs de groupe divisent le nombre. Le type group représente le séparateur des milliers. Le type decimal représente le point décimal. Le type fraction représente les chiffres après la décimale.

Pour le formatage des devises :

const formatter = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "EUR"
});

const parts = formatter.formatToParts(1234.56);
console.log(parts);
// [
//   { type: "currency", value: "€" },
//   { type: "integer", value: "1" },
//   { type: "group", value: "," },
//   { type: "integer", value: "234" },
//   { type: "decimal", value: "." },
//   { type: "fraction", value: "56" }
// ]

Le type currency apparaît avant ou après le nombre selon les conventions locales.

Pour les pourcentages :

const formatter = new Intl.NumberFormat("en-US", {
  style: "percent"
});

const parts = formatter.formatToParts(0.1234);
console.log(parts);
// [
//   { type: "integer", value: "12" },
//   { type: "percentSign", value: "%" }
// ]

Le type percentSign représente le symbole de pourcentage.

Pour la notation compacte :

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

const parts = formatter.formatToParts(1500000);
console.log(parts);
// [
//   { type: "integer", value: "1" },
//   { type: "decimal", value: "." },
//   { type: "fraction", value: "5" },
//   { type: "compact", value: "M" }
// ]

Le type compact représente l'indicateur de magnitude comme K, M ou B.

Application d'un style personnalisé aux parties des nombres

Le cas d'utilisation principal de formatToParts() est d'appliquer différents styles à différents composants. Vous pouvez traiter le tableau des parties pour envelopper des types spécifiques dans des éléments HTML.

Rendre le symbole de devise en gras :

const formatter = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD"
});

const parts = formatter.formatToParts(1234.56);
const html = parts
  .map(part => {
    if (part.type === "currency") {
      return `<strong>${part.value}</strong>`;
    }
    return part.value;
  })
  .join("");

console.log(html);
// Résultat : "<strong>$</strong>1,234.56"

Cette approche fonctionne pour n'importe quel langage de balisage. Vous pouvez générer du HTML, du JSX ou tout autre format en traitant le tableau des parties.

Styler différemment les parties décimales :

const formatter = new Intl.NumberFormat("en-US", {
  minimumFractionDigits: 2
});

const parts = formatter.formatToParts(1234.5);
const html = parts
  .map(part => {
    if (part.type === "decimal" || part.type === "fraction") {
      return `<span class="text-gray-500">${part.value}</span>`;
    }
    return part.value;
  })
  .join("");

console.log(html);
// Résultat : "1,234<span class="text-gray-500">.50</span>"

Ce modèle est courant dans les affichages de prix où la partie décimale apparaît plus petite ou plus claire.

Code couleur pour les nombres négatifs

Les applications financières affichent souvent les nombres négatifs en rouge. Avec formatToParts(), vous pouvez détecter le signe moins et appliquer un style en conséquence.

const formatter = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD"
});

function formatWithColor(number) {
  const parts = formatter.formatToParts(number);
  const hasMinusSign = parts.some(part => part.type === "minusSign");

  const html = parts
    .map(part => part.value)
    .join("");

  if (hasMinusSign) {
    return `<span class="text-red-600">${html}</span>`;
  }

  return html;
}

console.log(formatWithColor(-1234.56));
// Résultat : "<span class="text-red-600">-$1,234.56</span>"

console.log(formatWithColor(1234.56));
// Résultat : "$1,234.56"

Cette approche détecte les nombres négatifs de manière fiable dans toutes les locales, même celles qui utilisent des symboles ou des positions différents pour les indicateurs négatifs.

Construction d'affichages de nombres personnalisés avec plusieurs styles

Les interfaces complexes combinent souvent plusieurs règles de style. Vous pouvez appliquer différentes classes ou éléments à différents types de parties simultanément.

const formatter = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD"
});

function formatCurrency(number) {
  const parts = formatter.formatToParts(number);

  return parts
    .map(part => {
      switch (part.type) {
        case "currency":
          return `<span class="currency-symbol">${part.value}</span>`;
        case "integer":
          return `<span class="integer">${part.value}</span>`;
        case "group":
          return `<span class="group">${part.value}</span>`;
        case "decimal":
          return `<span class="decimal">${part.value}</span>`;
        case "fraction":
          return `<span class="fraction">${part.value}</span>`;
        case "minusSign":
          return `<span class="minus">${part.value}</span>`;
        default:
          return part.value;
      }
    })
    .join("");
}

console.log(formatCurrency(1234.56));
// Output: "<span class="currency-symbol">$</span><span class="integer">1</span><span class="group">,</span><span class="integer">234</span><span class="decimal">.</span><span class="fraction">56</span>"

Ce contrôle granulaire permet un style précis pour chaque composant. Vous pouvez ensuite utiliser CSS pour styliser différemment chaque classe.

Tous les types de parties disponibles

La propriété type peut avoir ces valeurs selon les options de formatage utilisées :

  • integer : chiffres des nombres entiers
  • fraction : chiffres décimaux
  • decimal : séparateur décimal
  • group : séparateur des milliers
  • currency : symbole monétaire
  • literal : espacement ou autre texte littéral ajouté par le formatage
  • percentSign : symbole de pourcentage
  • minusSign : indicateur de nombre négatif
  • plusSign : indicateur de nombre positif (lorsque signDisplay est défini)
  • unit : chaîne d'unité pour le formatage d'unité
  • compact : indicateur de magnitude en notation compacte (K, M, B)
  • exponentInteger : valeur d'exposant en notation scientifique
  • exponentMinusSign : signe négatif dans l'exposant
  • exponentSeparator : symbole séparant la mantisse de l'exposant
  • infinity : représentation de l'infini
  • nan : représentation de non-nombre
  • unknown : jetons non reconnus

Chaque option de formatage ne produit pas tous les types de parties. Les parties que vous recevez dépendent de la valeur numérique et de la configuration du formateur.

La notation scientifique produit des parties liées à l'exposant :

const formatter = new Intl.NumberFormat("en-US", {
  notation: "scientific"
});

const parts = formatter.formatToParts(1234);
console.log(parts);
// [
//   { type: "integer", value: "1" },
//   { type: "decimal", value: "." },
//   { type: "fraction", value: "234" },
//   { type: "exponentSeparator", value: "E" },
//   { type: "exponentInteger", value: "3" }
// ]

Les valeurs spéciales produisent des types de parties spécifiques :

const formatter = new Intl.NumberFormat("en-US");

console.log(formatter.formatToParts(Infinity));
// [{ type: "infinity", value: "∞" }]

console.log(formatter.formatToParts(NaN));
// [{ type: "nan", value: "NaN" }]

Création d'affichages de nombres accessibles

Vous pouvez utiliser formatToParts() pour ajouter des attributs d'accessibilité aux nombres formatés. Cela aide les lecteurs d'écran à annoncer correctement les valeurs.

const formatter = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD"
});

function formatAccessibleCurrency(number) {
  const parts = formatter.formatToParts(number);
  const formatted = parts.map(part => part.value).join("");

  return `<span aria-label="${number} US dollars">${formatted}</span>`;
}

console.log(formatAccessibleCurrency(1234.56));
// Output: "<span aria-label="1234.56 US dollars">$1,234.56</span>"

Cela garantit que les lecteurs d'écran annoncent à la fois la valeur d'affichage formatée et la valeur numérique sous-jacente avec le contexte approprié.

Mise en évidence de plages de nombres spécifiques

Certaines applications mettent en évidence les nombres qui se situent dans certaines plages. Avec formatToParts(), vous pouvez appliquer un style basé sur la valeur tout en maintenant un formatage approprié.

const formatter = new Intl.NumberFormat("en-US");

function formatWithThreshold(number, threshold) {
  const parts = formatter.formatToParts(number);
  const formatted = parts.map(part => part.value).join("");

  if (number >= threshold) {
    return `<span class="text-green-600 font-bold">${formatted}</span>`;
  }

  return formatted;
}

console.log(formatWithThreshold(1500, 1000));
// Output: "<span class="text-green-600 font-bold">1,500</span>"

console.log(formatWithThreshold(500, 1000));
// Output: "500"

Le nombre reçoit un formatage approprié pour la locale tandis que le style conditionnel s'applique selon la logique métier.

Quand utiliser formatToParts versus format

Utilisez format() lorsque vous avez besoin d'une simple chaîne formatée sans personnalisation. C'est le cas courant pour la plupart des affichages de nombres.

Utilisez formatToParts() lorsque vous avez besoin de :

  • Appliquer différents styles à différentes parties du nombre
  • Construire du HTML ou JSX avec des nombres formatés
  • Ajouter des attributs ou des métadonnées à des composants spécifiques
  • Intégrer des nombres formatés dans des mises en page complexes
  • Traiter programmatiquement la sortie formatée

La méthode formatToParts() a légèrement plus de surcharge que format() car elle crée un tableau d'objets au lieu d'une seule chaîne. Cette différence est négligeable pour les applications typiques, mais si vous formatez des milliers de nombres par seconde, format() offre de meilleures performances.

Pour la plupart des applications, choisissez en fonction de vos besoins de style plutôt que des préoccupations de performance. Si vous n'avez pas besoin de personnaliser la sortie, utilisez format(). Si vous avez besoin d'un style ou d'un balisage personnalisé, utilisez formatToParts().

Comment les parties préservent le formatage spécifique à la locale

Le tableau des parties maintient automatiquement les règles de formatage spécifiques à la locale. Les différentes locales placent les symboles à différentes positions et utilisent différents séparateurs, mais formatToParts() gère ces différences.

const usdFormatter = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD"
});

console.log(usdFormatter.formatToParts(1234.56));
// [
//   { type: "currency", value: "$" },
//   { type: "integer", value: "1" },
//   { type: "group", value: "," },
//   { type: "integer", value: "234" },
//   { type: "decimal", value: "." },
//   { type: "fraction", value: "56" }
// ]

const eurFormatter = new Intl.NumberFormat("de-DE", {
  style: "currency",
  currency: "EUR"
});

console.log(eurFormatter.formatToParts(1234.56));
// [
//   { type: "integer", value: "1" },
//   { type: "group", value: "." },
//   { type: "integer", value: "234" },
//   { type: "decimal", value: "," },
//   { type: "fraction", value: "56" },
//   { type: "literal", value: " " },
//   { type: "currency", value: "€" }
// ]

Le formatage allemand place la devise après le nombre avec un espace. Le séparateur de groupe est un point, et le séparateur décimal est une virgule. Votre code de style traite le tableau des parties de la même manière quelle que soit la locale, et le formatage s'adapte automatiquement.

Le type literal représente tout espacement ou texte inséré par le formateur qui ne correspond pas à d'autres catégories. Dans le formatage de devise allemand, il représente l'espace entre le nombre et le symbole de devise.

Combiner formatToParts avec des composants de framework

Les frameworks modernes comme React peuvent utiliser formatToParts() pour construire des composants efficacement.

function CurrencyDisplay({ value, locale, currency }) {
  const formatter = new Intl.NumberFormat(locale, {
    style: "currency",
    currency: currency
  });

  const parts = formatter.formatToParts(value);

  return (
    <span className="currency-display">
      {parts.map((part, index) => {
        if (part.type === "currency") {
          return <strong key={index}>{part.value}</strong>;
        }
        if (part.type === "fraction" || part.type === "decimal") {
          return <span key={index} className="text-sm text-gray-500">{part.value}</span>;
        }
        return <span key={index}>{part.value}</span>;
      })}
    </span>
  );
}

Ce composant applique différents styles aux différentes parties tout en maintenant un formatage approprié pour n'importe quelle locale et devise.