Relative Zeitangaben in React Router v7 formatieren
Zeitstempel als Phrasen wie „vor 2 Tagen“ formatieren
Problem
Zeitstempel als relative Zeitangaben wie „vor 2 Tagen“ oder „in 3 Stunden“ darzustellen, sorgt für mehr Kontext und ein unmittelbares Gefühl im Content. Allerdings folgen diese Phrasen komplexen grammatikalischen Regeln, die sich je nach Sprache stark unterscheiden. Im Englischen steht „ago“ nach der Zeitangabe für die Vergangenheit und „in“ vor der Zeitangabe für die Zukunft. Andere Sprachen beugen jedoch die Zeiteinheit, ändern die Wortstellung oder verwenden ganz andere Strukturen. Wer diese Phrasen manuell mit String-Verkettung zusammensetzt, erhält in allen Sprachen außer der hartcodierten falsche Ausgaben – und verschlechtert so das Nutzererlebnis für internationale Zielgruppen.
Lösung
Nutzen Sie eine lokalisierungsfähige Formatierung für relative Zeitangaben, um Zeitdifferenzen in grammatikalisch korrekte Phrasen umzuwandeln. Berechnen Sie die Differenz zwischen einem Zeitstempel und dem aktuellen Zeitpunkt und formatieren Sie diese Differenz nach den Regeln der Nutzersprache. So stellen Sie sicher, dass Vergangenheits- und Zukunftsangaben die richtige Grammatik, Wortstellung und Flexion für jede Sprache haben – ganz ohne manuelle String-Manipulation.
Schritte
1. Helferfunktion zur Berechnung relativer Zeitwerte erstellen
Die FormattedRelativeTime-Komponente benötigt eine numerische value- und eine unit-Eigenschaft. Entwickeln Sie eine Hilfsfunktion, die die Zeitdifferenz berechnet und die 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 wandelt einen Zeitstempel in einen vorzeichenbehafteten numerischen Wert und die passendste Zeiteinheit um, basierend auf der Größe der Differenz.
2. Relative-Zeit-Komponente mit FormattedRelativeTime erstellen
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 Formatter, Ausdrücke wie „gestern" anstelle von „vor 1 Tag" zu verwenden, wenn dies angemessen ist. Die optionale Prop updateIntervalInSeconds steuert, wie oft die Komponente neu gerendert wird, um die relative Zeit aktuell zu halten.
3. Verwenden Sie die Komponente in einer React-Router-Route
Rendern Sie die Komponente für relative Zeit in jeder Routenkomponente, in der Sie Zeitstempel anzeigen müssen.
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 dem Gebietsschema des Benutzers und erzeugt Ausdrücke wie „vor 2 Tagen" auf Deutsch, „il y a 2 jours" auf Französisch oder „hace 2 días" auf Spanisch.
4. Formatieren Sie relative Zeit imperativ mit useIntl
Für Fälle, in denen Sie die formatierte Zeichenkette direkt benötigen (z. B. zum Setzen von Elementattributen), verwenden Sie die Funktion formatRelativeTime aus dem Hook useIntl.
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={`Comment by ${author}, posted ${relativeTime}`}>
<strong>{author}</strong>
<p>{text}</p>
</div>
);
}
Dieser Ansatz liefert Ihnen eine formatierte Zeichenkette, die Sie in Attributen verwenden, mit anderem Text verketten oder an Nicht-React-APIs übergeben können, während die vollständige Gebietsschema-Unterstützung erhalten bleibt.