كيفية تنسيق الوقت النسبي في React Router v7
تنسيق الطوابع الزمنية كعبارات 'منذ يومين'
المشكلة
إن عرض الطوابع الزمنية كعبارات زمنية نسبية مثل "منذ يومين" أو "خلال 3 ساعات" يجعل المحتوى يبدو فوريًا وسياقيًا. ومع ذلك، تتبع هذه العبارات قواعد نحوية معقدة تختلف بشكل كبير عبر اللغات. تضع اللغة الإنجليزية كلمة "ago" بعد الكميات الماضية و"in" قبل الكميات المستقبلية، لكن اللغات الأخرى قد تُغيّر صيغة وحدة الوقت نفسها، أو تعيد ترتيب الكلمات، أو تستخدم تراكيب نحوية مختلفة تمامًا. إن بناء هذه العبارات يدويًا بدمج السلاسل النصية ينتج مخرجات غير صحيحة في كل لغة باستثناء اللغة التي قمت ببرمجتها، مما يؤدي إلى إفساد تجربة المستخدم للجمهور الدولي.
الحل
استخدم تنسيق الوقت النسبي المراعي للغة لتحويل اختلافات الطوابع الزمنية إلى عبارات صحيحة نحويًا. احسب الفرق الزمني بين طابع زمني معين واللحظة الحالية، ثم قم بتنسيق هذا الفرق باستخدام قواعد لغة المستخدم. هذا يضمن أن تعبيرات الوقت الماضية والمستقبلية تتبع القواعد النحوية الصحيحة، وترتيب الكلمات، وأنماط التصريف لكل لغة دون التلاعب اليدوي بالنصوص.
الخطوات
1. إنشاء مساعد لحساب قيم الوقت النسبية
يتطلب مكون FormattedRelativeTime خاصية value رقمية وخاصية unit. قم ببناء دالة مساعدة تحسب الفرق الزمني وتختار وحدة مناسبة.
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 };
}
}
تقوم هذه الدالة بتحويل الطابع الزمني إلى قيمة رقمية موقعة ووحدة الوقت الأكثر ملاءمة بناءً على حجم الفرق.
2. إنشاء مكون للوقت النسبي باستخدام FormattedRelativeTime
استخدم مكون FormattedRelativeTime من react-intl، الذي يعرض الوقت النسبي المنسق ويمكنه التحديث على فترات اختيارياً.
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}
/>
);
}
خيار numeric="auto" يسمح للمنسق باستخدام عبارات مثل "أمس" بدلاً من "منذ يوم واحد" عندما يكون ذلك مناسباً. خاصية updateIntervalInSeconds الاختيارية تتحكم في عدد مرات إعادة عرض المكون للحفاظ على تحديث الوقت النسبي.
3. استخدام المكون في مسار React Router
قم بعرض مكون الوقت النسبي في أي مكون مسار حيث تحتاج إلى عرض الطوابع الزمنية.
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>
);
}
يقوم المكون تلقائياً بتنسيق الطابع الزمني وفقاً للغة المستخدم، مما ينتج عبارات مثل "منذ يومين" بالعربية، "2 days ago" بالإنجليزية، أو "il y a 2 jours" بالفرنسية، أو "hace 2 días" بالإسبانية.
4. تنسيق الوقت النسبي بشكل إجرائي باستخدام useIntl
للحالات التي تحتاج فيها إلى السلسلة المنسقة مباشرة (مثل تعيين سمات العنصر)، استخدم وظيفة formatRelativeTime من الخطاف 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={`تعليق بواسطة ${author}، نُشر ${relativeTime}`}>
<strong>{author}</strong>
<p>{text}</p>
</div>
);
}
يمنحك هذا النهج سلسلة منسقة يمكنك استخدامها في السمات، أو دمجها مع نص آخر، أو تمريرها إلى واجهات برمجة تطبيقات غير React مع الحفاظ على دعم اللغة الكامل.