Wie du formatierte Ausgaben für individuelles Styling in einzelne Teile zerlegst

Nutze formatToParts(), um auf einzelne Bestandteile der formatierten Ausgabe für benutzerdefiniertes Styling zuzugreifen

Einführung

Die Methode format() bei JavaScript-Formatierungen gibt vollständige Strings wie „1.234,56 $“ oder „15. Januar 2025“ zurück. Für einfache Ausgaben reicht das aus, aber du kannst einzelne Bestandteile nicht unterschiedlich stylen. Beispielsweise kannst du das Währungssymbol nicht fett markieren, den Monatsnamen anders einfärben oder einzelnen Komponenten ein eigenes Markup geben.

JavaScript bietet die Methode formatToParts(), um dieses Problem zu lösen. Sie gibt nicht einen einzigen String zurück, sondern ein Array aus Objekten, von denen jedes einen Teil der formatierten Ausgabe repräsentiert. Jeder Teil hat einen Typ wie currency, month oder element und einen Wert mit dem eigentlichen Text. Du kannst diese Teile dann weiterverarbeiten, individuell stylen, komplexe Layouts aufbauen oder formatierte Inhalte in anspruchsvolle Benutzeroberflächen einbinden.

Die Methode formatToParts() gibt es bei mehreren Intl-Formatierern, darunter NumberFormat, DateTimeFormat, ListFormat, RelativeTimeFormat und DurationFormat. Das macht sie zu einem konsistenten Muster für alle Internationalisierungs-Formatierungen in JavaScript.

Warum formatierte Strings nicht einfach zu stylen sind

Wenn du einen formatierten String wie „1.234,56 $“ erhältst, kannst du nicht ohne Weiteres erkennen, wo das Währungssymbol aufhört und die Zahl beginnt. Verschiedene Sprachräume platzieren Symbole an unterschiedlichen Stellen. Manche nutzen auch andere Trennzeichen. Solche Strings zuverlässig zu parsen, erfordert aufwendige Logik, die die von der Intl API bereitgestellten Formatierungsregeln nachbauen müsste.

Stell dir ein Dashboard vor, das Geldbeträge anzeigt und das Währungssymbol in einer anderen Farbe darstellt. Mit format() müsstest du:

  1. Die Zeichen erkennen, die das Währungssymbol darstellen
  2. Abstände zwischen Symbol und Zahl berücksichtigen
  3. Unterschiedliche Symbolpositionen je nach Sprache beachten
  4. Den String sorgfältig parsen, ohne die Nummer zu beschädigen

Dieser Ansatz ist anfällig und fehleranfällig. Jede Änderung an den lokalen Formatierungsregeln bringt deine Parsing-Logik durcheinander.

Dasselbe Problem gibt es bei Datumsangaben, Listen und anderer formatierter Ausgabe. Du kannst formatierte Zeichenfolgen nicht zuverlässig parsen, um Komponenten zu identifizieren, ohne die lokal-spezifischen Formatierungsregeln selbst nachzubauen.

Die Methode formatToParts() löst dieses Problem, indem sie die einzelnen Teile separat bereitstellt. Du erhältst strukturierte Daten, die dir genau sagen, welcher Teil was darstellt – unabhängig vom Gebietsschema.

So funktioniert formatToParts

Die Methode formatToParts() funktioniert genauso wie format(), nur der Rückgabewert ist anders. Du erstellst einen Formatter mit denselben Optionen und rufst dann formatToParts() statt format() auf.

Die Methode gibt ein Array aus Objekten zurück. Jedes Objekt enthält zwei Eigenschaften:

  • type: Gibt an, was der jeweilige Teil darstellt, zum Beispiel currency, month oder literal
  • value: Enthält die tatsächliche Zeichenkette für diesen Teil

Die Teile erscheinen in der gleichen Reihenfolge wie in der formatierten Ausgabe. Das kannst du überprüfen, indem du alle Werte zusammenfügst – das ergibt genau die gleiche Ausgabe wie ein Aufruf von format().

Dieses Muster ist konsistent bei allen Formatierern, die formatToParts() unterstützen. Die genauen Teiltypen unterscheiden sich je nach Formatter, aber die Struktur bleibt immer gleich.

Formatierte Zahlen in Teile zerlegen

Der Formatter NumberFormat bietet die Methode formatToParts(), um formatierte Zahlen in ihre Bestandteile zu zerlegen. Das funktioniert für einfache Zahlen, Währungen, Prozentsätze und andere Zahlenformate.

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

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

Dadurch entsteht ein Array mit Objekten:

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

Jedes Objekt gibt an, was der Teil darstellt, und liefert seinen Wert. Der Typ currency steht für das Währungssymbol. integer steht für ganze Zahlziffern. group markiert das Tausendertrennzeichen. decimal repräsentiert das Dezimaltrennzeichen. fraction steht für Ziffern nach dem Komma.

Du kannst überprüfen, ob die Teile mit der formatierten Ausgabe übereinstimmen:

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

Die zusammengefügten Teile ergeben genau die gleiche Ausgabe wie der Aufruf von format().

Währungszeichen in formatierten Zahlen stylen

Der Hauptanwendungsfall für formatToParts() ist das Anwenden verschiedener Stile auf unterschiedliche Komponenten. Du kannst das Teile-Array verarbeiten, um bestimmte Typen in HTML-Elemente zu verpacken.

Das Währungssymbol fett darstellen:

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);
// Output: "<strong>$</strong>1,234.56"

Dieser Ansatz funktioniert für jede Markupsprache. Du kannst HTML, JSX oder jedes andere gewünschte Format erzeugen, indem du das Teile-Array verarbeitest.

Dezimalstellen anders stylen:

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);
// Output: "1,234<span class="text-gray-500">.50</span>"

Dieses Muster ist bei Preisangaben üblich, bei denen die Dezimalstellen kleiner oder heller dargestellt werden.

Formatierte Datumsangaben in Teile zerlegen

Der DateTimeFormat-Formatter stellt formatToParts() bereit, um formatierte Datums- und Zeitangaben zu zerlegen.

const formatter = new Intl.DateTimeFormat("en-US", {
  year: "numeric",
  month: "long",
  day: "numeric"
});

const date = new Date(2025, 0, 15);
const parts = formatter.formatToParts(date);
console.log(parts);

Dies gibt ein Array von Objekten aus:

[
  { type: "month", value: "January" },
  { type: "literal", value: " " },
  { type: "day", value: "15" },
  { type: "literal", value: ", " },
  { type: "year", value: "2025" }
]

Der Typ month steht für den Monatsnamen oder die Monatszahl. Der Typ day steht für den Tag des Monats. Der Typ year steht für das Jahr. Der Typ literal repräsentiert Leerzeichen, Satzzeichen oder anderen vom Formatter eingefügten Text.

Monatsnamen in formatierten Daten stylen

Du kannst einzelnen Komponenten eines Datums nach dem gleichen Muster wie bei Zahlen eigene Stile verpassen.

Den Monatsnamen fett darstellen:

const formatter = new Intl.DateTimeFormat("en-US", {
  year: "numeric",
  month: "long",
  day: "numeric"
});

const date = new Date(2025, 0, 15);
const parts = formatter.formatToParts(date);
const html = parts
  .map(part => {
    if (part.type === "month") {
      return `<strong>${part.value}</strong>`;
    }
    return part.value;
  })
  .join("");

console.log(html);
// Output: "<strong>January</strong> 15, 2025"

Mehrere Datumsbestandteile stylen:

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

const date = new Date(2025, 0, 15);
const parts = formatter.formatToParts(date);

const html = parts
  .map(part => {
    switch (part.type) {
      case "weekday":
        return `<span class="font-bold">${part.value}</span>`;
      case "month":
        return `<span class="text-blue-600">${part.value}</span>`;
      case "year":
        return `<span class="text-gray-500">${part.value}</span>`;
      default:
        return part.value;
    }
  })
  .join("");

console.log(html);
// Output: "<span class="font-bold">Wednesday</span>, <span class="text-blue-600">January</span> 15, <span class="text-gray-500">2025</span>"

Diese detaillierte Kontrolle ermöglicht ein präzises Styling für jede Komponente.

Formatierte Listen in Einzelteile zerlegen

Der ListFormat-Formatter bietet formatToParts(), um formatierte Listen in einzelne Teile zu zerlegen.

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

const items = ["apples", "oranges", "bananas"];
const parts = formatter.formatToParts(items);
console.log(parts);

Das Ergebnis ist ein Array aus Objekten:

[
  { type: "element", value: "apples" },
  { type: "literal", value: ", " },
  { type: "element", value: "oranges" },
  { type: "literal", value: ", and " },
  { type: "element", value: "bananas" }
]

Der Typ element steht für jedes einzelne Element der Liste. Der Typ literal steht für Trennzeichen und Konjunktionen, die vom Formatter eingefügt werden.

Einzelnes Styling von Listenelementen

Du kannst Listenelemente individuell gestalten, indem du das gleiche Muster verwendest.

Listenelemente fett darstellen:

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

const items = ["apples", "oranges", "bananas"];
const parts = formatter.formatToParts(items);
const html = parts
  .map(part => {
    if (part.type === "element") {
      return `<strong>${part.value}</strong>`;
    }
    return part.value;
  })
  .join("");

console.log(html);
// Output: "<strong>apples</strong>, <strong>oranges</strong>, and <strong>bananas</strong>"

Bestimmte Listenelemente stylen:

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

const items = ["apples", "oranges", "bananas"];
const parts = formatter.formatToParts(items);

let itemIndex = 0;
const html = parts
  .map(part => {
    if (part.type === "element") {
      const currentIndex = itemIndex++;
      if (currentIndex === 0) {
        return `<span class="text-green-600">${part.value}</span>`;
      }
      return part.value;
    }
    return part.value;
  })
  .join("");

console.log(html);
// Output: "<span class="text-green-600">apples</span>, oranges, and bananas"

Mit diesem Ansatz kannst du bestimmte Elemente hervorheben und dabei das richtige, lokale Format beibehalten.

Formatierte relative Zeiten in Teile zerlegen

Der RelativeTimeFormat-Formatter bietet formatToParts(), um relative Zeitangaben in ihre Bestandteile zu zerlegen.

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

const parts = formatter.formatToParts(-1, "day");
console.log(parts);

Das Ergebnis ist ein Array aus Objekten:

[
  { type: "literal", value: "yesterday" }
]

Für numerische relative Zeiten:

const formatter = new Intl.RelativeTimeFormat("en-US", {
  numeric: "always"
});

const parts = formatter.formatToParts(-3, "day");
console.log(parts);
// [
//   { type: "integer", value: "3" },
//   { type: "literal", value: " days ago" }
// ]

Der Typ integer steht für den Zahlenwert. Der Typ literal steht für die Zeiteinheit und die Richtung.

Formatierte Zeitspannen in Teile zerlegen

Der DurationFormat-Formatter bietet formatToParts(), um formatierte Zeitspannen aufzuteilen.

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

const parts = formatter.formatToParts({
  hours: 2,
  minutes: 30,
  seconds: 15
});
console.log(parts);

Das Ergebnis ist ein Array aus Objekten, ähnlich wie:

[
  { type: "integer", value: "2" },
  { type: "literal", value: " hours, " },
  { type: "integer", value: "30" },
  { type: "literal", value: " minutes, " },
  { type: "integer", value: "15" },
  { type: "literal", value: " seconds" }
]

Der integer-Typ steht für numerische Werte. Der literal-Typ steht für Einheitenbezeichnungen und Trennzeichen.

HTML aus formatierten Teilen generieren

Du kannst eine wiederverwendbare Funktion erstellen, die die einzelnen Teile verarbeitet und Styling-Regeln einheitlich anwendet.

function formatWithStyles(parts, styleMap) {
  return parts
    .map(part => {
      const style = styleMap[part.type];
      if (style) {
        return `<span class="${style}">${part.value}</span>`;
      }
      return part.value;
    })
    .join("");
}

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

const parts = numberFormatter.formatToParts(1234.56);
const html = formatWithStyles(parts, {
  currency: "font-bold text-gray-700",
  integer: "text-2xl",
  fraction: "text-sm text-gray-500"
});

console.log(html);
// Output: "<span class="font-bold text-gray-700">$</span><span class="text-2xl">1</span>,<span class="text-2xl">234</span>.<span class="text-sm text-gray-500">56</span>"

Mit diesem Muster trennst du die Styling-Regeln von der Formatierungslogik – das vereinfacht Wartung und Wiederverwendung.

Lokalspezifische Reihenfolge von Bestandteilen verstehen

Das parts-Array hält sich automatisch an lokalspezifische Formatierungsregeln. Verschiedene Sprachen ordnen Bestandteile unterschiedlich an und nutzen verschiedene Formate, aber formatToParts() berücksichtigt diese Unterschiede.

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: "€" }
// ]

Im Deutschen steht die Währung nach der Zahl und wird durch ein Leerzeichen getrennt. Als Tausendertrennzeichen dient ein Punkt, als Dezimaltrennzeichen ein Komma. Dein Styling-Code verarbeitet das parts-Array immer gleich, die Formatierung passt sich je nach Sprache automatisch an.

Barrierefreie formatierte Ausgaben erstellen

Mit formatToParts() kannst du barrierefreie Attribute zu formatierten Ausgaben hinzufügen. So werden Werte von Screenreadern korrekt vorgelesen.

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

function formatAccessible(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(formatAccessible(1234.56));
// Output: "<span aria-label="1234.56 US dollars">$1,234.56</span>"

Dadurch stellen Screenreader sicher, dass sowohl der formatierte Wert als auch der zugrundeliegende numerische Wert im passenden Kontext ausgegeben werden.

formatToParts mit Framework-Komponenten kombinieren

Moderne Frameworks wie React können formatToParts() nutzen, um Komponenten effizient zu bauen.

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>
  );
}

Diese Komponente wendet verschiedene Stile auf bestimmte Teile an und stellt dabei sicher, dass die Formatierung für jede Sprache und Währung korrekt ist.

Wann sollte man formatToParts verwenden?

Nutze format(), wenn du eine einfach formatierte Zeichenkette ohne Anpassung brauchst. Das ist der Regelfall für die meisten Anzeigen.

Verwende formatToParts(), wenn du Folgendes brauchst:

  • Unterschiedliche Stile auf verschiedene Teile des formatierten Ergebnisses anwenden
  • HTML oder JSX mit formatierten Inhalten bauen
  • Bestimmte Bestandteile mit Attributen oder Metadaten versehen
  • Formatierte Ausgaben in komplexe Layouts einbetten
  • Formatierte Ausgaben programmatisch weiterverarbeiten
  • Individuelle Designs erstellen, bei denen du die volle Kontrolle über die Darstellung brauchst

Die Methode formatToParts() erzeugt einen etwas höheren Overhead als format(), da sie ein Array von Objekten statt nur eines Strings erstellt. Für typische Anwendungen ist dieser Unterschied vernachlässigbar, aber wenn du Tausende Werte pro Sekunde formatierst, ist format() performanter.

Für die meisten Anwendungen solltest du je nach Styling-Bedarf und nicht nach Performance-Aspekten wählen. Wenn du die Ausgabe nicht anpassen musst, nutze format(). Wenn du individuelles Styling oder Markup brauchst, wähle formatToParts().

Häufige Part-Typen in verschiedenen Formatierern

Verschiedene Formatierer generieren unterschiedliche Part-Typen, aber einige Typen kommen bei mehreren Formatierern vor:

  • literal: Leerzeichen, Satzzeichen oder anderer Text, der durch das Formatieren hinzugefügt wird. Kommt in Datumsangaben, Zahlen, Listen und Zeitdauern vor.
  • integer: Ziffern ganzer Zahlen. Kommt in Zahlen, relativen Zeiten und Zeitdauern vor.
  • decimal: Dezimaltrennzeichen. Kommt in Zahlen vor.
  • fraction: Dezimalstellen. Kommt in Zahlen vor.

Formatierer-spezifische Typen sind unter anderem:

  • Zahlen: currency, group, percentSign, minusSign, plusSign, unit, compact, exponentInteger
  • Daten: weekday, era, year, month, day, hour, minute, second, dayPeriod, timeZoneName
  • Listen: element
  • Relative Zeiten: Numerische Werte erscheinen als integer, Text erscheint als literal

Das Verständnis dieser Typen hilft dir, Styling-Code zu schreiben, der mit den Ausgaben beliebiger Formatierer korrekt umgehen kann.