2
0

feat(i18n): HomePage accepts t/locale props, replaces hardcoded strings

This commit is contained in:
2026-05-18 22:21:01 +08:00
parent 0467b8179e
commit e8a6226151

View File

@@ -22,53 +22,74 @@ export type PostPreview = {
title: string;
description: string;
pubDate: string;
category: 'technical' | 'company';
category: "technical" | "company";
emoji?: string;
readTime: number;
};
export type HomeTranslations = {
heroDesc1: string;
heroDesc2: string;
latestPostsTitle: string;
viewAll: string;
minRead: string;
dateLocale: string;
};
const CATEGORY_STYLE = {
company: {
thumb: 'from-primary/20 to-primary/40',
tag: 'text-primary bg-primary/10',
defaultEmoji: '🔥',
thumb: "from-primary/20 to-primary/40",
tag: "text-primary bg-primary/10",
defaultEmoji: "🔥",
},
technical: {
thumb: 'from-secondary/50 to-secondary/70',
tag: 'text-secondary-foreground bg-secondary/40',
defaultEmoji: '⚙️',
thumb: "from-secondary/50 to-secondary/70",
tag: "text-secondary-foreground bg-secondary/40",
defaultEmoji: "⚙️",
},
} as const;
const LatestPosts = ({ posts }: { posts: PostPreview[] }) => {
const LatestPosts = ({
posts,
t,
locale,
}: {
posts: PostPreview[];
t: HomeTranslations;
locale: string;
}) => {
const blogBase = locale === "zh-cn" ? "/zh-cn/blog" : "/blog";
if (posts.length === 0) return null;
return (
<section className="px-6 py-16 md:px-12 lg:px-24">
<div className="mx-auto max-w-7xl">
<div className="mb-10 flex items-baseline justify-between">
<h2 className="font-heading text-foreground text-3xl font-bold md:text-4xl">
Latest from the Blog
{t.latestPostsTitle}
</h2>
<a
href="/blog"
href={blogBase}
className="text-primary text-sm font-bold hover:underline"
>
View all
{t.viewAll}
</a>
</div>
<div className="grid grid-cols-1 gap-6 md:grid-cols-3">
{posts.map((post, idx) => {
const style = CATEGORY_STYLE[post.category];
const emoji = post.emoji ?? style.defaultEmoji;
const date = new Date(post.pubDate).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric',
});
const date = new Date(post.pubDate).toLocaleDateString(
t.dateLocale,
{
year: "numeric",
month: "long",
day: "numeric",
},
);
return (
<motion.a
key={post.slug}
href={`/blog/${post.slug}`}
href={`${blogBase}/${post.slug}`}
initial={{ opacity: 0, y: 24 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
@@ -94,7 +115,7 @@ const LatestPosts = ({ posts }: { posts: PostPreview[] }) => {
{post.description}
</p>
<p className="text-muted-foreground/70 text-xs">
{date} · {post.readTime} min read
{date} · {post.readTime} {t.minRead}
</p>
</div>
</motion.a>
@@ -106,7 +127,7 @@ const LatestPosts = ({ posts }: { posts: PostPreview[] }) => {
);
};
const Hero = () => {
const Hero = ({ t }: { t: HomeTranslations }) => {
const bodyRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!bodyRef.current) return;
@@ -144,14 +165,10 @@ const Hero = () => {
Ignis Network
</div>
<p className="text-muted-foreground max-w-3xl text-lg leading-relaxed md:text-xl">
We're a startup company that builds resilient digital infrastructure
designed to endure, with AI applied where it meaningfully adds
strength and insight.
{t.heroDesc1}
</p>
<p className="text-muted-foreground mt-2 max-w-2xl text-lg leading-relaxed md:text-xl">
We focus on stability, clarity, and long-term reliability — creating
systems that smartly carry what matters, support growth over time,
and let everything else move forward with confidence.
{t.heroDesc2}
</p>
<motion.div
@@ -517,13 +534,17 @@ function _CTA() {
export default function Home({
latestPosts = [],
t,
locale = "en",
}: {
latestPosts?: PostPreview[];
t: HomeTranslations;
locale?: string;
}) {
return (
<div className="bg-background selection:bg-primary/20 selection:text-primary-foreground min-h-screen">
<Hero />
<LatestPosts posts={latestPosts} />
<Hero t={t} />
<LatestPosts posts={latestPosts} t={t} locale={locale} />
</div>
);
}