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 || ''
};