Wie man relative Zeitangaben in React Router v7 formatiert

Zeitstempel als '2 Tage zuvor' Ausdrücke formatieren

Problem

Die Anzeige von Zeitstempeln als relative Zeitangaben wie "vor 2 Tagen" oder "in 3 Stunden" lässt Inhalte unmittelbar und kontextbezogen wirken. Diese Ausdrücke folgen jedoch komplexen grammatikalischen Regeln, die sich zwischen verschiedenen Sprachen stark unterscheiden. Im Englischen steht "ago" nach Zeitangaben in der Vergangenheit und "in" vor zukünftigen Angaben, während andere Sprachen die Zeiteinheit selbst flektieren, Wörter umstellen oder völlig andere grammatikalische Strukturen verwenden können. Die manuelle Konstruktion dieser Ausdrücke durch Verkettung von Zeichenketten erzeugt in jeder Sprache außer der hardcodierten falsche Ausgaben und beeinträchtigt das Nutzererlebnis für internationale Zielgruppen.

Lösung

Verwenden Sie lokalisierungsbewusste relative Zeitformatierung, um Zeitstempeldifferenzen in grammatikalisch korrekte Ausdrücke umzuwandeln. Berechnen Sie die Zeitdifferenz zwischen einem gegebenen Zeitstempel und dem aktuellen Moment und formatieren Sie diese Differenz unter Verwendung der Lokalisierungsregeln des Benutzers. Dies stellt sicher, dass Zeitausdrücke für Vergangenheit und Zukunft der korrekten Grammatik, Wortstellung und den Flexionsmustern für jede Sprache folgen, ohne manuelle Zeichenkettenmanipulation.

Schritte

1. Erstellen Sie einen Helfer zur Berechnung relativer Zeitwerte

Die FormattedRelativeTime-Komponente benötigt einen numerischen value- und einen unit-Prop. Erstellen Sie eine Hilfsfunktion, die die Zeitdifferenz berechnet und eine passende Einheit auswählt.

export function getRelativeTimeValue(date: Date | number) {
  const now = Date.now();
  const timestamp = typeof date === "number" ? date : date.getTime();
  const diffInSeconds = Math.round((timestamp - now) / 1000);

  const minute = 60;
  const hour = minute * 60;
  const day = hour * 24;
  const week = day * 7;
  const month = day * 30;
  const year = day * 365;

  const absDiff = Math.abs(diffInSeconds);

  if (absDiff < minute) {
    return { value: diffInSeconds, unit: "second" as const };
  } else if (absDiff < hour) {
    return {
      value: Math.round(diffInSeconds / minute),
      unit: "minute" as const,
    };
  } else if (absDiff < day) {
    return { value: Math.round(diffInSeconds / hour), unit: "hour" as const };
  } else if (absDiff < week) {
    return { value: Math.round(diffInSeconds / day), unit: "day" as const };
  } else if (absDiff < month) {
    return { value: Math.round(diffInSeconds / week), unit: "week" as const };
  } else if (absDiff < year) {
    return { value: Math.round(diffInSeconds / month), unit: "month" as const };
  } else {
    return { value: Math.round(diffInSeconds / year), unit: "year" as const };
  }
}

Diese Funktion konvertiert einen Zeitstempel in einen vorzeichenbehafteten numerischen Wert und die am besten geeignete Zeiteinheit basierend auf der Größenordnung der Differenz.

2. Erstellen einer relativen Zeitkomponente mit FormattedRelativeTime

Verwenden Sie die FormattedRelativeTime-Komponente aus react-intl, die formatierte relative Zeitangaben rendert und optional in Intervallen aktualisiert werden kann.

import { FormattedRelativeTime } from "react-intl";
import { getRelativeTimeValue } from "./getRelativeTimeValue";

interface RelativeTimeProps {
  date: Date | number;
  updateIntervalInSeconds?: number;
}

export function RelativeTime({
  date,
  updateIntervalInSeconds,
}: RelativeTimeProps) {
  const { value, unit } = getRelativeTimeValue(date);

  return (
    <FormattedRelativeTime
      value={value}
      unit={unit}
      numeric="auto"
      updateIntervalInSeconds={updateIntervalInSeconds}
    />
  );
}

Die Option numeric="auto" ermöglicht es dem Formatierer, bei Bedarf Phrasen wie "gestern" anstelle von "vor 1 Tag" zu verwenden. Der optionale Parameter updateIntervalInSeconds steuert, wie oft die Komponente neu gerendert wird, um die relative Zeit aktuell zu halten.

3. Verwendung der Komponente in einer React Router-Route

Rendern Sie die Komponente für relative Zeit in jeder Routenkomponente, in der Zeitstempel angezeigt werden sollen.

import { RelativeTime } from "./RelativeTime";

interface Post {
  id: string;
  title: string;
  content: string;
  createdAt: number;
}

export function PostDetail({ post }: { post: Post }) {
  return (
    <article>
      <h1>{post.title}</h1>
      <time dateTime={new Date(post.createdAt).toISOString()}>
        <RelativeTime date={post.createdAt} />
      </time>
      <p>{post.content}</p>
    </article>
  );
}

Die Komponente formatiert den Zeitstempel automatisch entsprechend der Sprache des Benutzers und erzeugt Phrasen wie "vor 2 Tagen" auf Deutsch, "il y a 2 jours" auf Französisch oder "hace 2 días" auf Spanisch.

4. Imperatives Formatieren relativer Zeit mit useIntl

Für Fälle, in denen Sie die formatierte Zeichenfolge direkt benötigen (z.B. zum Setzen von Element-Attributen), verwenden Sie die Funktion formatRelativeTime aus dem useIntl-Hook.

import { useIntl } from "react-intl";
import { getRelativeTimeValue } from "./getRelativeTimeValue";

interface CommentProps {
  author: string;
  text: string;
  timestamp: number;
}

export function Comment({ author, text, timestamp }: CommentProps) {
  const intl = useIntl();
  const { value, unit } = getRelativeTimeValue(timestamp);
  const relativeTime = intl.formatRelativeTime(value, unit, {
    numeric: "auto",
  });

  return (
    <div aria-label={`Kommentar von ${author}, gepostet ${relativeTime}`}>
      <strong>{author}</strong>
      <p>{text}</p>
    </div>
  );
}

Dieser Ansatz liefert eine formatierte Zeichenfolge, die Sie in Attributen verwenden, mit anderem Text verketten oder an nicht-React-APIs übergeben können, während die vollständige Sprachunterstützung erhalten bleibt.