API Intl.RelativeTimeFormat
Formatez les chaînes de temps relatif en JavaScript avec une prise en charge complète de l'internationalisation
Introduction
L'affichage des horodatages relatifs comme "il y a 3 heures" ou "dans 2 jours" est une exigence courante dans les applications web. Les flux de médias sociaux, les sections de commentaires, les systèmes de notification et les journaux d'activité nécessitent tous des affichages temporels lisibles par l'humain qui se mettent à jour au fur et à mesure que le contenu vieillit.
Construire cette fonctionnalité à partir de zéro présente des défis. Vous devez calculer les différences de temps, sélectionner les unités appropriées, gérer les règles de pluralisation dans différentes langues et maintenir des traductions pour chaque locale supportée. Cette complexité explique pourquoi les développeurs se tournaient traditionnellement vers des bibliothèques comme Moment.js, ajoutant une taille de bundle significative pour ce qui semble être un formatage simple.
L'API Intl.RelativeTimeFormat fournit une solution native. Elle formate les chaînes de temps relatif avec un support complet d'internationalisation, gérant automatiquement les règles de pluralisation et les conventions culturelles. L'API fonctionne sur tous les principaux navigateurs avec une couverture mondiale de 95%, éliminant le besoin de dépendances externes tout en produisant des résultats naturels dans des dizaines de langues.
Utilisation de base
Le constructeur Intl.RelativeTimeFormat crée une instance de formateur qui convertit des valeurs numériques et des unités de temps en chaînes localisées.
const rtf = new Intl.RelativeTimeFormat('en');
console.log(rtf.format(-1, 'day'));
// "1 day ago"
console.log(rtf.format(2, 'hour'));
// "in 2 hours"
console.log(rtf.format(-3, 'month'));
// "3 months ago"
La méthode format() prend deux paramètres :
value: Un nombre indiquant la quantité de tempsunit: Une chaîne spécifiant l'unité de temps
Les valeurs négatives indiquent des temps passés, les valeurs positives indiquent des temps futurs. L'API gère automatiquement la pluralisation, produisant "il y a 1 jour" ou "il y a 2 jours" selon la valeur.
Unités de temps prises en charge
L'API prend en charge huit unités de temps, chacune acceptant les formes singulières et plurielles :
const rtf = new Intl.RelativeTimeFormat('en');
// Ces lignes produisent un résultat identique
console.log(rtf.format(-5, 'second'));
// "5 seconds ago"
console.log(rtf.format(-5, 'seconds'));
// "5 seconds ago"
Unités disponibles de la plus petite à la plus grande :
secondousecondsminuteouminuteshourouhoursdayoudaysweekouweeksmonthoumonthsquarterouquartersyearouyears
L'unité trimestre s'avère utile dans les applications commerciales qui suivent les périodes fiscales, tandis que les autres couvrent les besoins typiques de formatage de temps relatif.
Sortie en langage naturel
L'option numeric contrôle si le formateur utilise des valeurs numériques ou des alternatives en langage naturel.
const rtfNumeric = new Intl.RelativeTimeFormat('en', {
numeric: 'always'
});
console.log(rtfNumeric.format(-1, 'day'));
// "1 day ago"
console.log(rtfNumeric.format(0, 'day'));
// "in 0 days"
console.log(rtfNumeric.format(1, 'day'));
// "in 1 day"
Définir numeric sur auto produit des expressions plus idiomatiques pour les valeurs courantes :
const rtfAuto = new Intl.RelativeTimeFormat('en', {
numeric: 'auto'
});
console.log(rtfAuto.format(-1, 'day'));
// "yesterday"
console.log(rtfAuto.format(0, 'day'));
// "today"
console.log(rtfAuto.format(1, 'day'));
// "tomorrow"
Cette sortie en langage naturel crée des interfaces plus conversationnelles. L'option auto fonctionne avec toutes les unités de temps, bien que l'effet soit plus notable avec les jours. D'autres langues ont leurs propres alternatives idiomatiques que l'API gère automatiquement.
Styles de formatage
L'option style ajuste la verbosité de sortie pour différents contextes d'interface :
const rtfLong = new Intl.RelativeTimeFormat('en', {
style: 'long'
});
console.log(rtfLong.format(-2, 'hour'));
// "2 hours ago"
const rtfShort = new Intl.RelativeTimeFormat('en', {
style: 'short'
});
console.log(rtfShort.format(-2, 'hour'));
// "2 hr. ago"
const rtfNarrow = new Intl.RelativeTimeFormat('en', {
style: 'narrow'
});
console.log(rtfNarrow.format(-2, 'hour'));
// "2h ago"
Utilisez le style long (par défaut) pour les interfaces standard où la lisibilité est primordiale. Utilisez le style short pour les mises en page à espace limité comme les interfaces mobiles ou les tableaux de données. Utilisez le style narrow pour les affichages extrêmement compacts où chaque caractère compte.
Calcul des différences de temps
L'API Intl.RelativeTimeFormat formate les valeurs mais ne les calcule pas. Vous devez calculer vous-même les différences de temps et sélectionner les unités appropriées. Cette séparation des préoccupations vous donne le contrôle sur la logique de calcul tout en déléguant la complexité du formatage à l'API.
Calcul de base de la différence de temps
Pour une unité de temps spécifique, calculez la différence entre deux dates :
const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' });
function formatDaysAgo(date) {
const now = new Date();
const diffInMs = date - now;
const diffInDays = Math.round(diffInMs / (1000 * 60 * 60 * 24));
return rtf.format(diffInDays, 'day');
}
const yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);
console.log(formatDaysAgo(yesterday));
// "yesterday"
const nextWeek = new Date();
nextWeek.setDate(nextWeek.getDate() + 7);
console.log(formatDaysAgo(nextWeek));
// "in 7 days"
Cette approche fonctionne lorsque vous savez quelle unité convient à votre cas d'utilisation. Les horodatages des commentaires pourraient toujours utiliser des heures ou des jours, tandis que la planification d'événements pourrait se concentrer sur les jours ou les semaines.
Sélection automatique d'unité
Pour un formatage de temps relatif à usage général, sélectionnez l'unité la plus appropriée en fonction de l'ampleur de la différence de temps :
const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' });
const units = {
year: 24 * 60 * 60 * 1000 * 365,
month: 24 * 60 * 60 * 1000 * 365 / 12,
week: 24 * 60 * 60 * 1000 * 7,
day: 24 * 60 * 60 * 1000,
hour: 60 * 60 * 1000,
minute: 60 * 1000,
second: 1000
};
function formatRelativeTime(date) {
const now = new Date();
const diffInMs = date - now;
const absDiff = Math.abs(diffInMs);
for (const [unit, msValue] of Object.entries(units)) {
if (absDiff >= msValue || unit === 'second') {
const value = Math.round(diffInMs / msValue);
return rtf.format(value, unit);
}
}
}
const fiveMinutesAgo = new Date(Date.now() - 5 * 60 * 1000);
console.log(formatRelativeTime(fiveMinutesAgo));
// "5 minutes ago"
const threeDaysAgo = new Date(Date.now() - 3 * 24 * 60 * 60 * 1000);
console.log(formatRelativeTime(threeDaysAgo));
// "3 days ago"
Cette implémentation parcourt les unités de la plus grande à la plus petite, sélectionnant la première unité où la différence de temps dépasse la valeur en millisecondes de l'unité. Le recours aux secondes garantit que la fonction renvoie toujours un résultat.
Les seuils d'unité utilisent des valeurs approximatives. Les mois sont calculés comme 1/12 d'une année plutôt que de tenir compte des longueurs variables des mois. Cette approximation fonctionne bien pour les affichages de temps relatifs où la précision importe moins que la lisibilité.
Support d'internationalisation
Le formateur respecte les conventions spécifiques à la locale pour l'affichage du temps relatif. Différentes langues ont différentes règles de pluralisation, différents ordres de mots et différentes expressions idiomatiques.
const rtfEnglish = new Intl.RelativeTimeFormat('en', {
numeric: 'auto'
});
console.log(rtfEnglish.format(-1, 'day'));
// "yesterday"
const rtfSpanish = new Intl.RelativeTimeFormat('es', {
numeric: 'auto'
});
console.log(rtfSpanish.format(-1, 'day'));
// "ayer"
const rtfJapanese = new Intl.RelativeTimeFormat('ja', {
numeric: 'auto'
});
console.log(rtfJapanese.format(-1, 'day'));
// "昨日"
Les règles de pluralisation varient considérablement selon les langues. L'anglais distingue entre un et plusieurs (1 day vs 2 days). L'arabe possède six formes plurielles selon le nombre. Le japonais utilise la même forme quelle que soit la quantité. L'API gère automatiquement ces complexités.
const rtfArabic = new Intl.RelativeTimeFormat('ar');
console.log(rtfArabic.format(-1, 'day'));
// "قبل يوم واحد"
console.log(rtfArabic.format(-2, 'day'));
// "قبل يومين"
console.log(rtfArabic.format(-3, 'day'));
// "قبل 3 أيام"
console.log(rtfArabic.format(-11, 'day'));
// "قبل 11 يومًا"
Le formateur gère également la direction du texte pour les langues de droite à gauche et applique des conventions de formatage culturellement appropriées. Cette localisation automatique élimine le besoin de maintenir des fichiers de traduction ou d'implémenter une logique de pluralisation personnalisée.
Formatage avancé avec formatToParts
La méthode formatToParts() renvoie la chaîne formatée sous forme d'un tableau d'objets, permettant une personnalisation du style ou une manipulation des composants individuels.
const rtf = new Intl.RelativeTimeFormat('en');
const parts = rtf.formatToParts(-5, 'second');
console.log(parts);
// [
// { type: 'integer', value: '5', unit: 'second' },
// { type: 'literal', value: ' seconds ago' }
// ]
Chaque objet part contient :
type: soitintegerpour les valeurs numériques, soitliteralpour le textevalue: le contenu textuel de cette partieunit: l'unité de temps (présente sur les parties de type integer)
Cette structure permet un rendu personnalisé où vous pourriez vouloir styliser les nombres différemment du texte, ou extraire des composants spécifiques pour l'affichage :
const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' });
function formatWithStyledNumber(value, unit) {
const parts = rtf.formatToParts(value, unit);
return parts.map(part => {
if (part.type === 'integer') {
return `<strong>${part.value}</strong>`;
}
return part.value;
}).join('');
}
console.log(formatWithStyledNumber(-5, 'hour'));
// "<strong>5</strong> hours ago"
Lorsque vous utilisez numeric: 'auto' avec des valeurs qui ont des alternatives en langage naturel, formatToParts() renvoie une seule partie littérale :
const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' });
const parts = rtf.formatToParts(-1, 'day');
console.log(parts);
// [
// { type: 'literal', value: 'yesterday' }
// ]
Ce comportement vous permet de détecter quand le langage naturel est utilisé par rapport au formatage numérique, vous permettant d'appliquer différents styles ou comportements en fonction du type de sortie.
Optimisation des performances
La création d'instances Intl.RelativeTimeFormat implique le chargement de données locales et l'initialisation des règles de formatage. Cette opération est suffisamment coûteuse pour éviter de la répéter inutilement.
Mise en cache des instances de formateur
Créez les formateurs une seule fois et réutilisez-les :
const formatterCache = new Map();
function getFormatter(locale, options = {}) {
const cacheKey = `${locale}-${JSON.stringify(options)}`;
if (!formatterCache.has(cacheKey)) {
formatterCache.set(
cacheKey,
new Intl.RelativeTimeFormat(locale, options)
);
}
return formatterCache.get(cacheKey);
}
// Réutilisation des formateurs mis en cache
const rtf = getFormatter('en', { numeric: 'auto' });
console.log(rtf.format(-1, 'day'));
// "yesterday"
Cette stratégie de mise en cache devient importante lors du formatage de nombreux horodatages, comme pour l'affichage de flux d'activités ou de fils de commentaires.
Minimiser les frais de calcul
Stockez les horodatages plutôt que de calculer les temps relatifs à plusieurs reprises :
// Stockage de la date de création
const comment = {
text: "Great article!",
createdAt: new Date('2025-10-14T10:30:00Z')
};
// Calcul du temps relatif uniquement lors du rendu
function renderComment(comment, locale) {
const rtf = getFormatter(locale, { numeric: 'auto' });
const units = {
day: 24 * 60 * 60 * 1000,
hour: 60 * 60 * 1000,
minute: 60 * 1000,
second: 1000
};
const diffInMs = comment.createdAt - new Date();
const absDiff = Math.abs(diffInMs);
for (const [unit, msValue] of Object.entries(units)) {
if (absDiff >= msValue || unit === 'second') {
const value = Math.round(diffInMs / msValue);
return rtf.format(value, unit);
}
}
}
Cette approche sépare le stockage des données de la présentation, vous permettant de recalculer les temps relatifs lorsque les paramètres régionaux de l'utilisateur changent ou lors des mises à jour de rendu, sans modifier les données sous-jacentes.
Implémentation pratique
La combinaison de la logique de calcul avec le formatage produit une fonction utilitaire réutilisable adaptée aux applications de production :
class RelativeTimeFormatter {
constructor(locale = 'en', options = { numeric: 'auto' }) {
this.formatter = new Intl.RelativeTimeFormat(locale, options);
this.units = [
{ name: 'year', ms: 24 * 60 * 60 * 1000 * 365 },
{ name: 'month', ms: 24 * 60 * 60 * 1000 * 365 / 12 },
{ name: 'week', ms: 24 * 60 * 60 * 1000 * 7 },
{ name: 'day', ms: 24 * 60 * 60 * 1000 },
{ name: 'hour', ms: 60 * 60 * 1000 },
{ name: 'minute', ms: 60 * 1000 },
{ name: 'second', ms: 1000 }
];
}
format(date) {
const now = new Date();
const diffInMs = date - now;
const absDiff = Math.abs(diffInMs);
for (const unit of this.units) {
if (absDiff >= unit.ms || unit.name === 'second') {
const value = Math.round(diffInMs / unit.ms);
return this.formatter.format(value, unit.name);
}
}
}
}
// Utilisation
const formatter = new RelativeTimeFormatter('en');
const fiveMinutesAgo = new Date(Date.now() - 5 * 60 * 1000);
console.log(formatter.format(fiveMinutesAgo));
// "5 minutes ago"
const tomorrow = new Date(Date.now() + 24 * 60 * 60 * 1000);
console.log(formatter.format(tomorrow));
// "tomorrow"
Cette classe encapsule à la fois le formateur et la logique de sélection d'unité, fournissant une interface propre qui accepte des objets Date et renvoie des chaînes formatées.
Intégration avec les frameworks
Dans les applications React, créez le formateur une seule fois et transmettez-le via le contexte ou les props :
import { createContext, useContext } from 'react';
const RelativeTimeContext = createContext(null);
export function RelativeTimeProvider({ locale, children }) {
const formatter = new RelativeTimeFormatter(locale);
return (
<RelativeTimeContext.Provider value={formatter}>
{children}
</RelativeTimeContext.Provider>
);
}
export function useRelativeTime() {
const formatter = useContext(RelativeTimeContext);
if (!formatter) {
throw new Error('useRelativeTime must be used within RelativeTimeProvider');
}
return formatter;
}
// Utilisation dans un composant
function CommentTimestamp({ date }) {
const formatter = useRelativeTime();
return <time>{formatter.format(date)}</time>;
}
Ce modèle garantit que les formateurs sont créés une seule fois par locale et partagés entre tous les composants qui nécessitent un formatage de temps relatif.
Compatibilité des navigateurs
Intl.RelativeTimeFormat fonctionne sur tous les navigateurs modernes avec une couverture mondiale de 95 % :
- Chrome 71+
- Firefox 65+
- Safari 14+
- Edge 79+
Internet Explorer ne prend pas en charge cette API. Pour les applications nécessitant la prise en charge d'IE, des polyfills sont disponibles, bien que l'implémentation native offre de meilleures performances et des tailles de bundle plus réduites.
Quand utiliser cette API
Intl.RelativeTimeFormat fonctionne mieux pour :
- Afficher l'âge du contenu dans les flux et les chronologies
- Montrer les horodatages des commentaires ou des publications
- Formater la planification d'événements par rapport à l'heure actuelle
- Construire des systèmes de notification avec des horodatages relatifs
- Créer des journaux d'activité avec des temps lisibles par l'humain
L'API n'est pas adaptée pour :
- Le formatage de date et d'heure absolues (utilisez
Intl.DateTimeFormat) - Le suivi précis du temps nécessitant une précision exacte en millisecondes
- Les minuteurs de compte à rebours qui se mettent à jour chaque seconde
- Les calculs arithmétiques de dates ou de calendrier
Pour les applications nécessitant à la fois des affichages de temps relatifs et absolus, combinez Intl.RelativeTimeFormat avec Intl.DateTimeFormat. Affichez des temps relatifs pour le contenu récent et passez aux dates absolues pour le contenu plus ancien :
function formatTimestamp(date, locale = 'en') {
const now = new Date();
const diffInMs = Math.abs(date - now);
const sevenDaysInMs = 7 * 24 * 60 * 60 * 1000;
if (diffInMs < sevenDaysInMs) {
const rtf = new RelativeTimeFormatter(locale);
return rtf.format(date);
} else {
const dtf = new Intl.DateTimeFormat(locale, {
year: 'numeric',
month: 'short',
day: 'numeric'
});
return dtf.format(date);
}
}
const yesterday = new Date(Date.now() - 24 * 60 * 60 * 1000);
console.log(formatTimestamp(yesterday));
// "hier"
const lastMonth = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
console.log(formatTimestamp(lastMonth));
// "14 sept. 2025"
Cette approche hybride offre les avantages du langage naturel des temps relatifs pour le contenu récent tout en maintenant la clarté pour les horodatages plus anciens.