كيفية تنسيق الوقت النسبي في TanStack Start v1
تنسيق الطوابع الزمنية كعبارات 'منذ يومين'
المشكلة
عرض الطوابع الزمنية كعبارات وقت نسبي مثل "منذ يومين" أو "خلال 3 ساعات" يجعل المعلومات الزمنية أكثر سهولة للمستخدمين. ومع ذلك، تتبع هذه العبارات قواعد نحوية معقدة تختلف باختلاف اللغة. تضع الإنجليزية "ago" بعد الكميات الماضية و"in" قبل الكميات المستقبلية، لكن اللغات الأخرى قد تستخدم ترتيبات كلمات مختلفة، أو تصرّف وحدات الزمن، أو تستخدم هياكل نحوية مختلفة تماماً. إنشاء هذه العبارات يدوياً باستخدام دمج النصوص ينتج مخرجات غير صحيحة في كل لغة باستثناء اللغة التي قمت بترميزها بشكل ثابت.
تتطلب واجهة برمجة التطبيقات الأساسية للتنسيق قيمة رقمية ووحدة زمنية، لكن الطوابع الزمنية تصل ككائنات Date أو قيم بالميلي ثانية. يتطلب تحويل الطابع الزمني إلى الوحدة والقيمة المناسبة حساب الفرق من اللحظة الحالية واختيار الوحدة الأكثر طبيعية للتعبير عن هذا الفرق.
الحل
احسب الفرق الزمني بين طابع زمني واللحظة الحالية، واختر الوحدة الأنسب لهذا الفرق، ثم قم بتنسيق النتيجة باستخدام تنسيق الوقت النسبي الخاص باللغة المحلية. يحول هذا الطوابع الزمنية الخام إلى عبارات صحيحة نحوياً تتكيف مع لغة المستخدم.
استخدم تنسيق الوقت النسبي في react-intl مع دالة مساعدة تحدد أفضل وحدة. تقارن الدالة المساعدة الفرق الزمني بالعتبات لكل وحدة وتُرجع كلاً من القيمة الرقمية واسم الوحدة. يطبق المنسق بعد ذلك القواعد النحوية الصحيحة للغة المحلية النشطة.
الخطوات
1. إنشاء دالة مساعدة لاختيار الوحدة
قم ببناء دالة تحسب الفرق الزمني وتختار الوحدة الأكثر طبيعية للتعبير عنه.
type RelativeTimeUnit =
| "second"
| "minute"
| "hour"
| "day"
| "week"
| "month"
| "year";
interface RelativeTimeValue {
value: number;
unit: RelativeTimeUnit;
}
export function selectRelativeTimeUnit(
timestamp: Date | number,
baseTime: Date | number = Date.now(),
): RelativeTimeValue {
const date = typeof timestamp === "number" ? timestamp : timestamp.getTime();
const base = typeof baseTime === "number" ? baseTime : baseTime.getTime();
const diffMs = date - base;
const absDiff = Math.abs(diffMs);
const minute = 60 * 1000;
const hour = 60 * minute;
const day = 24 * hour;
const week = 7 * day;
const month = 30 * day;
const year = 365 * day;
if (absDiff < minute) {
return { value: Math.round(diffMs / 1000), unit: "second" };
}
if (absDiff < hour) {
return { value: Math.round(diffMs / minute), unit: "minute" };
}
if (absDiff < day) {
return { value: Math.round(diffMs / hour), unit: "hour" };
}
if (absDiff < week) {
return { value: Math.round(diffMs / day), unit: "day" };
}
if (absDiff < month) {
return { value: Math.round(diffMs / week), unit: "week" };
}
if (absDiff < year) {
return { value: Math.round(diffMs / month), unit: "month" };
}
return { value: Math.round(diffMs / year), unit: "year" };
}
تقوم هذه الدالة بتحويل الطابع الزمني إلى قيمة ووحدة نسبية من خلال مقارنة فرق الوقت مع عتبات ثابتة. تُرجع قيمًا سالبة للأوقات الماضية وقيمًا موجبة للأوقات المستقبلية، والتي يفسرها المنسق بشكل صحيح.
2. إنشاء مكون للوقت النسبي
قم ببناء مكون يجمع بين مساعد اختيار الوحدة وتنسيق react-intl.
import { FormattedRelativeTime } from "react-intl";
import { selectRelativeTimeUnit } from "./selectRelativeTimeUnit";
interface RelativeTimeProps {
date: Date | number;
numeric?: "always" | "auto";
style?: "long" | "short" | "narrow";
}
export function RelativeTime({
date,
numeric = "auto",
style = "long",
}: RelativeTimeProps) {
const { value, unit } = selectRelativeTimeUnit(date);
return (
<FormattedRelativeTime
value={value}
unit={unit}
numeric={numeric}
style={style}
/>
);
}
يقبل هذا المكون طابعًا زمنيًا وخيارات التنسيق، ويحسب القيمة والوحدة النسبية، ثم يفوض إلى مكون react-intl للعرض الواعي باللغة المحلية. ينتج خيار numeric="auto" عبارات مثل "أمس" بدلاً من "منذ يوم واحد" عند الاقتضاء.
3. استخدام المكون في المسارات الخاصة بك
قم باستيراد وعرض المكون في أي مكان تحتاج فيه إلى عرض الطوابع الزمنية النسبية.
import { createFileRoute } from "@tanstack/react-router";
import { RelativeTime } from "../components/RelativeTime";
export const Route = createFileRoute("/posts/$postId")({
component: PostPage,
});
function PostPage() {
const post = {
title: "Understanding Relative Time",
publishedAt: new Date("2024-11-15T10:30:00Z"),
updatedAt: new Date(Date.now() - 2 * 60 * 60 * 1000),
};
return (
<article>
<h1>{post.title}</h1>
<p>
Published <RelativeTime date={post.publishedAt} />
</p>
<p>
Updated <RelativeTime date={post.updatedAt} style="short" />
</p>
</article>
);
}
يعمل المكون في كل من سياقات العرض من جانب الخادم والعميل. على الخادم ينتج عبارة الوقت النسبي الأولية، وعلى العميل يعرض نفس العبارة باستخدام اللغة المحلية للمستخدم من IntlProvider.
4. إضافة خيار تنسيق حتمي
للحالات التي تحتاج فيها إلى السلسلة المنسقة مباشرة، قم بإنشاء مساعد قائم على الخطافات.
import { useIntl } from "react-intl";
import { selectRelativeTimeUnit } from "./selectRelativeTimeUnit";
export function useRelativeTime() {
const intl = useIntl();
return (
date: Date | number,
options?: {
numeric?: "always" | "auto";
style?: "long" | "short" | "narrow";
},
) => {
const { value, unit } = selectRelativeTimeUnit(date);
return intl.formatRelativeTime(value, unit, options);
};
}
يُرجع هذا الخطاف دالة تنسق الطوابع الزمنية بشكل حتمي، وهو مفيد لتعيين سمات النص أو حساب القيم خارج JSX.
5. استخدام الخطاف لسياقات غير المكونات
قم باستدعاء الخطاف في المكونات حيث تحتاج إلى سلاسل منسقة للسمات أو المنطق.
import { createFileRoute } from "@tanstack/react-router";
import { useRelativeTime } from "../hooks/useRelativeTime";
export const Route = createFileRoute("/events/$eventId")({
component: EventPage,
});
function EventPage() {
const formatRelativeTime = useRelativeTime();
const event = {
name: "Product Launch",
startTime: new Date(Date.now() + 3 * 24 * 60 * 60 * 1000),
};
const timeUntil = formatRelativeTime(event.startTime);
return (
<div>
<h1>{event.name}</h1>
<time dateTime={event.startTime.toISOString()} title={timeUntil}>
{timeUntil}
</time>
</div>
);
}
يوفر الخطاف نفس منطق التنسيق في شكل وظيفي، مما يسمح لك باستخدام سلاسل الوقت النسبي في السمات أو القيم المحسوبة أو في أي مكان لا يمكن فيه عرض مكون.