diff --git a/.env.local.example b/.env.local.example index c201c55..59342a2 100644 --- a/.env.local.example +++ b/.env.local.example @@ -1,8 +1,25 @@ # Copy this file to `.env.local` and adjust values. # All vars prefixed with NEXT_PUBLIC_ are exposed to the browser. +# Core site info NEXT_PUBLIC_SITE_NAME="Your Name" NEXT_PUBLIC_SITE_TITLE="Your Personal Site" NEXT_PUBLIC_SITE_DESCRIPTION="Personal homepage and blog." NEXT_PUBLIC_SITE_URL="https://example.com" NEXT_PUBLIC_SITE_AUTHOR="Your Name" +NEXT_PUBLIC_SITE_TAGLINE="Short tagline for your blog." +NEXT_PUBLIC_POSTS_PER_PAGE="5" +NEXT_PUBLIC_DEFAULT_LOCALE="zh-TW" + +# Social and profile +NEXT_PUBLIC_TWITTER_HANDLE="@yourhandle" +NEXT_PUBLIC_GITHUB_URL="https://github.com/yourname" +NEXT_PUBLIC_LINKEDIN_URL="https://www.linkedin.com/in/yourname/" +NEXT_PUBLIC_EMAIL_CONTACT="you@example.com" + +# SEO / Open Graph +NEXT_PUBLIC_OG_DEFAULT_IMAGE="/assets/og-default.jpg" +NEXT_PUBLIC_TWITTER_CARD_TYPE="summary_large_image" + +# Analytics (public ID only) +NEXT_PUBLIC_ANALYTICS_ID="" diff --git a/app/blog/[slug]/page.tsx b/app/blog/[slug]/page.tsx index 5811bef..b1fb36d 100644 --- a/app/blog/[slug]/page.tsx +++ b/app/blog/[slug]/page.tsx @@ -2,6 +2,7 @@ import { notFound } from 'next/navigation'; import type { Metadata } from 'next'; import { allPosts } from 'contentlayer/generated'; import { getPostBySlug } from '@/lib/posts'; +import { siteConfig } from '@/lib/config'; export function generateStaticParams() { return allPosts.map((post) => ({ @@ -44,7 +45,9 @@ export default function BlogPostPage({ params }: Props) { )} {post.published_at && (

- {new Date(post.published_at).toLocaleDateString('zh-TW')} + {new Date(post.published_at).toLocaleDateString( + siteConfig.defaultLocale + )}

)} {post.tags && ( diff --git a/app/blog/page.tsx b/app/blog/page.tsx index 32be464..5557f6c 100644 --- a/app/blog/page.tsx +++ b/app/blog/page.tsx @@ -1,5 +1,6 @@ import Link from 'next/link'; import { getAllPostsSorted } from '@/lib/posts'; +import { siteConfig } from '@/lib/config'; export const metadata = { title: 'Blog' @@ -22,7 +23,9 @@ export default function BlogIndexPage() {
{post.published_at && - new Date(post.published_at).toLocaleDateString('zh-TW')} + new Date(post.published_at).toLocaleDateString( + siteConfig.defaultLocale + )} {post.tags && post.tags.length > 0 && ( {post.tags.map((t) => ( @@ -47,4 +50,3 @@ export default function BlogIndexPage() { ); } - diff --git a/app/layout.tsx b/app/layout.tsx index 2cb1bf8..fdcc644 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -9,7 +9,22 @@ export const metadata: Metadata = { default: siteConfig.title, template: `%s | ${siteConfig.title}` }, - description: siteConfig.description + description: siteConfig.description, + metadataBase: new URL(siteConfig.url), + openGraph: { + title: siteConfig.title, + description: siteConfig.description, + url: siteConfig.url, + siteName: siteConfig.title, + images: [siteConfig.ogImage] + }, + twitter: { + card: siteConfig.twitterCard, + site: siteConfig.social.twitter || undefined, + title: siteConfig.title, + description: siteConfig.description, + images: [siteConfig.ogImage] + } }; export default function RootLayout({ @@ -18,7 +33,7 @@ export default function RootLayout({ children: React.ReactNode; }) { return ( - + {children} @@ -27,4 +42,3 @@ export default function RootLayout({ ); } - diff --git a/app/page.tsx b/app/page.tsx index f25ce9b..d900bd5 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -3,7 +3,7 @@ import { getAllPostsSorted } from '@/lib/posts'; import { siteConfig } from '@/lib/config'; export default function HomePage() { - const posts = getAllPostsSorted().slice(0, 5); + const posts = getAllPostsSorted().slice(0, siteConfig.postsPerPage); return (
@@ -12,7 +12,7 @@ export default function HomePage() { 你好,我是 {siteConfig.name}

- 這裡是我的個人首頁與技術 Blog。 + {siteConfig.tagline}

@@ -26,7 +26,9 @@ export default function HomePage() { {post.published_at && ( - {new Date(post.published_at).toLocaleDateString('zh-TW')} + {new Date(post.published_at).toLocaleDateString( + siteConfig.defaultLocale + )} )} diff --git a/components/site-footer.tsx b/components/site-footer.tsx index 9a8d826..7595426 100644 --- a/components/site-footer.tsx +++ b/components/site-footer.tsx @@ -1,9 +1,55 @@ import { siteConfig } from '@/lib/config'; export function SiteFooter() { + const { social } = siteConfig; + return ( ); } diff --git a/lib/config.ts b/lib/config.ts index 9d80f7f..5fcd7e9 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -5,5 +5,26 @@ export const siteConfig = { process.env.NEXT_PUBLIC_SITE_DESCRIPTION || 'Personal homepage and blog.', url: process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000', - author: process.env.NEXT_PUBLIC_SITE_AUTHOR || 'Your Name' + author: process.env.NEXT_PUBLIC_SITE_AUTHOR || 'Your Name', + tagline: + process.env.NEXT_PUBLIC_SITE_TAGLINE || + '這裡是我的個人首頁與技術 Blog。', + postsPerPage: + Number(process.env.NEXT_PUBLIC_POSTS_PER_PAGE) > 0 + ? Number(process.env.NEXT_PUBLIC_POSTS_PER_PAGE) + : 5, + defaultLocale: process.env.NEXT_PUBLIC_DEFAULT_LOCALE || 'zh-TW', + social: { + twitter: process.env.NEXT_PUBLIC_TWITTER_HANDLE || '', + github: process.env.NEXT_PUBLIC_GITHUB_URL || '', + linkedin: process.env.NEXT_PUBLIC_LINKEDIN_URL || '', + email: process.env.NEXT_PUBLIC_EMAIL_CONTACT || '' + }, + ogImage: process.env.NEXT_PUBLIC_OG_DEFAULT_IMAGE || '/assets/og-default.jpg', + twitterCard: + (process.env.NEXT_PUBLIC_TWITTER_CARD_TYPE as + | 'summary' + | 'summary_large_image' + | undefined) || 'summary_large_image', + analyticsId: process.env.NEXT_PUBLIC_ANALYTICS_ID || '' };