Fix runttime
This commit is contained in:
parent
d1ed5c641b
commit
c9e9ae6b90
@ -1,7 +1,7 @@
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import type { CommonContent, HomeContent, HomeVariantContent } from "@/content/types";
|
import type { CommonContent, HomeContent, HomeVariantContent } from "@/content/types";
|
||||||
import type { Locale } from "@/lib/i18n";
|
import type { Locale } from "@/lib/i18n";
|
||||||
import { getEmailHref, isComingSoonMode } from "@/lib/site";
|
import { getEmailHref, getModeValue, isComingSoonMode } from "@/lib/site";
|
||||||
|
|
||||||
type HeroSectionProps = {
|
type HeroSectionProps = {
|
||||||
locale: Locale;
|
locale: Locale;
|
||||||
@ -10,7 +10,7 @@ type HeroSectionProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default function HeroSection({ locale, home, common }: HeroSectionProps) {
|
export default function HeroSection({ locale, home, common }: HeroSectionProps) {
|
||||||
const activeHome: HomeVariantContent = isComingSoonMode() ? home.comingSoon : home.full;
|
const activeHome: HomeVariantContent = getModeValue(home);
|
||||||
const emailHref = getEmailHref();
|
const emailHref = getEmailHref();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import type { Locale } from "@/lib/i18n";
|
import type { Locale } from "@/lib/i18n";
|
||||||
import type { CommonContent } from "@/content/types";
|
import type { CommonContent } from "@/content/types";
|
||||||
|
import { getModeValue } from "@/lib/site";
|
||||||
|
|
||||||
type SiteFooterProps = {
|
type SiteFooterProps = {
|
||||||
locale: Locale;
|
locale: Locale;
|
||||||
@ -8,13 +9,14 @@ type SiteFooterProps = {
|
|||||||
|
|
||||||
export default function SiteFooter({ locale, common }: SiteFooterProps) {
|
export default function SiteFooter({ locale, common }: SiteFooterProps) {
|
||||||
const year = new Date().getFullYear();
|
const year = new Date().getFullYear();
|
||||||
|
const commonVariant = getModeValue(common.variants);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<footer className="site-footer">
|
<footer className="site-footer">
|
||||||
<div className="container footer-content">
|
<div className="container footer-content">
|
||||||
<div className="footer-copy">
|
<div className="footer-copy">
|
||||||
<p>{common.footerRights.replace("{year}", String(year))}</p>
|
<p>{common.footerRights.replace("{year}", String(year))}</p>
|
||||||
<p>{common.footerBuiltWith}</p>
|
<p>{commonVariant.footerBuiltWith}</p>
|
||||||
</div>
|
</div>
|
||||||
<p className="locale-badge">{locale.toUpperCase()}</p>
|
<p className="locale-badge">{locale.toUpperCase()}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import LanguageSwitcher from "@/components/LanguageSwitcher";
|
|||||||
import ThemeToggle from "@/components/ThemeToggle";
|
import ThemeToggle from "@/components/ThemeToggle";
|
||||||
import type { Locale } from "@/lib/i18n";
|
import type { Locale } from "@/lib/i18n";
|
||||||
import type { CommonContent } from "@/content/types";
|
import type { CommonContent } from "@/content/types";
|
||||||
import { isComingSoonMode } from "@/lib/site";
|
import { getModeValue, isComingSoonMode } from "@/lib/site";
|
||||||
|
|
||||||
type SiteHeaderProps = {
|
type SiteHeaderProps = {
|
||||||
locale: Locale;
|
locale: Locale;
|
||||||
@ -12,6 +12,7 @@ type SiteHeaderProps = {
|
|||||||
|
|
||||||
export default function SiteHeader({ locale, common }: SiteHeaderProps) {
|
export default function SiteHeader({ locale, common }: SiteHeaderProps) {
|
||||||
const isComingSoon = isComingSoonMode();
|
const isComingSoon = isComingSoonMode();
|
||||||
|
const commonVariant = getModeValue(common.variants);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className="site-header">
|
<header className="site-header">
|
||||||
@ -20,7 +21,7 @@ export default function SiteHeader({ locale, common }: SiteHeaderProps) {
|
|||||||
<Link href={`/${locale}`} className="brand">
|
<Link href={`/${locale}`} className="brand">
|
||||||
{common.siteTitle}
|
{common.siteTitle}
|
||||||
</Link>
|
</Link>
|
||||||
<span className="brand-tagline">{common.siteTagline}</span>
|
<span className="brand-tagline">{commonVariant.siteTagline}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{!isComingSoon ? (
|
{!isComingSoon ? (
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import type { Dictionary } from "@/content/types";
|
|||||||
const ar: Dictionary = {
|
const ar: Dictionary = {
|
||||||
common: {
|
common: {
|
||||||
siteTitle: "ضياء",
|
siteTitle: "ضياء",
|
||||||
siteTagline: "موقع شخصي جديد قيد الإطلاق",
|
|
||||||
navLabel: "التنقل الرئيسي",
|
navLabel: "التنقل الرئيسي",
|
||||||
languageSwitcherLabel: "تبديل اللغة",
|
languageSwitcherLabel: "تبديل اللغة",
|
||||||
themeToggleLabel: "نمط الواجهة",
|
themeToggleLabel: "نمط الواجهة",
|
||||||
@ -15,8 +14,16 @@ const ar: Dictionary = {
|
|||||||
contact: "تواصل",
|
contact: "تواصل",
|
||||||
},
|
},
|
||||||
footerRights: "{year} جميع الحقوق محفوظة",
|
footerRights: "{year} جميع الحقوق محفوظة",
|
||||||
footerBuiltWith: "موقع ثنائي اللغة مهيأ للعرض المهني والنشر على خادم خاص.",
|
variants: {
|
||||||
availabilityBadge: "متاح لمشاريع الويب والتطوير المخصص",
|
comingSoon: {
|
||||||
|
siteTagline: "موقع شخصي جديد قيد الإطلاق",
|
||||||
|
footerBuiltWith: "صفحة مؤقتة للإعلان عن قرب إطلاق الموقع الكامل على الخادم الخاص.",
|
||||||
|
},
|
||||||
|
full: {
|
||||||
|
siteTagline: "مطور ويب يبني تجارب سريعة وقابلة للتوسع",
|
||||||
|
footerBuiltWith: "موقع ثنائي اللغة مهيأ للعرض المهني والنشر على خادم خاص.",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
home: {
|
home: {
|
||||||
comingSoon: {
|
comingSoon: {
|
||||||
@ -92,10 +99,16 @@ const ar: Dictionary = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
metadata: {
|
metadata: {
|
||||||
homeTitle: "قريبًا",
|
home: {
|
||||||
homeDescription: "صفحة ترحيبية مؤقتة للإعلان عن قرب إطلاق الموقع الشخصي الجديد.",
|
comingSoon: {
|
||||||
fullHomeTitle: "الصفحة الرئيسية",
|
title: "قريبًا",
|
||||||
fullHomeDescription: "موقع شخصي احترافي ثنائي اللغة يعرض الهوية المهنية والخبرة التقنية والاستعداد للنشر.",
|
description: "صفحة ترحيبية مؤقتة للإعلان عن قرب إطلاق الموقع الشخصي الجديد.",
|
||||||
|
},
|
||||||
|
full: {
|
||||||
|
title: "الصفحة الرئيسية",
|
||||||
|
description: "موقع شخصي احترافي ثنائي اللغة يعرض الهوية المهنية والخبرة التقنية والاستعداد للنشر.",
|
||||||
|
},
|
||||||
|
},
|
||||||
aboutTitle: "من أنا",
|
aboutTitle: "من أنا",
|
||||||
aboutDescription: "تعرف على الخبرات، المهارات، ومنهج العمل في بناء مواقع وأنظمة ويب حديثة.",
|
aboutDescription: "تعرف على الخبرات، المهارات، ومنهج العمل في بناء مواقع وأنظمة ويب حديثة.",
|
||||||
contactTitle: "تواصل",
|
contactTitle: "تواصل",
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import type { Dictionary } from "@/content/types";
|
|||||||
const en: Dictionary = {
|
const en: Dictionary = {
|
||||||
common: {
|
common: {
|
||||||
siteTitle: "Diyaa",
|
siteTitle: "Diyaa",
|
||||||
siteTagline: "A new personal site is on the way",
|
|
||||||
navLabel: "Main navigation",
|
navLabel: "Main navigation",
|
||||||
languageSwitcherLabel: "Switch language",
|
languageSwitcherLabel: "Switch language",
|
||||||
themeToggleLabel: "Interface theme",
|
themeToggleLabel: "Interface theme",
|
||||||
@ -15,8 +14,16 @@ const en: Dictionary = {
|
|||||||
contact: "Contact",
|
contact: "Contact",
|
||||||
},
|
},
|
||||||
footerRights: "{year} All rights reserved",
|
footerRights: "{year} All rights reserved",
|
||||||
footerBuiltWith: "A bilingual professional site prepared for deployment on a private server.",
|
variants: {
|
||||||
availabilityBadge: "Available for web builds and custom development",
|
comingSoon: {
|
||||||
|
siteTagline: "A new personal site is on the way",
|
||||||
|
footerBuiltWith: "A temporary landing page announcing the upcoming launch on the private server.",
|
||||||
|
},
|
||||||
|
full: {
|
||||||
|
siteTagline: "Web developer building fast, scalable digital experiences",
|
||||||
|
footerBuiltWith: "A bilingual professional site prepared for deployment on a private server.",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
home: {
|
home: {
|
||||||
comingSoon: {
|
comingSoon: {
|
||||||
@ -92,10 +99,16 @@ const en: Dictionary = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
metadata: {
|
metadata: {
|
||||||
homeTitle: "Coming Soon",
|
home: {
|
||||||
homeDescription: "A temporary landing page announcing the upcoming launch of the new personal website.",
|
comingSoon: {
|
||||||
fullHomeTitle: "Home",
|
title: "Coming Soon",
|
||||||
fullHomeDescription: "A bilingual professional site presenting technical expertise, project readiness, and server-friendly deployment.",
|
description: "A temporary landing page announcing the upcoming launch of the new personal website.",
|
||||||
|
},
|
||||||
|
full: {
|
||||||
|
title: "Home",
|
||||||
|
description: "A bilingual professional site presenting technical expertise, project readiness, and server-friendly deployment.",
|
||||||
|
},
|
||||||
|
},
|
||||||
aboutTitle: "About",
|
aboutTitle: "About",
|
||||||
aboutDescription: "Learn about experience, skills, and the working approach behind modern web builds.",
|
aboutDescription: "Learn about experience, skills, and the working approach behind modern web builds.",
|
||||||
contactTitle: "Contact",
|
contactTitle: "Contact",
|
||||||
|
|||||||
@ -1,6 +1,15 @@
|
|||||||
|
export type ModeVariants<T> = {
|
||||||
|
comingSoon: T;
|
||||||
|
full: T;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CommonVariantContent = {
|
||||||
|
siteTagline: string;
|
||||||
|
footerBuiltWith: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type CommonContent = {
|
export type CommonContent = {
|
||||||
siteTitle: string;
|
siteTitle: string;
|
||||||
siteTagline: string;
|
|
||||||
navLabel: string;
|
navLabel: string;
|
||||||
languageSwitcherLabel: string;
|
languageSwitcherLabel: string;
|
||||||
themeToggleLabel: string;
|
themeToggleLabel: string;
|
||||||
@ -12,8 +21,7 @@ export type CommonContent = {
|
|||||||
contact: string;
|
contact: string;
|
||||||
};
|
};
|
||||||
footerRights: string;
|
footerRights: string;
|
||||||
footerBuiltWith: string;
|
variants: ModeVariants<CommonVariantContent>;
|
||||||
availabilityBadge: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type HomeVariantContent = {
|
export type HomeVariantContent = {
|
||||||
@ -29,10 +37,7 @@ export type HomeVariantContent = {
|
|||||||
}>;
|
}>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type HomeContent = {
|
export type HomeContent = ModeVariants<HomeVariantContent>;
|
||||||
comingSoon: HomeVariantContent;
|
|
||||||
full: HomeVariantContent;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type AboutContent = {
|
export type AboutContent = {
|
||||||
kicker: string;
|
kicker: string;
|
||||||
@ -64,11 +69,13 @@ export type ContactContent = {
|
|||||||
channels: ContactChannelContent[];
|
channels: ContactChannelContent[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type MetadataVariantContent = {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type MetadataContent = {
|
export type MetadataContent = {
|
||||||
homeTitle: string;
|
home: ModeVariants<MetadataVariantContent>;
|
||||||
homeDescription: string;
|
|
||||||
fullHomeTitle: string;
|
|
||||||
fullHomeDescription: string;
|
|
||||||
aboutTitle: string;
|
aboutTitle: string;
|
||||||
aboutDescription: string;
|
aboutDescription: string;
|
||||||
contactTitle: string;
|
contactTitle: string;
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { getDictionary, type Locale } from "@/lib/i18n";
|
import { getDictionary, type Locale } from "@/lib/i18n";
|
||||||
import { getLocalizedPath, getLocalizedUrl, isComingSoonMode } from "@/lib/site";
|
import { getLocalizedPath, getLocalizedUrl, getModeValue } from "@/lib/site";
|
||||||
|
|
||||||
type PageKey = "home" | "about" | "contact";
|
type PageKey = "home" | "about" | "contact";
|
||||||
|
|
||||||
@ -13,11 +13,11 @@ const pagePathMap: Record<PageKey, string> = {
|
|||||||
export function buildPageMetadata(locale: Locale, page: PageKey): Metadata {
|
export function buildPageMetadata(locale: Locale, page: PageKey): Metadata {
|
||||||
const dictionary = getDictionary(locale);
|
const dictionary = getDictionary(locale);
|
||||||
const pathname = pagePathMap[page];
|
const pathname = pagePathMap[page];
|
||||||
const isComingSoon = isComingSoonMode();
|
const homeMetadata = getModeValue(dictionary.metadata.home);
|
||||||
const metadataByPage = {
|
const metadataByPage = {
|
||||||
home: {
|
home: {
|
||||||
title: isComingSoon ? dictionary.metadata.homeTitle : dictionary.metadata.fullHomeTitle,
|
title: homeMetadata.title,
|
||||||
description: isComingSoon ? dictionary.metadata.homeDescription : dictionary.metadata.fullHomeDescription,
|
description: homeMetadata.description,
|
||||||
},
|
},
|
||||||
about: {
|
about: {
|
||||||
title: dictionary.metadata.aboutTitle,
|
title: dictionary.metadata.aboutTitle,
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import type { ContactContent } from "@/content/types";
|
import type { ContactContent, ModeVariants } from "@/content/types";
|
||||||
|
|
||||||
const FALLBACK_SITE_URL = "https://example.com";
|
const FALLBACK_SITE_URL = "https://example.com";
|
||||||
const SITE_MODES = ["coming-soon", "full"] as const;
|
const SITE_MODES = ["coming-soon", "full"] as const;
|
||||||
@ -60,6 +60,10 @@ export function isComingSoonMode(): boolean {
|
|||||||
return getSiteMode() === "coming-soon";
|
return getSiteMode() === "coming-soon";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getModeValue<T>(variants: ModeVariants<T>): T {
|
||||||
|
return variants[getSiteMode()];
|
||||||
|
}
|
||||||
|
|
||||||
export function getLocalizedPath(pathname: string, locale: "ar" | "en"): string {
|
export function getLocalizedPath(pathname: string, locale: "ar" | "en"): string {
|
||||||
const normalizedPath = pathname === "/" ? "" : pathname;
|
const normalizedPath = pathname === "/" ? "" : pathname;
|
||||||
return `/${locale}${normalizedPath}`;
|
return `/${locale}${normalizedPath}`;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user