feat: enhance date formatting utility with locale support and integrate it across preview components.

This commit is contained in:
JOYCEQL
2026-02-11 22:00:18 +08:00
parent d7389d0a79
commit 0917d678a3
7 changed files with 79 additions and 29 deletions
+14 -11
View File
@@ -3,20 +3,23 @@
import { ThemeProvider } from "next-themes";
import { Analytics } from "@vercel/analytics/next";
import { HeroUIProvider } from "@heroui/react";
import { useLocale } from "next-intl";
export function Providers({ children }: { children: React.ReactNode }) {
const locale = useLocale();
return (
<HeroUIProvider>
<ThemeProvider
attribute="class"
defaultTheme="light"
enableSystem
disableTransitionOnChange
storageKey="magic-resume-theme"
>
{children}
<Analytics />
</ThemeProvider>
<HeroUIProvider locale={locale}>
<ThemeProvider
attribute="class"
defaultTheme="light"
enableSystem
disableTransitionOnChange
storageKey="magic-resume-theme"
>
{children}
<Analytics />
</ThemeProvider>
</HeroUIProvider>
);
}
+1 -1
View File
@@ -65,7 +65,7 @@ const BaseInfo = ({
key: field.key,
value:
field.key === "birthDate" && basic[field.key]
? formatDateString(basic[field.key] as string)
? formatDateString(basic[field.key] as string, locale)
: (basic[field.key] as string),
icon: basic.icons?.[field.key] || "User",
label: field.label,
+4 -1
View File
@@ -4,6 +4,8 @@ import SectionTitle from "./SectionTitle";
import { GlobalSettings, CustomItem } from "@/types/resume";
import { useResumeStore } from "@/store/useResumeStore";
import { normalizeRichTextContent } from "@/lib/richText";
import { formatDateString } from "@/lib/utils";
import { useLocale } from "next-intl";
interface CustomSectionProps {
sectionId: string;
@@ -21,6 +23,7 @@ const CustomSection = ({
showTitle = true,
}: CustomSectionProps) => {
const { setActiveSection } = useResumeStore();
const locale = useLocale();
const visibleItems = items?.filter((item) => {
return item.visible && (item.title || item.description);
});
@@ -86,7 +89,7 @@ const CustomSection = ({
fontSize: `${globalSettings?.subheaderSize || 16}px`,
}}
>
{item.dateRange}
{formatDateString(item.dateRange, locale)}
</span>
</motion.div>
+3 -2
View File
@@ -85,8 +85,9 @@ const EducationSection = ({
fontSize: `${globalSettings?.subheaderSize || 16}px`,
}}
>
{`${formatDateString(edu.startDate)} - ${formatDateString(
edu.endDate
{`${formatDateString(edu.startDate, locale)} - ${formatDateString(
edu.endDate,
locale
)}`}
</span>
</motion.div>
+4 -1
View File
@@ -5,6 +5,8 @@ import { Experience, GlobalSettings } from "@/types/resume";
import SectionTitle from "./SectionTitle";
import { useResumeStore } from "@/store/useResumeStore";
import { normalizeRichTextContent } from "@/lib/richText";
import { formatDateString } from "@/lib/utils";
import { useLocale } from "next-intl";
interface ExperienceSectionProps {
experiences?: Experience[];
@@ -22,6 +24,7 @@ const ExperienceItem = React.forwardRef<HTMLDivElement, ExperienceItemProps>(
const centerSubtitle = globalSettings?.centerSubtitle;
const flexLayout = globalSettings?.flexibleHeaderLayout;
const gridColumns = centerSubtitle ? 3 : 2;
const locale = useLocale();
return (
<motion.div
@@ -55,7 +58,7 @@ const ExperienceItem = React.forwardRef<HTMLDivElement, ExperienceItemProps>(
fontSize: `${globalSettings?.subheaderSize || 16}px`,
}}
>
{experience.date}
{formatDateString(experience.date, locale)}
</div>
</motion.div>
{experience.position && !centerSubtitle && (
+4 -1
View File
@@ -6,6 +6,8 @@ import SectionTitle from "./SectionTitle";
import { Project, GlobalSettings } from "@/types/resume";
import { useResumeStore } from "@/store/useResumeStore";
import { normalizeRichTextContent } from "@/lib/richText";
import { formatDateString } from "@/lib/utils";
import { useLocale } from "next-intl";
interface ProjectItemProps {
project: Project;
@@ -15,6 +17,7 @@ interface ProjectItemProps {
const ProjectItem = React.forwardRef<HTMLDivElement, ProjectItemProps>(
({ project, globalSettings }, ref) => {
const centerSubtitle = globalSettings?.centerSubtitle;
const locale = useLocale();
const flexLayout = globalSettings?.flexibleHeaderLayout;
@@ -84,7 +87,7 @@ const ProjectItem = React.forwardRef<HTMLDivElement, ProjectItemProps>(
fontSize: `${globalSettings?.subheaderSize || 16}px`,
}}
>
{project.date}
{formatDateString(project.date, locale)}
</div>
</motion.div>
{project.role && !centerSubtitle && (
+49 -12
View File
@@ -5,19 +5,56 @@ export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
export function formatDateString(dateStr: string | undefined): string {
if (!dateStr) return "";
// Check if it matches YYYY-MM
// If so, replace - with .
function parseToDate(dateStr: string): Date | null {
let year: number | null = null;
let month: number | null = null;
if (dateStr.match(/^\d{4}-\d{2}$/)) {
return dateStr.replace("-", ".");
const parts = dateStr.split("-");
year = parseInt(parts[0], 10);
month = parseInt(parts[1], 10);
} else if (dateStr.match(/^\d{4}-\d{2}-\d{2}$/)) {
const parts = dateStr.split("-");
year = parseInt(parts[0], 10);
month = parseInt(parts[1], 10);
} else if (dateStr.match(/^\d{4}\.\d{2}$/)) {
const parts = dateStr.split(".");
year = parseInt(parts[0], 10);
month = parseInt(parts[1], 10);
} else if (dateStr.match(/^\d{4}\/\d{2}$/)) {
const parts = dateStr.split("/");
year = parseInt(parts[0], 10);
month = parseInt(parts[1], 10);
}
if (dateStr.match(/^\d{4}-\d{2}-\d{2}$/)) {
// Cut off day if present? User wants "month" precision.
return dateStr.substring(0, 7).replace("-", ".");
if (year !== null && month !== null) {
return new Date(Date.UTC(year, month - 1, 1));
}
return null;
}
export function formatDateString(dateStr: string | undefined, locale: string = "zh"): string {
if (!dateStr) return "";
if (dateStr.includes(" - ")) {
const [start, end] = dateStr.split(" - ");
return `${formatDateString(start, locale)} - ${formatDateString(end, locale)}`;
}
const date = parseToDate(dateStr);
if (!date) return dateStr;
try {
if (locale === "zh" || locale === "zh-CN") {
return `${date.getUTCFullYear()}.${String(date.getUTCMonth() + 1).padStart(2, "0")}`;
}
const formatter = new Intl.DateTimeFormat(locale, {
year: 'numeric',
month: '2-digit',
timeZone: 'UTC'
});
return formatter.format(date);
} catch (e) {
return dateStr;
}
return dateStr;
}