Comment formater le temps relatif dans React Router v7

Formater les horodatages en phrases du type « il y a 2 jours »

Problème

Afficher les horodatages sous forme de phrases de temps relatif comme « il y a 2 jours » ou « dans 3 heures » rend le contenu immédiat et contextuel. Cependant, ces phrases suivent des règles grammaticales complexes qui varient considérablement d'une langue à l'autre. L'anglais place « ago » après les quantités passées et « in » avant les quantités futures, mais d'autres langues peuvent fléchir l'unité de temps elle-même, réorganiser les mots ou utiliser des structures grammaticales entièrement différentes. Construire manuellement ces phrases par concaténation de chaînes produit un résultat incorrect dans toutes les langues sauf celle que vous avez codée en dur, ce qui dégrade l'expérience utilisateur pour les audiences internationales.

Solution

Utilisez le formatage de temps relatif tenant compte de la locale pour convertir les différences d'horodatage en phrases grammaticalement correctes. Calculez la différence de temps entre un horodatage donné et le moment actuel, puis formatez cette différence en utilisant les règles de la locale de l'utilisateur. Cela garantit que les expressions de temps passé et futur suivent la grammaire, l'ordre des mots et les modèles de flexion corrects pour chaque langue sans manipulation manuelle de chaînes.

Étapes

1. Créer une fonction auxiliaire pour calculer les valeurs de temps relatif

Le composant FormattedRelativeTime nécessite une propriété value numérique et une propriété unit. Créez une fonction auxiliaire qui calcule la différence de temps et sélectionne une unité appropriée.

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

Cette fonction convertit un horodatage en une valeur numérique signée et l'unité de temps la plus appropriée en fonction de l'amplitude de la différence.

2. Créer un composant de temps relatif utilisant FormattedRelativeTime

Utilisez le composant FormattedRelativeTime de react-intl, qui affiche le temps relatif formaté et peut éventuellement se mettre à jour à intervalles réguliers.

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

L'option numeric="auto" permet au formateur d'utiliser des expressions comme "hier" au lieu de "il y a 1 jour" lorsque cela est approprié. La prop optionnelle updateIntervalInSeconds contrôle la fréquence à laquelle le composant se rafraîchit pour maintenir l'heure relative à jour.

3. Utiliser le composant dans une route React Router

Affichez le composant de temps relatif dans n'importe quel composant de route où vous devez afficher des horodatages.

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

Le composant formate automatiquement l'horodatage selon la locale de l'utilisateur, produisant des expressions comme "il y a 2 jours" en français, "2 days ago" en anglais, ou "hace 2 días" en espagnol.

4. Formater le temps relatif de manière impérative avec useIntl

Pour les cas où vous avez besoin de la chaîne formatée directement (comme pour définir des attributs d'éléments), utilisez la fonction formatRelativeTime du 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>
  );
}

Cette approche vous donne une chaîne formatée que vous pouvez utiliser dans des attributs, concaténer avec d'autres textes, ou transmettre à des API non-React tout en maintenant une prise en charge complète des locales.