fix: 语言切换全面修复 —— 品牌层/主题首页/详情区块/404 全量 i18n(zh 矩阵 13/13)
- 站点品牌双语:每站 brandZh(品牌名/标语/hero/导航/页脚/搜索占位)由生成器写入 站点 messages 的 zh 覆盖;SiteChrome 改为 async getTranslations 按语言渲染 - 五套主题首页接线 siteHome/hero 命名空间(新增 wire-theme-i18n.py 一次性接线脚本, 仅处理组件函数体、hook 自动插入、hero 用 th 命名空间) - 详情页 Use cases/Good for/FAQ/reviewCta、用例与合集索引与详情、提交页、 对比页、Alternatives 表头与 FAQ 全部走 next-intl - 404 双语言:locale 内走 next-intl,根级按 Accept-Language - 修复 zh.json 中 84 处 zhXxx 旁挂键归位 + en.json 残留清理(i18n 694 键一致) - 回归:/zh 下 13 页全中文无英文残留、无横向溢出;五站 /zh lang=zh 全部正确
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
// 自动生成:[locale]/not-found.tsx 的薄路由
|
||||
export { default } from "@aidir/core/pages/[locale]/not-found";
|
||||
@@ -6,13 +6,25 @@ import { locales, defaultLocale, type Locale } from "@aidir/core/i18n.config";
|
||||
const brandMessages = {
|
||||
en: {
|
||||
seo: { home: {"title":"LaunchAI — Discover and Upvote Today's AI Products","description":"A daily launch feed for AI products. Upvote the best AI tools, follow makers and climb the leaderboard.","ogImage":"/og-image.png"} },
|
||||
hero: { title: "LaunchAI — Discover and Upvote Today's AI Products", description: "A daily launch feed for AI products. Upvote the best AI tools, follow makers and climb the leaderboard." },
|
||||
hero: { title: "Today's AI product launches", description: "Fresh AI products, every day. Upvote the best ones to the top." },
|
||||
common: { brand: { name: "LaunchAI", tagline: "Discover and upvote today's AI products." } },
|
||||
navigation: {
|
||||
main: { tools: "Today", blog: "Blog" },
|
||||
site: { items: ["Today","Leaderboard","Categories","Collections","Blog","Submit"], hrefs: ["/","/rankings","/tools","/best","/blog","/submit"] },
|
||||
},
|
||||
siteFooter: { groups: [{"title":"Community","links":[{"label":"Submit a Product","href":"/submit"},{"label":"Leaderboard","href":"/rankings"},{"label":"Dashboard","href":"/dashboard"}]},{"title":"Explore","links":[{"label":"All Products","href":"/tools"},{"label":"Use Cases","href":"/ai-tools-for"},{"label":"Collections","href":"/best"}]},{"title":"About","links":[{"label":"Blog","href":"/blog"},{"label":"Terms","href":"/terms"},{"label":"Privacy","href":"/privacy"}]}] },
|
||||
siteHeader: { searchPlaceholder: "Search AI products..." },
|
||||
},
|
||||
zh: {
|
||||
seo: { home: {"title":"LaunchAI — 发现并投票今日 AI 新品","description":"AI 产品每日发布社区:为最好的 AI 工具投票,登上社区排行榜。","ogImage":"/og-image.png"} },
|
||||
hero: { title: "LaunchAI — 发现并投票今日 AI 新品", description: "AI 产品每日发布社区:为最好的 AI 工具投票,登上社区排行榜。" },
|
||||
common: { brand: { name: "LaunchAI", tagline: "Discover and upvote today's AI products." } },
|
||||
hero: { title: "今日 AI 产品发布", description: "每天上新。为最好的产品投票,冲上排行榜。" },
|
||||
common: { brand: { name: "LaunchAI", tagline: "发现并投票今日的 AI 新品。" } },
|
||||
navigation: {
|
||||
main: { tools: "今日", blog: "博客" },
|
||||
site: { items: ["今日","排行榜","分类","合集","博客","提交产品"], hrefs: ["/","/rankings","/tools","/best","/blog","/submit"] },
|
||||
},
|
||||
siteFooter: { groups: [{"title":"社区","links":[{"label":"Submit a Product","href":"/submit"},{"label":"Leaderboard","href":"/rankings"},{"label":"Dashboard","href":"/dashboard"}]},{"title":"探索","links":[{"label":"All Products","href":"/tools"},{"label":"Use Cases","href":"/ai-tools-for"},{"label":"Collections","href":"/best"}]},{"title":"关于","links":[{"label":"Blog","href":"/blog"},{"label":"Terms","href":"/terms"},{"label":"Privacy","href":"/privacy"}]}] },
|
||||
siteHeader: { searchPlaceholder: "搜索 AI 产品..." },
|
||||
},
|
||||
} as const;
|
||||
|
||||
|
||||
@@ -2,6 +2,26 @@ import type { SiteConfig } from "@/kit/types";
|
||||
|
||||
// LaunchAI —— 独立站点的全部品牌/导航/页脚配置
|
||||
export const siteConfig: SiteConfig = {
|
||||
"brandZh": {
|
||||
"name": "LaunchAI",
|
||||
"tagline": "发现并投票今日的 AI 新品。",
|
||||
"headline": "今日 AI 产品发布",
|
||||
"subheadline": "每天上新。为最好的产品投票,冲上排行榜。",
|
||||
"nav": [
|
||||
"今日",
|
||||
"排行榜",
|
||||
"分类",
|
||||
"合集",
|
||||
"博客",
|
||||
"提交产品"
|
||||
],
|
||||
"footer": [
|
||||
"社区",
|
||||
"探索",
|
||||
"关于"
|
||||
],
|
||||
"searchPlaceholder": "搜索 AI 产品..."
|
||||
},
|
||||
"accent": "16 85% 55%",
|
||||
"port": 3002,
|
||||
"brand": {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// 自动生成:站点首页组件(源自底座 product 形态,品牌来自 @/site/brand)
|
||||
import { siteConfig } from "@/site/brand";
|
||||
import { Flame, Trophy, Rocket, ChevronRight, MessageSquare } from "lucide-react";
|
||||
import { getTranslations } from "next-intl/server";
|
||||
import { VoteButton } from "@/features/voting/components/vote-button";
|
||||
import { LocaleLink } from "@/components/locale-link";
|
||||
import { ToolLogo } from "@/components/tool-logo";
|
||||
@@ -103,6 +104,8 @@ function LaunchRow({
|
||||
}
|
||||
|
||||
async function SiteHome({ locale }: { locale: string }) {
|
||||
const t = await getTranslations("siteHome");
|
||||
const th = await getTranslations("hero");
|
||||
const data = await getHomeDataSafe(10);
|
||||
|
||||
return (
|
||||
@@ -111,17 +114,17 @@ async function SiteHome({ locale }: { locale: string }) {
|
||||
<section className="border-b border-border bg-gradient-to-b from-primary/5 to-background">
|
||||
<div className="mx-auto max-w-5xl px-4 py-12 text-center sm:px-6 md:py-16">
|
||||
<h1 className="text-2xl font-extrabold tracking-tight text-foreground sm:text-4xl">
|
||||
{siteConfig.brand.headline}
|
||||
{th("title")}
|
||||
</h1>
|
||||
<p className="mt-3 text-sm text-muted-foreground sm:text-base">
|
||||
{siteConfig.brand.subheadline}
|
||||
{th("description")}
|
||||
</p>
|
||||
<LocaleLink
|
||||
href="/submit"
|
||||
className="mt-6 inline-flex items-center gap-1.5 rounded-full bg-primary px-6 py-2.5 text-sm font-semibold text-primary-foreground shadow transition-all hover:opacity-90"
|
||||
>
|
||||
<Rocket className="h-4 w-4" />
|
||||
Launch your product
|
||||
{t("launchButton")}
|
||||
</LocaleLink>
|
||||
</div>
|
||||
</section>
|
||||
@@ -140,14 +143,14 @@ async function SiteHome({ locale }: { locale: string }) {
|
||||
))
|
||||
) : (
|
||||
<p className="px-6 py-12 text-center text-sm text-muted-foreground">
|
||||
No launches yet — be the first to submit.
|
||||
{t("noLaunches")}
|
||||
</p>
|
||||
)}
|
||||
<LocaleLink
|
||||
href="/tools"
|
||||
className="flex items-center justify-center gap-1 border-t border-border py-3 text-xs font-medium text-muted-foreground hover:text-primary"
|
||||
>
|
||||
Browse all products
|
||||
{t("feedBrowse")}
|
||||
<ChevronRight className="h-3 w-3" />
|
||||
</LocaleLink>
|
||||
</div>
|
||||
@@ -155,7 +158,7 @@ async function SiteHome({ locale }: { locale: string }) {
|
||||
{/* New */}
|
||||
{data.newest.length > 0 && (
|
||||
<div className="mt-8">
|
||||
<SectionHeader title="Fresh launches" viewAllHref="/rankings?board=new" />
|
||||
<SectionHeader title={t("newTitle")} viewAllHref="/rankings?board=new" />
|
||||
<div className="overflow-hidden rounded-2xl border border-border bg-background">
|
||||
{data.newest.slice(0, 5).map((tool) => (
|
||||
<LaunchRow key={tool.id} tool={tool} locale={locale} />
|
||||
@@ -200,14 +203,14 @@ async function SiteHome({ locale }: { locale: string }) {
|
||||
href="/rankings"
|
||||
className="mt-4 block rounded-lg border border-border py-2 text-center text-xs font-medium text-muted-foreground hover:border-primary/40 hover:text-primary"
|
||||
>
|
||||
Full leaderboard
|
||||
{t("leaderboardFull")}
|
||||
</LocaleLink>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{data.editorsChoice.length > 0 && (
|
||||
<div className="rounded-2xl border border-border bg-background p-5">
|
||||
<h3 className="text-sm font-bold text-foreground">Editor's picks</h3>
|
||||
<h3 className="text-sm font-bold text-foreground">{t("picksEditorialLabel")}</h3>
|
||||
<ol className="mt-4 space-y-3">
|
||||
{data.editorsChoice.slice(0, 4).map((tool) => (
|
||||
<li key={tool.id}>
|
||||
|
||||
Reference in New Issue
Block a user