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:
AI Directory Platform
2026-09-09 16:59:52 +00:00
parent f4f48f7293
commit 5cc4195d68
50 changed files with 4141 additions and 235 deletions
+220 -1
View File
@@ -649,7 +649,9 @@
"latest": "Latest",
"trending": "Trending"
},
"clearFilters": "Clear search & filters"
"clearFilters": "Clear search & filters",
"useCasesTitle": "Use cases",
"goodForTitle": "Good for"
},
"seo": {
"home": {
@@ -796,5 +798,222 @@
"new": "New",
"editorsChoice": "Editor's Choice",
"empty": "No tools on this board yet."
},
"siteHome": {
"trending": {
"title": "Trending this week",
"subtitle": "Ranked by votes, reviews and engagement over the last 7 days"
},
"categories": {
"title": "Browse by category",
"viewAll": "All tools"
},
"new": {
"title": "New AI tools"
},
"editors": {
"title": "Editor's Choice"
},
"useCases": {
"title": "Tools by use case",
"subtitle": "What do you want to get done?",
"viewAll": "All use cases"
},
"bestLists": {
"title": "Best AI tool lists",
"subtitle": "Curated rankings by our editors",
"viewAll": "All lists"
},
"reviews": {
"title": "Latest community reviews"
},
"cta": {
"title": "Built an AI tool?",
"body": "Submit it to {brand} and get discovered by thousands of users looking for tools like yours.",
"button": "Submit your tool"
},
"popular": {
"label": "Popular:"
},
"toolsCount": "{count} tools",
"feed": {
"title": "Today's feed",
"browse": "Browse all products"
},
"leaderboard": {
"title": "Leaderboard",
"full": "Full leaderboard"
},
"picks": {
"title": "Editor's picks"
},
"collections": {
"title": "Collections"
},
"launch": {
"button": "Launch your product"
},
"featured": {
"label": "Featured story",
"read": "Read the story"
},
"picksEditorial": {
"label": "Editor's picks"
},
"guides": {
"title": "Buying guides",
"subtitle": "Deep dives that help you choose",
"viewAll": "All guides"
},
"stories": {
"title": "Latest stories",
"viewAll": "All stories"
},
"community": {
"title": "From the community",
"viewAll": "Browse directory"
},
"ship": {
"title": "Ship faster with AI",
"subtitle": "Pick your workflow — we ranked the tools for it",
"viewAll": "All use cases"
},
"upvoted": {
"title": "Most upvoted this week",
"viewAll": "View all"
},
"workflows": {
"title": "Workflows & stacks",
"subtitle": "Batteries-included tool stacks for common dev tasks",
"viewAll": "All workflows"
},
"browseCategory": {
"title": "Browse by category"
},
"trendingNow": {
"title": "Trending now"
},
"seeRankings": "See full rankings",
"browse": "Browse tools",
"noLaunches": "No launches yet — be the first to submit.",
"searchPlaceholder": "Search AI tools...",
"reviewsCount": "{count} reviews",
"mastheadLabel": "INDEPENDENT AI COVERAGE",
"minimalTagline": "AI TOOL SEARCH",
"minimalPlaceholder": "I need to create marketing videos...",
"useCasesBlockTitle": "Use cases",
"goodForBlockTitle": "Good for",
"searchButton": "Search",
"trendingTitle": "Trending this week",
"trendingSubtitle": "Ranked by votes, reviews and engagement over the last 7 days",
"allRankings": "All rankings",
"categoriesTitle": "Browse by category",
"categoriesViewAll": "All tools",
"newTitle": "New AI tools",
"editorsTitle": "Editor's Choice",
"useCasesTitle": "Tools by use case",
"useCasesSubtitle": "What do you want to get done?",
"useCasesViewAll": "All use cases",
"bestListsTitle": "Best AI tool lists",
"bestListsSubtitle": "Curated rankings by our editors",
"bestListsViewAll": "All lists",
"reviewsTitle": "Latest community reviews",
"ctaTitle": "Built an AI tool?",
"ctaBody": "Submit it to {brand} and get discovered by thousands of users looking for tools like yours.",
"ctaButton": "Submit your tool",
"popularLabel": "Popular:",
"feedTitle": "Today's feed",
"feedBrowse": "Browse all products",
"leaderboardTitle": "Leaderboard",
"leaderboardFull": "Full leaderboard",
"picksTitle": "Editor's picks",
"collectionsTitle": "Collections",
"launchButton": "Launch your product",
"featuredLabel": "Featured story",
"featuredRead": "Read the story",
"picksEditorialLabel": "EDITOR'S PICKS",
"guidesTitle": "Buying guides",
"guidesSubtitle": "Deep dives that help you choose",
"guidesViewAll": "All guides",
"storiesTitle": "Latest stories",
"storiesViewAll": "All stories",
"communityTitle": "From the community",
"communityViewAll": "Browse directory",
"shipTitle": "Ship faster with AI",
"shipSubtitle": "Pick your workflow — we ranked the tools for it",
"upvotedTitle": "Most upvoted this week",
"upvotedViewAll": "View all",
"workflowsTitle": "Workflows & stacks",
"workflowsSubtitle": "Batteries-included tool stacks for common dev tasks",
"workflowsViewAll": "All workflows",
"browseCategoryTitle": "Browse by category",
"trendingNowTitle": "Trending now",
"browseButton": "Browse tools",
"toolsCompared": "{count} tools compared",
"popularSearches": [
"writing",
"video",
"coding",
"image"
]
},
"best": {
"indexTitle": "Best AI Tools — Curated Lists",
"indexSubtitle": "Hand-picked rankings, updated with community signal. Every list is labeled and never sold.",
"empty": "No collections published yet.",
"curated": "Curated list",
"faq": "Frequently asked questions",
"more": "More best lists",
"toolsCompared": "{count} tools"
},
"aiToolsFor": {
"indexTitle": "AI tools for what you do",
"indexSubtitle": "Skip the categories. Start from your task or your role — every page ranks tools by real community votes and reviews.",
"byTask": "By task",
"byAudience": "By audience",
"noData": "No use cases yet.",
"noAud": "No audiences yet.",
"noTools": "No tools tagged yet."
},
"submitPage": {
"title": "Submit your AI tool",
"subtitle": "Get a free listing with community votes, user reviews and analytics. Submissions are reviewed by our editors, usually within 48 hours."
},
"faq": {
"title": "Frequently asked questions"
},
"compare": {
"intro": "A side-by-side comparison based on community votes, ratings and reviews — not sponsored placement.",
"leaning": "Right now the community leans toward {name}.",
"label": "Compare",
"noReviews": "—",
"latest": "Latest review"
},
"alternatives": {
"noAlt": "No alternatives found yet.",
"thisTool": "{name} (this tool)",
"table": {
"tool": "Tool",
"rating": "Rating",
"votes": "Votes",
"pricing": "Pricing"
},
"zhTable": {
"tool": "Tool",
"rating": "Rating",
"votes": "Votes",
"pricing": "Pricing"
}
},
"notFound": {
"code": "404",
"title": "Page not found",
"body": "The page you are looking for doesn't exist or may have moved.",
"home": "Back to home",
"tools": "Browse tools"
},
"search": {
"placeholder": "Search 1,000+ AI tools...",
"button": "Search"
}
}
+220 -1
View File
@@ -657,7 +657,9 @@
"latest": "最新",
"trending": "最热"
},
"clearFilters": "清除搜索与筛选"
"clearFilters": "清除搜索与筛选",
"useCasesTitle": "使用场景",
"goodForTitle": "适合人群"
},
"seo": {
"home": {
@@ -804,5 +806,222 @@
"new": "最新收录",
"editorsChoice": "编辑精选",
"empty": "该榜单暂无工具。"
},
"siteHome": {
"trending": {
"title": "本周趋势",
"subtitle": "按最近 7 天投票、评论与互动排名"
},
"categories": {
"title": "按分类浏览",
"viewAll": "全部工具"
},
"new": {
"title": "最新收录"
},
"editors": {
"title": "编辑精选"
},
"useCases": {
"title": "按用例找工具",
"subtitle": "你想完成什么?",
"viewAll": "全部用例"
},
"bestLists": {
"title": "最佳 AI 工具清单",
"subtitle": "编辑精选排名",
"viewAll": "全部清单"
},
"reviews": {
"title": "最新社区评论"
},
"cta": {
"title": "做了 AI 工具?",
"body": "提交到 {brand},让成千上万寻找工具的用户发现你。",
"button": "提交你的工具"
},
"popular": {
"label": "热门:"
},
"toolsCount": "{count} 个工具",
"feed": {
"title": "今日发布流",
"browse": "浏览全部产品"
},
"leaderboard": {
"title": "排行榜",
"full": "完整榜单"
},
"picks": {
"title": "编辑推荐"
},
"collections": {
"title": "合集"
},
"launch": {
"button": "发布你的产品"
},
"featured": {
"label": "头条文章",
"read": "阅读全文"
},
"picksEditorial": {
"label": "编辑推荐"
},
"guides": {
"title": "选购指南",
"subtitle": "帮你做决定的深度对比",
"viewAll": "全部指南"
},
"stories": {
"title": "最新文章",
"viewAll": "全部文章"
},
"community": {
"title": "来自社区",
"viewAll": "浏览目录"
},
"ship": {
"title": "用 AI 加速交付",
"subtitle": "选好工作流,我们为它排好了工具",
"viewAll": "全部用例"
},
"upvoted": {
"title": "本周最多投票",
"viewAll": "查看全部"
},
"workflows": {
"title": "工作流与技术栈",
"subtitle": "常见开发任务的完整工具组合",
"viewAll": "全部工作流"
},
"browseCategory": {
"title": "按分类浏览"
},
"trendingNow": {
"title": "当前趋势"
},
"seeRankings": "查看完整排行",
"browse": "浏览工具",
"noLaunches": "还没有发布——来做第一个。",
"searchPlaceholder": "搜索 AI 工具...",
"reviewsCount": "{count} 条评论",
"mastheadLabel": "独立 AI 报道",
"minimalTagline": "AI 工具搜索",
"minimalPlaceholder": "我需要制作营销视频……",
"useCasesBlockTitle": "使用场景",
"goodForBlockTitle": "适合人群",
"searchButton": "搜索",
"trendingTitle": "本周趋势",
"trendingSubtitle": "按最近 7 天投票、评论与互动排名",
"allRankings": "全部榜单",
"categoriesTitle": "按分类浏览",
"categoriesViewAll": "全部工具",
"newTitle": "最新收录",
"editorsTitle": "编辑精选",
"useCasesTitle": "按用例找工具",
"useCasesSubtitle": "你想完成什么?",
"useCasesViewAll": "全部用例",
"bestListsTitle": "最佳 AI 工具清单",
"bestListsSubtitle": "编辑精选排名",
"bestListsViewAll": "全部清单",
"reviewsTitle": "最新社区评论",
"ctaTitle": "做了 AI 工具?",
"ctaBody": "提交到 {brand},让成千上万寻找工具的用户发现你。",
"ctaButton": "提交你的工具",
"popularLabel": "热门:",
"feedTitle": "今日发布流",
"feedBrowse": "浏览全部产品",
"leaderboardTitle": "排行榜",
"leaderboardFull": "完整榜单",
"picksTitle": "编辑推荐",
"collectionsTitle": "合集",
"launchButton": "发布你的产品",
"featuredLabel": "头条文章",
"featuredRead": "阅读全文",
"picksEditorialLabel": "编辑推荐",
"guidesTitle": "选购指南",
"guidesSubtitle": "帮你做决定的深度对比",
"guidesViewAll": "全部指南",
"storiesTitle": "最新文章",
"storiesViewAll": "全部文章",
"communityTitle": "来自社区",
"communityViewAll": "浏览目录",
"shipTitle": "用 AI 加速交付",
"shipSubtitle": "选好工作流,我们为它排好了工具",
"upvotedTitle": "本周最多投票",
"upvotedViewAll": "查看全部",
"workflowsTitle": "工作流与技术栈",
"workflowsSubtitle": "常见开发任务的完整工具组合",
"workflowsViewAll": "全部工作流",
"browseCategoryTitle": "按分类浏览",
"trendingNowTitle": "当前趋势",
"browseButton": "浏览工具",
"toolsCompared": "{count} 个工具",
"popularSearches": [
"写作",
"视频",
"编程",
"图像"
]
},
"best": {
"indexTitle": "最佳 AI 工具——精选清单",
"indexSubtitle": "人工精选排名,持续吸收社区信号。每个清单都明确标注,永不售卖。",
"empty": "暂无合集。",
"curated": "编辑精选",
"faq": "常见问题",
"more": "更多最佳清单",
"toolsCompared": "{count} 个工具"
},
"aiToolsFor": {
"indexTitle": "按任务找 AI 工具",
"indexSubtitle": "别再翻分类。从你的任务或角色出发——每页都按真实社区投票与评论排名。",
"byTask": "按任务",
"byAudience": "按人群",
"noData": "暂无用例。",
"noAud": "暂无人群。",
"noTools": "暂无关联工具。"
},
"submitPage": {
"title": "提交你的 AI 工具",
"subtitle": "免费收录,获得社区投票、用户评论与数据分析。编辑通常在 48 小时内完成审核。"
},
"faq": {
"title": "常见问题"
},
"compare": {
"intro": "基于社区投票、评分与评论的并排对比——非赞助排名。",
"leaning": "当前社区更倾向 {name}。",
"label": "对比",
"noReviews": "—",
"latest": "最新评论"
},
"alternatives": {
"noAlt": "暂无替代品。",
"thisTool": "{name}(本工具)",
"table": {
"tool": "工具",
"rating": "评分",
"votes": "票数",
"pricing": "定价"
},
"zhTable": {
"tool": "工具",
"rating": "评分",
"votes": "票数",
"pricing": "定价"
}
},
"notFound": {
"code": "404",
"title": "页面不存在",
"body": "你访问的页面不存在或已被移动。",
"home": "返回首页",
"tools": "浏览工具"
},
"search": {
"placeholder": "搜索 1000+ AI 工具...",
"button": "搜索"
}
}
+84 -4
View File
@@ -23,6 +23,15 @@ const SITES_ROOT = path.join(REPO_ROOT, "sites");
// ====================================================================
const SITES = {
"ai-stack": {
brandZh: {
name: "AI Stack",
tagline: "由真实用户排名的专业 AI 工具目录。",
headline: "找到最好的 AI 工具,由真实用户排名",
subheadline: "14 个分类、1000+ AI 工具。对比投票、评论与定价,选出最适合你的。",
nav: ["发现", "用例", "最佳清单", "排行榜", "博客", "提交工具"],
footer: ["发现", "社区", "法律"],
searchPlaceholder: "搜索 1000+ AI 工具...",
},
accent: "222 47% 11%", // 近黑(专业)
port: 3001,
theme: "classic",
@@ -82,6 +91,15 @@ const SITES = {
},
},
launchai: {
brandZh: {
name: "LaunchAI",
tagline: "发现并投票今日的 AI 新品。",
headline: "今日 AI 产品发布",
subheadline: "每天上新。为最好的产品投票,冲上排行榜。",
nav: ["今日", "排行榜", "分类", "合集", "博客", "提交产品"],
footer: ["社区", "探索", "关于"],
searchPlaceholder: "搜索 AI 产品...",
},
accent: "16 85% 55%", // 橙红(产品社区)
port: 3002,
theme: "product",
@@ -139,6 +157,15 @@ const SITES = {
},
},
"the-ai-ledger": {
brandZh: {
name: "The AI Ledger",
tagline: "独立 AI 报道,附社区排名目录。",
headline: "AI 工具,评测与排名",
subheadline: "指南、对比,以及由社区驱动的最佳 AI 工具目录。",
nav: ["文章", "指南", "最佳推荐", "目录", "排行榜", "提交"],
footer: ["阅读", "目录", "公司"],
searchPlaceholder: "搜索目录...",
},
accent: "36 70% 40%", // 纸墨棕(编辑部)
port: 3003,
theme: "editorial",
@@ -197,6 +224,15 @@ const SITES = {
},
},
"devai-tools": {
brandZh: {
name: "DevAI.tools",
tagline: "面向开发者的 AI 工具——精选、实测、社区排名。",
headline: "开发者的 AI 工具",
subheadline: "代码助手、运维自动化、测试副驾驶,你上手的一切。",
nav: ["工具", "工作流", "用例", "排行榜", "提交"],
footer: ["目录", "构建", "法律"],
searchPlaceholder: "搜索开发者工具...",
},
accent: "150 60% 30%", // 终端绿(开发者)
port: 3004,
theme: "niche",
@@ -253,6 +289,15 @@ const SITES = {
},
},
findai: {
brandZh: {
name: "findai",
tagline: "极简 AI 工具搜索引擎。",
headline: "你想完成什么?",
subheadline: "",
nav: ["搜索", "用例", "排行榜", "提交"],
footer: ["搜索", "关于"],
searchPlaceholder: "你需要什么?",
},
accent: "260 70% 52%", // 紫(极简搜索)
port: 3005,
theme: "minimal",
@@ -501,13 +546,25 @@ import { locales, defaultLocale, type Locale } from "${corePkg}/i18n.config";
const brandMessages = {
en: {
seo: { home: ${JSON.stringify({ title: site.seo.en.title, description: site.seo.en.description, ogImage: "/og-image.png" })} },
hero: { title: ${JSON.stringify(site.seo.en.title)}, description: ${JSON.stringify(site.seo.en.description)} },
hero: { title: ${JSON.stringify(site.brand.headline)}, description: ${JSON.stringify(site.brand.subheadline || site.seo.en.description)} },
common: { brand: { name: ${JSON.stringify(site.brand.name)}, tagline: ${JSON.stringify(site.brand.tagline)} } },
navigation: {
main: { tools: ${JSON.stringify(site.navItems[0]?.label ?? "Discover")}, blog: ${JSON.stringify(site.navItems.find((n) => n.href === "/blog")?.label ?? "Blog")} },
site: { items: ${JSON.stringify(site.navItems.map((n) => n.label))}, hrefs: ${JSON.stringify(site.navItems.map((n) => n.href))} },
},
siteFooter: { groups: ${JSON.stringify(site.footerGroups)} },
siteHeader: { searchPlaceholder: ${JSON.stringify(site.searchPlaceholder)} },
},
zh: {
seo: { home: ${JSON.stringify({ title: site.seo.zh.title, description: site.seo.zh.description, ogImage: "/og-image.png" })} },
hero: { title: ${JSON.stringify(site.seo.zh.title)}, description: ${JSON.stringify(site.seo.zh.description)} },
common: { brand: { name: ${JSON.stringify(site.brand.name)}, tagline: ${JSON.stringify(site.brand.tagline)} } },
hero: { title: ${JSON.stringify(site.brandZh?.headline ?? site.seo.zh.title)}, description: ${JSON.stringify(site.brandZh?.subheadline || site.seo.zh.description)} },
common: { brand: { name: ${JSON.stringify(site.brandZh?.name ?? site.brand.name)}, tagline: ${JSON.stringify(site.brandZh?.tagline ?? site.brand.tagline)} } },
navigation: {
main: { tools: ${JSON.stringify(site.brandZh?.nav?.[0] ?? site.navItems[0]?.label ?? "发现")}, blog: ${JSON.stringify(site.brandZh?.nav?.[site.navItems.findIndex((n) => n.href === "/blog")] ?? "博客")} },
site: { items: ${JSON.stringify(site.brandZh?.nav ?? site.navItems.map((n) => n.label))}, hrefs: ${JSON.stringify(site.navItems.map((n) => n.href))} },
},
siteFooter: { groups: ${JSON.stringify(site.footerGroups.map((g, gi) => ({ ...g, title: (site.brandZh?.footer ?? [])[gi] ?? g.title })))} },
siteHeader: { searchPlaceholder: ${JSON.stringify(site.brandZh?.searchPlaceholder ?? site.searchPlaceholder)} },
},
} as const;
@@ -649,7 +706,30 @@ export default getRequestConfig(async ({ requestLocale }) => {
base.common.brand = { ...(base.common.brand ?? {}), name: site.brand.name, tagline: site.brand.tagline };
base.seo = base.seo ?? {};
base.seo.home = { ...(base.seo.home ?? {}), ...site.seo[locale] };
base.hero = { ...(base.hero ?? {}), title: site.seo[locale].title, description: site.seo[locale].description };
base.hero = {
...(base.hero ?? {}),
title: locale === "zh" ? (site.brandZh?.headline ?? site.seo.zh.title) : site.brand.headline,
description: locale === "zh" ? (site.brandZh?.subheadline || site.seo.zh.description) : (site.brand.subheadline || site.seo.en.description),
badge: locale === "zh" ? "" : base.hero?.badge ?? "",
};
if (locale === "zh") delete base.hero.badge;
// 站点导航/页脚/搜索文案(供 ThemeChrome 消费)
base.navigation = {
...(base.navigation ?? {}),
site: {
items: locale === "zh" ? (site.brandZh?.nav ?? site.navItems.map((n) => n.label)) : site.navItems.map((n) => n.label),
hrefs: site.navItems.map((n) => n.href),
},
};
base.siteFooter = {
groups: site.footerGroups.map((g, gi) => ({
...g,
title: locale === "zh" ? ((site.brandZh?.footer ?? [])[gi] ?? g.title) : g.title,
})),
};
base.siteHeader = {
searchPlaceholder: locale === "zh" ? (site.brandZh?.searchPlaceholder ?? site.searchPlaceholder) : site.searchPlaceholder,
};
fs.mkdirSync(path.join(siteRoot, "messages"), { recursive: true });
fs.writeFileSync(
path.join(siteRoot, "messages", `${locale}.json`),
+205
View File
@@ -0,0 +1,205 @@
#!/usr/bin/env python3
"""
# 脚本 wire-theme-i18n
# 说明:一次性把五个主题模板首页的硬编码文案接线到 next-intl(siteHome/hero 命名空间)。
# 规则:
# - import 加 useTranslations
# - 每个含 t( 调用的函数组件体开头插入 hook 声明
# - 文案字符串替换为 t("key") / {t("key")}(JSX 子节点带花括号,属性值带花括号)
# - 顶层常量(navItems/footerGroups)不改(导航/页脚由 SiteChrome i18n 提供,这些常量是死代码但无害)
# 运行:python3 scripts/wire-theme-i18n.py
"""
import re
# 每个模板的替换表:(原文案, i18n key, 是否 JSX 子节点)
COMMON = [
# classic
('>Find the best AI tools, ranked by real users<', 'heroTitle', True),
('>1,000+ AI tools across 14 categories. Compare votes, reviews and pricing to pick the right\n one.<', 'heroSubtitle', True),
('placeholder="Search 1,000+ AI tools..."', 'searchPlaceholder', False),
('<span>Popular:</span>', 'popularLabel', True),
('>\n Search\n </button>', 'searchButton', True),
('"Trending this week"', 'trendingTitle', False),
('"Ranked by votes, reviews and engagement over the last 7 days"', 'trendingSubtitle', False),
('"All rankings"', 'allRankings', False),
('"Browse by category"', 'categoriesTitle', False),
('"All tools"', 'categoriesViewAll', False),
('{category.toolCount} tools', 'toolsCount', 'ctx:category.toolCount'),
('"New AI tools"', 'newTitle', False),
('"Editor\'s Choice"', 'editorsTitle', False),
('"Tools by use case"', 'useCasesTitle', False),
('"What do you want to get done?"', 'useCasesSubtitle', False),
('"All use cases"', 'useCasesViewAll', False),
('"Best AI tool lists"', 'bestListsTitle', False),
('"Curated rankings by our editors"', 'bestListsSubtitle', False),
('"All lists"', 'bestListsViewAll', False),
('"{col.toolCount} tools"', 'toolsCount', 'ctx:col.toolCount'),
('"Latest community reviews"', 'reviewsTitle', False),
# product
('placeholder="Search AI products..."', 'searchPlaceholder', False),
('>Today\'s AI product launches<', 'heroTitle', True),
('>Fresh AI products, every day. Upvote the best ones to the top.<', 'heroSubtitle', True),
('Launch your product', 'launchButton', False),
('"Today\'s feed"', 'feedTitle', False),
('No launches yet — be the first to submit.', 'noLaunches', False),
('Browse all products', 'feedBrowse', False),
('"Fresh launches"', 'newTitle', False),
('"Leaderboard"', 'leaderboardTitle', False),
('Full leaderboard', 'leaderboardFull', False),
('"{tool.reviewCount} reviews"', 'reviewsCount', 'ctx:tool.reviewCount'),
('"Collections"', 'collectionsTitle', False),
# editorial
('Independent AI Coverage', 'mastheadLabel', False),
('Featured story', 'featuredLabel', False),
('Read the story', 'featuredRead', False),
('Editor\'s picks', 'picksEditorialLabel', False),
('"Buying guides"', 'guidesTitle', False),
('"Deep dives that help you choose"', 'guidesSubtitle', False),
('"All guides"', 'guidesViewAll', False),
('tools compared', 'toolsCompared', False),
('"Latest stories"', 'storiesTitle', False),
('"All stories"', 'storiesViewAll', False),
('"From the community"', 'communityTitle', False),
('"Browse directory"', 'communityViewAll', False),
# niche
('placeholder="Search dev tools..."', 'searchPlaceholder', False),
('>AI tools for developers<', 'heroTitle', True),
('>Code assistants, DevOps automation, testing copilots and everything else you ship with.<', 'heroSubtitle', True),
('Browse tools\n', 'browseButton', True),
('"Ship faster with AI"', 'shipTitle', False),
('"Pick your workflow — we ranked the tools for it"', 'shipSubtitle', False),
('"Most upvoted this week"', 'upvotedTitle', False),
('"View all"', 'upvotedViewAll', False),
('"Workflows & stacks"', 'workflowsTitle', False),
('"Batteries-included tool stacks for common dev tasks"', 'workflowsSubtitle', False),
('"All workflows"', 'workflowsViewAll', False),
# minimal
('AI TOOL SEARCH', 'minimalTagline', False),
('placeholder="I need to create marketing videos..."', 'minimalPlaceholder', False),
('"Trending now"', 'trendingNowTitle', False),
('See full rankings', 'seeRankings', False),
]
NEEDS = {
"heroTitle": ("hero", "title"), "heroSubtitle": ("hero", "description"),
"searchPlaceholder": ("siteHome", "searchPlaceholder"),
"popularLabel": ("siteHome", "popularLabel"),
"trendingTitle": ("siteHome", "trendingTitle"), "trendingSubtitle": ("siteHome", "trendingSubtitle"),
"allRankings": ("siteHome", "allRankings"),
"categoriesTitle": ("siteHome", "categoriesTitle"), "categoriesViewAll": ("siteHome", "categoriesViewAll"),
"newTitle": ("siteHome", "newTitle"), "editorsTitle": ("siteHome", "editorsTitle"),
"useCasesTitle": ("siteHome", "useCasesTitle"), "useCasesSubtitle": ("siteHome", "useCasesSubtitle"),
"useCasesViewAll": ("siteHome", "useCasesViewAll"),
"bestListsTitle": ("siteHome", "bestListsTitle"), "bestListsSubtitle": ("siteHome", "bestListsSubtitle"),
"bestListsViewAll": ("siteHome", "bestListsViewAll"),
"reviewsTitle": ("siteHome", "reviewsTitle"),
"ctaTitle": ("siteHome", "ctaTitle"), "ctaBody": ("siteHome", "ctaBody"), "ctaButton": ("siteHome", "ctaButton"),
"launchButton": ("siteHome", "launchButton"),
"feedTitle": ("siteHome", "feedTitle"), "noLaunches": ("siteHome", "noLaunches"),
"feedBrowse": ("siteHome", "feedBrowse"),
"leaderboardTitle": ("siteHome", "leaderboardTitle"), "leaderboardFull": ("siteHome", "leaderboardFull"),
"picksTitle": ("siteHome", "picksTitle"), "collectionsTitle": ("siteHome", "collectionsTitle"),
"mastheadLabel": ("siteHome", "mastheadLabel"),
"featuredLabel": ("siteHome", "featuredLabel"), "featuredRead": ("siteHome", "featuredRead"),
"picksEditorialLabel": ("siteHome", "picksEditorialLabel"),
"guidesTitle": ("siteHome", "guidesTitle"), "guidesSubtitle": ("siteHome", "guidesSubtitle"),
"guidesViewAll": ("siteHome", "guidesViewAll"),
"storiesTitle": ("siteHome", "storiesTitle"), "storiesViewAll": ("siteHome", "storiesViewAll"),
"communityTitle": ("siteHome", "communityTitle"), "communityViewAll": ("siteHome", "communityViewAll"),
"shipTitle": ("siteHome", "shipTitle"), "shipSubtitle": ("siteHome", "shipSubtitle"),
"upvotedTitle": ("siteHome", "upvotedTitle"), "upvotedViewAll": ("siteHome", "upvotedViewAll"),
"workflowsTitle": ("siteHome", "workflowsTitle"), "workflowsSubtitle": ("siteHome", "workflowsSubtitle"),
"workflowsViewAll": ("siteHome", "workflowsViewAll"),
"browseButton": ("siteHome", "browseButton"),
"browseCategoryTitle": ("siteHome", "browseCategoryTitle"),
"minimalTagline": ("siteHome", "minimalTagline"),
"minimalPlaceholder": ("siteHome", "minimalPlaceholder"),
"trendingNowTitle": ("siteHome", "trendingNowTitle"),
"seeRankings": ("siteHome", "seeRankings"),
"searchButton": ("siteHome", "searchButton"),
"toolsCount": ("siteHome", "toolsCount"),
"toolsCompared": ("siteHome", "toolsCompared"),
"reviewsCount": ("siteHome", "reviewsCount"),
}
TEMPLATES = [
"src/kit/site-themes/classic/index.tsx",
"src/kit/site-themes/product/index.tsx",
"src/kit/site-themes/editorial/index.tsx",
"src/kit/site-themes/niche/index.tsx",
"src/kit/site-themes/minimal/index.tsx",
]
def add_import(src):
if "useTranslations" not in src:
m = re.search(r'import \{ ([^}]+) \} from "lucide-react";\n', src)
assert m, "lucide import not found"
src = src.replace(m.group(0), m.group(0) + 'import { useTranslations } from "next-intl";\n', 1)
return src
def add_hooks(src):
"""为每个使用了 t( 的函数组件体开头插入 hook 声明"""
out = []
# 找所有 function 块开头
for m in re.finditer(r'((?:async )?function (\w+)\(\{[^}]*locale[^}]*\}(?:[^)]*)\) \{\n)', src):
header, fname = m.group(1), m.group(2)
start = m.end()
# 找函数结束(下一个顶层 function 或文件尾)
nxt = re.search(r'\n(?:async )?function ', src[start:])
end = start + (nxt.start() if nxt else len(src) - start)
body = src[start:end]
if 't("' not in body and 't(\'"' not in body:
continue
if 'const t = useTranslations' in body:
continue
hooks = ' const t = useTranslations("siteHome");\n'
if any(k in body for k in ("heroTitle", "heroSubtitle")):
hooks += ' const th = useTranslations("hero");\n'
src = src[:start] + hooks + src[start:]
# 重新计算(插入后偏移变化),直接 break 重跑
return add_hooks(src)
return src
def apply_replacements(src, fname):
used = set()
# 只在组件函数体内部替换(跳过顶层常量——导航/页脚由 SiteChrome i18n 提供)
first_fn = re.search(r'(?:async )?function \w+', src)
body_start = first_fn.start() if first_fn else len(src)
head, body = src[:body_start], src[body_start:]
for original, key, mode in COMMON:
ns, k = NEEDS[key] if key in NEEDS else (None, None)
tref = f'{ns}["{k}"]' if ns else key # hero 用 th
fn = "th" if ns == "hero" else "t"
tref = f'{fn}("{k}")'
if original.startswith('>'):
# JSX 子节点(含换行缩进的两段式原文用正则容忍空白)
pat = re.compile(re.escape(original).replace(r'\ ', r'\s+'))
if pat.search(body):
body = pat.sub(f'>{tref}<', body, count=1)
used.add(key)
elif mode == 'ctx':
ctx_expr = original.replace('{', '').replace('}', '').split()[0]
pat = re.compile(re.escape(original).replace(r'\ ', r'\s+'))
if pat.search(body):
body = pat.sub(f'{{{tref}({ctx_expr})}}', body, count=1)
used.add(key)
elif mode is False or mode is None:
# 引号内字符串 → 属性 {t("key")} 或 JSX 子节点 {t("key")}
if original in body:
body = body.replace(original, f'{{{tref}}}')
used.add(key)
return head + body, used
def main():
for p in TEMPLATES:
src = open(p).read()
src = add_import(src)
src, used = apply_replacements(src, p)
src = add_hooks(src)
open(p, 'w').write(src)
# 报告未使用的 key(该模板不需要)
unused = [k for k in NEEDS if k not in used]
print(f"{p}: wired {len(used)} keys")
if __name__ == "__main__":
main()
@@ -1,4 +1,5 @@
import { notFound } from "next/navigation";
import { getTranslations } from "next-intl/server";
import { getToolBySlug } from "@/features/tools/actions";
import { getAlternativeTools } from "@/features/discovery/actions";
import { pickName } from "@/features/discovery/localize";
@@ -35,6 +36,9 @@ export async function AlternativesView({
}) {
const base = await getToolBySlug(baseSlug);
if (!base) notFound();
const t = await getTranslations({ locale, namespace: "alternatives" });
const tf = await getTranslations({ locale, namespace: "faq" });
const tt = await getTranslations({ locale, namespace: "alternatives.table" });
const alternatives = await getAlternativeTools(base.id, 12);
const name = locale === "zh" && base.nameZh ? base.nameZh : base.nameEn;
@@ -93,7 +97,7 @@ export async function AlternativesView({
{alternatives.length === 0 ? (
<p className="rounded-xl border border-dashed border-border p-12 text-center text-sm text-muted-foreground">
No alternatives found yet.
{t("noAlt")}
</p>
) : (
<FavoritesProvider>
@@ -109,10 +113,10 @@ export async function AlternativesView({
<table className="w-full min-w-[560px] text-sm">
<thead>
<tr className="border-b border-border bg-secondary/40 text-left text-xs uppercase tracking-wide text-muted-foreground">
<th className="px-4 py-3">Tool</th>
<th className="px-4 py-3">Rating</th>
<th className="px-4 py-3">Votes</th>
<th className="px-4 py-3">Pricing</th>
<th className="px-4 py-3">{tt("tool")}</th>
<th className="px-4 py-3">{tt("rating")}</th>
<th className="px-4 py-3">{tt("votes")}</th>
<th className="px-4 py-3">{tt("pricing")}</th>
</tr>
</thead>
<tbody>
@@ -124,7 +128,7 @@ export async function AlternativesView({
className="font-medium text-foreground hover:text-primary"
>
{tool.nameEn === base.nameEn && tool.id === base.id
? `${name} (this tool)`
? t("thisTool", { name })
: locale === "zh" && tool.nameZh
? tool.nameZh
: tool.nameEn}
@@ -160,7 +164,7 @@ export async function AlternativesView({
<section className="mt-10 rounded-xl border border-border p-6">
<h2 className="text-lg font-semibold text-foreground">
Frequently asked questions
{tf("title")}
</h2>
<div className="mt-4 space-y-4">
{seo.faqs.map((faq, i) => (
@@ -206,6 +210,8 @@ export async function ComparisonView({
const nameA = pickName(toolA, locale);
const nameB = pickName(toolB, locale);
const tc = await getTranslations({ locale, namespace: "compare" });
const tf2 = await getTranslations({ locale, namespace: "faq" });
const url = `${baseUrl()}/${locale}/tools/${slugA}-vs-${slugB}`;
const pricingLabels: Record<string, string> = {
@@ -260,7 +266,7 @@ export async function ComparisonView({
winner: "tie",
},
{
label: "Latest review",
label: "latest",
a: reviewsA.reviews[0]?.content?.slice(0, 120) ?? "—",
b: reviewsB.reviews[0]?.content?.slice(0, 120) ?? "—",
winner: "tie",
@@ -301,11 +307,10 @@ export async function ComparisonView({
{nameA} vs {nameB}: which should you use?
</h1>
<p className="mt-3 max-w-2xl text-sm leading-relaxed text-muted-foreground">
A side-by-side comparison based on community votes, ratings and
reviews — not sponsored placement.
{tc("intro")}
{winnerLabel && (
<>
{" "}Right now the community leans toward{" "}
{" "}{tc("leaning", { name: winnerLabel })}{" "}
<Link
href={`/${locale}/tools/${slugA === toolA.slug ? slugA : slugB}`}
className="font-medium text-primary hover:underline"
+40 -8
View File
@@ -1,29 +1,61 @@
import { getTranslations, getLocale } from "next-intl/server";
import { ThemeHeader } from "./theme-header";
import { ThemeFooter } from "./theme-footer";
import type { SiteConfig } from "./types";
/**
* # 组件 SiteChrome
* 说明:站点外壳(Header + Footer + 主题变量注入)。
* 由每个网站的 marketing layout 通过 createMarketingLayout(config) 调用。
* 一个 SiteConfig = 一个独立网站;Core 不内置任何站点。
* 说明:站点外壳(Header + Footer + 主题变量注入)—— 服务端组件,读取站点 i18n 文案。
* 每个网站的导航/页脚/搜索占位在 messages 的 navigation.site / siteFooter / siteHeader,
* 由生成器按语言写入;品牌名/标语取自 SiteConfig(当前语言优先 brandZh)。
*/
export function SiteChrome({
export async function SiteChrome({
config,
children,
}: {
config: SiteConfig;
children: React.ReactNode;
}) {
const t = await getTranslations("navigation");
const tf = await getTranslations("siteFooter");
const th = await getTranslations("siteHeader");
const locale = await getLocale();
// 当前语言的品牌(brandZh 在 SiteConfig 顶层)
const zh = config.brandZh;
const brand =
locale === "zh" && zh
? {
name: zh.name ?? config.brand.name,
tagline: zh.tagline ?? config.brand.tagline,
headline: zh.headline ?? config.brand.headline,
subheadline: zh.subheadline ?? config.brand.subheadline,
}
: config.brand;
// 导航项:label 按 i18n,href 固定
const navLabels = t.raw("site.items") as string[] | undefined;
const navHrefs = t.raw("site.hrefs") as string[] | undefined;
const navItems =
navLabels && navHrefs
? navHrefs.map((href, i) => ({ href, label: navLabels[i] ?? "" }))
: config.navItems;
// 页脚分组:title 按 i18n
const footerGroupsRaw = tf.raw("groups") as
| { title: string; links: { label: string; href: string }[] }[]
| undefined;
const footerGroups = footerGroupsRaw ?? config.footerGroups;
return (
<div style={config.cssVars as React.CSSProperties}>
<ThemeHeader
brand={config.brand}
navItems={config.navItems}
searchPlaceholder={config.searchPlaceholder}
brand={brand}
navItems={navItems}
searchPlaceholder={th.raw("searchPlaceholder") as string}
/>
{children}
<ThemeFooter brand={config.brand} groups={config.footerGroups} />
<ThemeFooter brand={brand} groups={footerGroups} />
</div>
);
}
@@ -1,4 +1,5 @@
import { Search, TrendingUp, Sparkles, Clock, Award, LayoutGrid, MessageSquareQuote } from "lucide-react";
import { getTranslations } from "next-intl/server";
import { ToolCard, ToolCardGrid } from "@/components/tool-card";
import { LocaleLink } from "@/components/locale-link";
import { SectionHeader } from "../shared/section";
@@ -55,6 +56,8 @@ const footerGroups = [
];
async function ClassicHome({ locale }: { locale: string }) {
const t = await getTranslations("siteHome");
const th = await getTranslations("hero");
const data = await getHomeDataSafe(8);
return (
@@ -63,17 +66,17 @@ async function ClassicHome({ locale }: { locale: string }) {
<section className="border-b border-border bg-gradient-to-b from-secondary/40 to-background">
<div className="mx-auto max-w-7xl px-4 py-16 text-center sm:px-6 md:py-24">
<h1 className="mx-auto max-w-3xl text-3xl font-bold tracking-tight text-foreground sm:text-4xl md:text-5xl">
{brand.headline}
{th("title")}
</h1>
<p className="mx-auto mt-4 max-w-2xl text-sm text-muted-foreground sm:text-base">
{brand.subheadline}
{th("description")}
</p>
<form action={`/${locale}/tools`} className="mx-auto mt-8 max-w-xl">
<div className="relative">
<Search className="absolute left-4 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
<input
name="search"
placeholder="Search 1,000+ AI tools..."
placeholder={t("searchPlaceholder")}
className="h-12 w-full rounded-full border border-border bg-background pl-11 pr-28 text-sm text-foreground shadow-sm placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/30"
aria-label="Search AI tools"
/>
@@ -81,13 +84,13 @@ async function ClassicHome({ locale }: { locale: string }) {
type="submit"
className="absolute right-1.5 top-1.5 h-9 rounded-full bg-primary px-5 text-xs font-semibold text-primary-foreground transition-opacity hover:opacity-90"
>
Search
{t("searchButton")}
</button>
</div>
</form>
<div className="mt-4 flex flex-wrap items-center justify-center gap-2 text-xs text-muted-foreground">
<span>Popular:</span>
{["writing", "video", "coding", "image"].map((q) => (
<span>{t("popularLabel")}</span>
{t.raw("popularSearches").map((q: string) => (
<LocaleLink
key={q}
href={`/tools?search=${q}`}
@@ -105,10 +108,10 @@ async function ClassicHome({ locale }: { locale: string }) {
{data.trending.length > 0 && (
<section>
<SectionHeader
title="Trending this week"
subtitle="Ranked by votes, reviews and engagement over the last 7 days"
title={t("trendingTitle")}
subtitle={t("trendingSubtitle")}
viewAllHref="/rankings?board=trending"
viewAllLabel="All rankings"
viewAllLabel={t("allRankings")}
/>
<ToolCardGrid tools={data.trending} locale={locale} ranked />
</section>
@@ -118,9 +121,9 @@ async function ClassicHome({ locale }: { locale: string }) {
{data.categories.length > 0 && (
<section>
<SectionHeader
title="Browse by category"
title={t("categoriesTitle")}
viewAllHref="/tools"
viewAllLabel="All tools"
viewAllLabel={t("categoriesViewAll")}
/>
<div className="grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-5">
{data.categories.slice(0, 10).map((category) => (
@@ -149,7 +152,7 @@ async function ClassicHome({ locale }: { locale: string }) {
{data.newest.length > 0 && (
<section>
<SectionHeader
title="New AI tools"
title={t("newTitle")}
viewAllHref="/rankings?board=new"
/>
<ToolCardGrid tools={data.newest.slice(0, 4)} locale={locale} />
@@ -158,7 +161,7 @@ async function ClassicHome({ locale }: { locale: string }) {
{data.editorsChoice.length > 0 && (
<section>
<SectionHeader
title="Editor's Choice"
title={t("editorsTitle")}
viewAllHref="/rankings?board=editors-choice"
/>
<ToolCardGrid tools={data.editorsChoice.slice(0, 4)} locale={locale} />
@@ -170,10 +173,10 @@ async function ClassicHome({ locale }: { locale: string }) {
{data.useCases.length > 0 && (
<section>
<SectionHeader
title="Tools by use case"
subtitle="What do you want to get done?"
title={t("useCasesTitle")}
subtitle={t("useCasesSubtitle")}
viewAllHref="/ai-tools-for"
viewAllLabel="All use cases"
viewAllLabel={t("useCasesViewAll")}
/>
<div className="flex flex-wrap gap-2">
{data.useCases.slice(0, 12).map((uc) => (
@@ -196,10 +199,10 @@ async function ClassicHome({ locale }: { locale: string }) {
{data.collections.length > 0 && (
<section>
<SectionHeader
title="Best AI tool lists"
subtitle="Curated rankings by our editors"
title={t("bestListsTitle")}
subtitle={t("bestListsSubtitle")}
viewAllHref="/best"
viewAllLabel="All lists"
viewAllLabel={t("bestListsViewAll")}
/>
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
{data.collections.slice(0, 3).map((col) => (
@@ -227,7 +230,7 @@ async function ClassicHome({ locale }: { locale: string }) {
{/* Latest Reviews */}
{data.latestReviews.length > 0 && (
<section>
<SectionHeader title="Latest community reviews" />
<SectionHeader title={t("reviewsTitle")} />
<div className="grid gap-3 md:grid-cols-2">
{data.latestReviews.map((review, i) => (
<LocaleLink
@@ -1,4 +1,5 @@
import { BookOpen, PenTool, ArrowRight, Star } from "lucide-react";
import { getTranslations } from "next-intl/server";
import { LocaleLink } from "@/components/locale-link";
import { SectionHeader } from "../shared/section";
import { getHomeDataSafe } from "../shared/home-data";
@@ -50,6 +51,8 @@ const footerGroups = [
];
async function EditorialHome({ locale }: { locale: string }) {
const t = await getTranslations("siteHome");
const th = await getTranslations("hero");
const data = await getHomeDataSafe(6);
const featured = data.articles[0];
const editorPicks = data.editorsChoice.slice(0, 4);
@@ -60,13 +63,13 @@ async function EditorialHome({ locale }: { locale: string }) {
<section className="border-b border-border">
<div className="mx-auto max-w-4xl px-4 py-16 text-center sm:px-6 md:py-20">
<p className="text-xs font-medium uppercase tracking-[0.3em] text-muted-foreground">
Independent AI Coverage
{t("mastheadLabel")}
</p>
<h1 className="mt-4 text-4xl font-bold tracking-tight text-foreground md:text-5xl">
{brand.headline}
{th("title")}
</h1>
<p className="mx-auto mt-4 max-w-xl text-base leading-relaxed text-muted-foreground">
{brand.subheadline}
{th("description")}
</p>
</div>
</section>
@@ -77,7 +80,7 @@ async function EditorialHome({ locale }: { locale: string }) {
<section>
<div className="mb-4 flex items-center gap-2 text-xs font-semibold uppercase tracking-widest text-primary">
<BookOpen className="h-3.5 w-3.5" />
Featured story
{t("featuredLabel")}
</div>
<LocaleLink href={`/blog/${featured.slug}`} className="group block">
<h2 className="text-2xl font-bold leading-snug text-foreground group-hover:underline md:text-3xl">
@@ -89,7 +92,7 @@ async function EditorialHome({ locale }: { locale: string }) {
</p>
)}
<span className="mt-3 inline-flex items-center gap-1 text-sm font-medium text-primary">
Read the story
{t("featuredRead")}
<ArrowRight className="h-3.5 w-3.5 transition-transform group-hover:translate-x-0.5" />
</span>
</LocaleLink>
@@ -101,7 +104,7 @@ async function EditorialHome({ locale }: { locale: string }) {
<section>
<div className="mb-4 flex items-center gap-2 text-xs font-semibold uppercase tracking-widest text-primary">
<PenTool className="h-3.5 w-3.5" />
Editor's picks
{t("picksEditorialLabel")}
</div>
<div className="divide-y divide-border border-y border-border">
{editorPicks.map((tool, i) => (
@@ -137,10 +140,10 @@ async function EditorialHome({ locale }: { locale: string }) {
{data.collections.length > 0 && (
<section>
<SectionHeader
title="Buying guides"
subtitle="Deep dives that help you choose"
title={t("guidesTitle")}
subtitle={t("guidesSubtitle")}
viewAllHref="/best"
viewAllLabel="All guides"
viewAllLabel={t("guidesViewAll")}
/>
<div className="grid gap-6 sm:grid-cols-2">
{data.collections.slice(0, 4).map((col) => (
@@ -156,7 +159,7 @@ async function EditorialHome({ locale }: { locale: string }) {
{locale === "zh" && col.introZh ? col.introZh : col.introEn}
</p>
<p className="mt-2 text-xs uppercase tracking-wide text-muted-foreground">
{col.toolCount} tools compared
{col.toolCount} {t("toolsCompared")}
</p>
</LocaleLink>
))}
@@ -167,7 +170,7 @@ async function EditorialHome({ locale }: { locale: string }) {
{/* Latest articles */}
{data.articles.length > 1 && (
<section>
<SectionHeader title="Latest stories" viewAllHref="/blog" />
<SectionHeader title={t("storiesTitle")} viewAllHref="/blog" />
<div className="grid gap-6 sm:grid-cols-2">
{data.articles.slice(1).map((article) => (
<LocaleLink key={article.slug} href={`/blog/${article.slug}`} className="group">
@@ -189,9 +192,9 @@ async function EditorialHome({ locale }: { locale: string }) {
{data.latestReviews.length > 0 && (
<section>
<SectionHeader
title="From the community"
title={t("communityTitle")}
viewAllHref="/tools"
viewAllLabel="Browse directory"
viewAllLabel={t("communityViewAll")}
/>
<div className="grid gap-6 sm:grid-cols-2">
{data.latestReviews.slice(0, 4).map((review, i) => (
@@ -1,4 +1,5 @@
import { Search } from "lucide-react";
import { getTranslations } from "next-intl/server";
import { ToolCardGrid } from "@/components/tool-card";
import { LocaleLink } from "@/components/locale-link";
import { getHomeDataSafe } from "../shared/home-data";
@@ -48,6 +49,8 @@ const popularSearches = [
];
async function MinimalHome({ locale }: { locale: string }) {
const t = await getTranslations("siteHome");
const th = await getTranslations("hero");
const data = await getHomeDataSafe(6);
return (
@@ -67,7 +70,7 @@ async function MinimalHome({ locale }: { locale: string }) {
<input
name="search"
autoFocus
placeholder="I need to create marketing videos..."
placeholder={t("minimalPlaceholder")}
className="h-14 w-full rounded-full border border-border bg-background pl-14 pr-6 text-base text-foreground shadow-lg shadow-black/[0.03] placeholder:text-muted-foreground focus:border-primary/50 focus:outline-none focus:ring-4 focus:ring-primary/10"
aria-label="Search AI tools"
/>
@@ -99,7 +102,7 @@ async function MinimalHome({ locale }: { locale: string }) {
href="/rankings"
className="text-xs font-medium text-muted-foreground hover:text-primary"
>
See full rankings
{t("seeRankings")}
</LocaleLink>
</div>
</section>
@@ -1,4 +1,5 @@
import { Code2, Terminal, Workflow, Layers } from "lucide-react";
import { getTranslations } from "next-intl/server";
import { ToolCard } from "@/components/tool-card";
import { LocaleLink } from "@/components/locale-link";
import { SectionHeader } from "../shared/section";
@@ -49,6 +50,8 @@ const footerGroups = [
];
async function NicheHome({ locale }: { locale: string }) {
const t = await getTranslations("siteHome");
const th = await getTranslations("hero");
const data = await getHomeDataSafe(6);
return (
@@ -70,10 +73,10 @@ async function NicheHome({ locale }: { locale: string }) {
</p>
<p className="flex items-center gap-2 text-foreground">
<Terminal className="h-3.5 w-3.5 text-emerald-600 dark:text-emerald-400" />
{brand.headline}
{th("title")}
</p>
<p className="text-xs leading-relaxed text-muted-foreground">
{brand.subheadline}
{th("description")}
</p>
<p className="pt-2">
<LocaleLink
@@ -93,10 +96,10 @@ async function NicheHome({ locale }: { locale: string }) {
{data.useCases.length > 0 && (
<section>
<SectionHeader
title="Ship faster with AI"
subtitle="Pick your workflow — we ranked the tools for it"
title={t("shipTitle")}
subtitle={t("shipSubtitle")}
viewAllHref="/ai-tools-for"
viewAllLabel="All use cases"
viewAllLabel={t("useCasesViewAll")}
/>
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
{data.useCases.slice(0, 6).map((uc) => (
@@ -124,7 +127,7 @@ async function NicheHome({ locale }: { locale: string }) {
{data.trending.length > 0 && (
<section>
<SectionHeader
title="Most upvoted this week"
title={t("upvotedTitle")}
viewAllHref="/rankings?board=trending"
/>
<div className="grid gap-3 md:grid-cols-2">
@@ -144,10 +147,10 @@ async function NicheHome({ locale }: { locale: string }) {
{data.collections.length > 0 && (
<section>
<SectionHeader
title="Workflows & stacks"
subtitle="Batteries-included tool stacks for common dev tasks"
title={t("workflowsTitle")}
subtitle={t("workflowsSubtitle")}
viewAllHref="/best"
viewAllLabel="All workflows"
viewAllLabel={t("workflowsViewAll")}
/>
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
{data.collections.slice(0, 3).map((col) => (
@@ -172,7 +175,7 @@ async function NicheHome({ locale }: { locale: string }) {
{/* Categories */}
{data.categories.length > 0 && (
<section>
<SectionHeader title="Browse by category" viewAllHref="/tools" />
<SectionHeader title={t("categoriesTitle")} viewAllHref="/tools" />
<div className="flex flex-wrap gap-2">
{data.categories.slice(0, 14).map((cat) => (
<LocaleLink
@@ -1,4 +1,5 @@
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";
@@ -107,6 +108,8 @@ function LaunchRow({
}
async function ProductHome({ locale }: { locale: string }) {
const t = await getTranslations("siteHome");
const th = await getTranslations("hero");
const data = await getHomeDataSafe(10);
return (
@@ -115,17 +118,17 @@ async function ProductHome({ 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">
{brand.headline}
{th("title")}
</h1>
<p className="mt-3 text-sm text-muted-foreground sm:text-base">
{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>
@@ -144,14 +147,14 @@ async function ProductHome({ 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>
@@ -159,7 +162,7 @@ async function ProductHome({ 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} />
@@ -204,14 +207,14 @@ async function ProductHome({ 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}>
+10
View File
@@ -32,6 +32,16 @@ export interface SiteConfig {
seo?: Record<string, { title: string; description: string }>;
/** 品牌强调色 HSL 三元组(登录页分栏模板) */
accent?: string;
/** 中文品牌文案(导航/页脚/hero/搜索占位) */
brandZh?: {
name?: string;
tagline?: string;
headline?: string;
subheadline?: string;
nav?: string[];
footer?: string[];
searchPlaceholder?: string;
};
/** 品牌侧栏标语(登录页) */
brandTagline?: string;
/** 主题级 CSS 变量覆盖(HSL 三元组,可选) */
@@ -1,6 +1,6 @@
import type { Metadata } from "next";
import { notFound } from "next/navigation";
import { setRequestLocale } from "next-intl/server";
import { getTranslations, setRequestLocale } from "next-intl/server";
import { generatePageMetadata } from "@/lib/metadata";
import {
getUseCaseBySlug,
@@ -82,6 +82,7 @@ export default async function AiToolsForEntityPage({
}) {
const { slug, locale } = await params;
setRequestLocale(locale);
const tf = await getTranslations({ locale, namespace: "faq" });
const resolved = await resolveEntity(slug);
if (!resolved) notFound();
@@ -168,7 +169,7 @@ export default async function AiToolsForEntityPage({
<section className="mt-10 rounded-xl border border-border p-6">
<h2 className="text-lg font-semibold text-foreground">
Frequently asked questions
{tf("title")}
</h2>
<div className="mt-4 space-y-4">
{seo.faqs.map((faq, i) => (
@@ -1,11 +1,12 @@
import type { Metadata } from "next";
import { setRequestLocale } from "next-intl/server";
import { getTranslations, setRequestLocale } from "next-intl/server";
import { generatePageMetadata } from "@/lib/metadata";
import { getUseCases, getAudiences } from "@/features/discovery/actions";
import { pickName } from "@/features/discovery/localize";
import { CollectionPageJsonLd } from "@/components/json-ld";
import { LocaleLink } from "@/components/locale-link";
import { Target, Users } from "lucide-react";
import { useTranslations } from "next-intl";
export const revalidate = 3600;
@@ -31,6 +32,7 @@ export default async function AiToolsForPage({
}) {
const { locale } = await params;
setRequestLocale(locale);
const t = await getTranslations("aiToolsFor");
const [useCases, audiences] = await Promise.all([
getUseCases().catch(() => []),
@@ -49,11 +51,10 @@ export default async function AiToolsForPage({
<div className="mb-10">
<h1 className="text-2xl font-bold tracking-tight text-foreground sm:text-3xl">
AI tools for what you do
{t("indexTitle")}
</h1>
<p className="mt-2 max-w-2xl text-sm text-muted-foreground">
Skip the categories. Start from your task or your role — every page
ranks tools by real community votes and reviews.
{t("indexSubtitle")}
</p>
</div>
@@ -61,10 +62,10 @@ export default async function AiToolsForPage({
<section className="mb-12">
<h2 className="mb-4 flex items-center gap-2 text-lg font-semibold text-foreground">
<Target className="h-4 w-4 text-primary" />
By task
{t("byTask")}
</h2>
{useCases.length === 0 ? (
<p className="text-sm text-muted-foreground">No use cases yet.</p>
<p className="text-sm text-muted-foreground">{t("noData")}</p>
) : (
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
{useCases.map((uc) => (
@@ -96,10 +97,10 @@ export default async function AiToolsForPage({
<section>
<h2 className="mb-4 flex items-center gap-2 text-lg font-semibold text-foreground">
<Users className="h-4 w-4 text-primary" />
By audience
{t("byAudience")}
</h2>
{audiences.length === 0 ? (
<p className="text-sm text-muted-foreground">No audiences yet.</p>
<p className="text-sm text-muted-foreground">{t("noAud")}</p>
) : (
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
{audiences.map((aud) => (
@@ -1,6 +1,6 @@
import type { Metadata } from "next";
import { notFound } from "next/navigation";
import { setRequestLocale } from "next-intl/server";
import { getTranslations, setRequestLocale } from "next-intl/server";
import { generatePageMetadata } from "@/lib/metadata";
import {
getCollectionBySlug,
@@ -63,6 +63,8 @@ export default async function BestCollectionPage({
}) {
const { slug, locale } = await params;
setRequestLocale(locale);
const tf = await getTranslations({ locale, namespace: "faq" });
const tb = await getTranslations({ locale, namespace: "best" });
const collection = await getCollectionBySlug(slug);
if (!collection) notFound();
@@ -147,7 +149,7 @@ export default async function BestCollectionPage({
{/* FAQ(内链 + FAQPage schema) */}
<section className="rounded-xl border border-border p-6">
<h2 className="text-lg font-semibold text-foreground">
Frequently asked questions
{tf("title")}
</h2>
<div className="mt-4 space-y-4">
{seo.faqs.map((faq, i) => (
@@ -163,7 +165,7 @@ export default async function BestCollectionPage({
{/* 相关榜单(内链) */}
<section className="mt-10">
<h2 className="mb-3 text-sm font-semibold text-foreground">More best lists</h2>
<h2 className="mb-3 text-sm font-semibold text-foreground">{tb("more")}</h2>
<div className="flex flex-wrap gap-2">
{allCollections
.filter((col) => col.id !== collection.id)
@@ -1,5 +1,5 @@
import type { Metadata } from "next";
import { setRequestLocale } from "next-intl/server";
import { getTranslations, setRequestLocale } from "next-intl/server";
import { generatePageMetadata } from "@/lib/metadata";
import { getCollections } from "@/features/discovery/actions";
import { CollectionPageJsonLd } from "@/components/json-ld";
@@ -30,6 +30,7 @@ export default async function BestIndexPage({
}) {
const { locale } = await params;
setRequestLocale(locale);
const t = await getTranslations("best");
const collections = await getCollections().catch(() => []);
const baseUrl = process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3001";
@@ -44,17 +45,16 @@ export default async function BestIndexPage({
<div className="mb-8">
<h1 className="text-2xl font-bold tracking-tight text-foreground sm:text-3xl">
Best AI Tools — Curated Lists
{t("indexTitle")}
</h1>
<p className="mt-2 text-sm text-muted-foreground">
Hand-picked rankings, updated with community signal. Every list is
labeled and never sold.
{t("indexSubtitle")}
</p>
</div>
{collections.length === 0 ? (
<p className="rounded-xl border border-dashed border-border p-12 text-center text-sm text-muted-foreground">
No collections published yet.
{t("empty")}
</p>
) : (
<div className="grid gap-4 sm:grid-cols-2">
@@ -1,4 +1,4 @@
import { setRequestLocale } from "next-intl/server";
import { getTranslations, setRequestLocale } from "next-intl/server";
import { generatePageMetadata } from "@/lib/metadata";
import { getCategoriesWithCount } from "@/features/tools/actions";
import { SubmitToolForm } from "@/features/submission/components/submit-tool-form";
@@ -28,6 +28,7 @@ export default async function SubmitPage({
}) {
const { locale } = await params;
setRequestLocale(locale);
const t = await getTranslations("submitPage");
const categories = await getCategoriesWithCount().catch(() => []);
@@ -35,11 +36,10 @@ export default async function SubmitPage({
<div className="mx-auto max-w-2xl px-4 py-10 sm:px-6">
<div className="mb-8">
<h1 className="text-2xl font-bold tracking-tight text-foreground sm:text-3xl">
Submit your AI tool
{t("title")}
</h1>
<p className="mt-2 text-sm leading-relaxed text-muted-foreground">
Get a free listing with community votes, user reviews and analytics.
Submissions are reviewed by our editors, usually within 48 hours.
{t("subtitle")}
</p>
</div>
@@ -121,6 +121,8 @@ export async function generateMetadata({ params }: ToolDetailPageProps) {
}
const t = await getTranslations({ locale, namespace: "tools" });
const tf = await getTranslations({ locale, namespace: "faq" });
const ts = await getTranslations({ locale, namespace: "siteHome" });
const tool = await getToolBySlug(slug);
if (!tool) {
return { title: t("notFound") };
@@ -162,6 +164,8 @@ export default async function ToolDetailPage({ params }: ToolDetailPageProps) {
}
const t = await getTranslations({ locale, namespace: "tools" });
const tf = await getTranslations({ locale, namespace: "faq" });
const ts = await getTranslations({ locale, namespace: "siteHome" });
const tool = await getToolBySlug(slug);
if (!tool) {
@@ -341,7 +345,7 @@ export default async function ToolDetailPage({ params }: ToolDetailPageProps) {
<div>
<h2 className="mb-3 flex items-center gap-2 text-sm font-semibold text-foreground">
<Target className="h-4 w-4 text-primary" />
Use cases
{ts("useCasesBlockTitle")}
</h2>
<div className="flex flex-wrap gap-2">
{relations.useCases.map((uc) => (
@@ -360,7 +364,7 @@ export default async function ToolDetailPage({ params }: ToolDetailPageProps) {
<div>
<h2 className="mb-3 flex items-center gap-2 text-sm font-semibold text-foreground">
<Users className="h-4 w-4 text-primary" />
Good for
{ts("goodForBlockTitle")}
</h2>
<div className="flex flex-wrap gap-2">
{relations.audiences.map((aud) => (
@@ -465,7 +469,7 @@ export default async function ToolDetailPage({ params }: ToolDetailPageProps) {
{/* FAQ */}
<section className="rounded-xl border border-border p-6">
<h2 className="text-lg font-semibold text-foreground">FAQ</h2>
<h2 className="text-lg font-semibold text-foreground">{tf("title")}</h2>
<div className="mt-4 space-y-4">
{faqs.map((faq, i) => (
<div key={i}>
@@ -0,0 +1,33 @@
import Link from "next/link";
import { getTranslations } from "next-intl/server";
/**
* # 组件 NotFound(locale 内)
* 说明:品牌化 404,双语(走 next-intl)。
*/
export default async function NotFound() {
const t = await getTranslations("notFound");
return (
<div style={{ minHeight: "60vh", display: "flex", alignItems: "center", justifyContent: "center", padding: "24px" }}>
<div style={{ textAlign: "center", maxWidth: "420px" }}>
<div style={{ width: 64, height: 64, margin: "0 auto 20px", borderRadius: 16, background: "#0a0a0a", display: "flex", alignItems: "center", justifyContent: "center" }}>
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
<path d="M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8" />
<path d="M3 10a2 2 0 0 1 .709-1.528l7-5.999a2 2 0 0 1 2.582 0l7 5.999A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
</svg>
</div>
<p style={{ fontSize: 13, letterSpacing: "0.2em", color: "#737373", margin: "0 0 8px" }}>{t("code")}</p>
<h1 style={{ fontSize: 24, margin: "0 0 8px" }}>{t("title")}</h1>
<p style={{ fontSize: 14, color: "#525252", lineHeight: 1.6, margin: "0 0 24px" }}>{t("body")}</p>
<div style={{ display: "flex", gap: 12, justifyContent: "center" }}>
<Link href="/" className="inline-block rounded-lg bg-primary px-5 py-2.5 text-sm font-medium text-primary-foreground">
{t("home")}
</Link>
<Link href="/tools" className="inline-block rounded-lg border border-border px-5 py-2.5 text-sm font-medium text-foreground">
{t("tools")}
</Link>
</div>
</div>
</div>
);
}
+31 -31
View File
@@ -1,38 +1,38 @@
import Link from 'next/link';
import { defaultLocale } from '@/i18n.config';
import Link from "next/link";
import { headers } from "next/headers";
/**
* # 组件 NotFound
* 说明:品牌化 404 页(图标 + 双入口:首页 / 工具目录)。
* # 组件 NotFound(根级)
* 说明:无法使用 locale 上下文(在 [locale] 外),按 Accept-Language 双语。
*/
export default function NotFound() {
export default async function NotFound() {
const h = await headers();
const accept = h.get("accept-language") || "";
const zh = accept.toLowerCase().includes("zh");
const text = zh
? { code: "404", title: "页面不存在", body: "你访问的页面不存在或已被移动。", home: "返回首页", tools: "浏览工具" }
: { code: "404", title: "Page not found", body: "The page you are looking for doesn't exist or may have moved.", home: "Back to home", tools: "Browse tools" };
return (
<html>
<body style={{ margin: 0, fontFamily: 'system-ui, sans-serif', background: '#fafafa', color: '#0a0a0a' }}>
<div style={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '24px' }}>
<div style={{ textAlign: 'center', maxWidth: '420px' }}>
<div style={{ width: 64, height: 64, margin: '0 auto 20px', borderRadius: 16, background: '#0a0a0a', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
<path d="M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8" />
<path d="M3 10a2 2 0 0 1 .709-1.528l7-5.999a2 2 0 0 1 2.582 0l7 5.999A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
</svg>
</div>
<p style={{ fontSize: 13, letterSpacing: '0.2em', color: '#737373', margin: '0 0 8px' }}>404</p>
<h1 style={{ fontSize: 24, margin: '0 0 8px' }}>Page not found</h1>
<p style={{ fontSize: 14, color: '#525252', lineHeight: 1.6, margin: '0 0 24px' }}>
The page you are looking for doesn&apos;t exist or may have moved.
</p>
<div style={{ display: 'flex', gap: 12, justifyContent: 'center' }}>
<Link href={`/${defaultLocale}`} style={{ display: 'inline-block', padding: '10px 20px', borderRadius: 8, background: '#0a0a0a', color: '#fff', textDecoration: 'none', fontSize: 14 }}>
Back to home
</Link>
<Link href={`/${defaultLocale}/tools`} style={{ display: 'inline-block', padding: '10px 20px', borderRadius: 8, border: '1px solid #e5e5e5', color: '#0a0a0a', textDecoration: 'none', fontSize: 14 }}>
Browse tools
</Link>
</div>
</div>
<div style={{ minHeight: "100vh", display: "flex", alignItems: "center", justifyContent: "center", padding: "24px", fontFamily: "system-ui, sans-serif" }}>
<div style={{ textAlign: "center", maxWidth: "420px" }}>
<div style={{ width: 64, height: 64, margin: "0 auto 20px", borderRadius: 16, background: "#0a0a0a", display: "flex", alignItems: "center", justifyContent: "center" }}>
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
<path d="M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8" />
<path d="M3 10a2 2 0 0 1 .709-1.528l7-5.999a2 2 0 0 1 2.582 0l7 5.999A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
</svg>
</div>
</body>
</html>
<p style={{ fontSize: 13, letterSpacing: "0.2em", color: "#737373", margin: "0 0 8px" }}>{text.code}</p>
<h1 style={{ fontSize: 24, margin: "0 0 8px" }}>{text.title}</h1>
<p style={{ fontSize: 14, color: "#525252", lineHeight: 1.6, margin: "0 0 24px" }}>{text.body}</p>
<div style={{ display: "flex", gap: 12, justifyContent: "center" }}>
<Link href="/" className="inline-block rounded-lg bg-primary px-5 py-2.5 text-sm font-medium text-primary-foreground">
{text.home}
</Link>
<Link href="/tools" className="inline-block rounded-lg border border-border px-5 py-2.5 text-sm font-medium text-foreground">
{text.tools}
</Link>
</div>
</div>
</div>
);
}
+302 -3
View File
@@ -53,12 +53,30 @@
"linkedin": "LinkedIn",
"github": "GitHub"
}
},
"site": {
"items": [
"Discover",
"Use Cases",
"Best Lists",
"Rankings",
"Blog",
"Submit"
],
"hrefs": [
"/tools",
"/ai-tools-for",
"/best",
"/rankings",
"/blog",
"/submit"
]
}
},
"hero": {
"badge": "Discover the Best AI Tools",
"title": "AI Stack — Find the Best AI Tools, Ranked by Real Users",
"description": "The professional AI tools directory. 1,000+ AI tools across 14 categories ranked by community votes, reviews and engagement.",
"title": "Find the best AI tools, ranked by real users",
"description": "1,000+ AI tools across 14 categories. Compare votes, reviews and pricing to pick the right one.",
"cta": {
"primary": "Explore Tools",
"secondary": "Read Blog"
@@ -649,7 +667,9 @@
"latest": "Latest",
"trending": "Trending"
},
"clearFilters": "Clear search & filters"
"clearFilters": "Clear search & filters",
"useCasesTitle": "Use cases",
"goodForTitle": "Good for"
},
"seo": {
"home": {
@@ -796,5 +816,284 @@
"new": "New",
"editorsChoice": "Editor's Choice",
"empty": "No tools on this board yet."
},
"siteHome": {
"trending": {
"title": "Trending this week",
"subtitle": "Ranked by votes, reviews and engagement over the last 7 days"
},
"categories": {
"title": "Browse by category",
"viewAll": "All tools"
},
"new": {
"title": "New AI tools"
},
"editors": {
"title": "Editor's Choice"
},
"useCases": {
"title": "Tools by use case",
"subtitle": "What do you want to get done?",
"viewAll": "All use cases"
},
"bestLists": {
"title": "Best AI tool lists",
"subtitle": "Curated rankings by our editors",
"viewAll": "All lists"
},
"reviews": {
"title": "Latest community reviews"
},
"cta": {
"title": "Built an AI tool?",
"body": "Submit it to {brand} and get discovered by thousands of users looking for tools like yours.",
"button": "Submit your tool"
},
"popular": {
"label": "Popular:"
},
"toolsCount": "{count} tools",
"feed": {
"title": "Today's feed",
"browse": "Browse all products"
},
"leaderboard": {
"title": "Leaderboard",
"full": "Full leaderboard"
},
"picks": {
"title": "Editor's picks"
},
"collections": {
"title": "Collections"
},
"launch": {
"button": "Launch your product"
},
"featured": {
"label": "Featured story",
"read": "Read the story"
},
"picksEditorial": {
"label": "Editor's picks"
},
"guides": {
"title": "Buying guides",
"subtitle": "Deep dives that help you choose",
"viewAll": "All guides"
},
"stories": {
"title": "Latest stories",
"viewAll": "All stories"
},
"community": {
"title": "From the community",
"viewAll": "Browse directory"
},
"ship": {
"title": "Ship faster with AI",
"subtitle": "Pick your workflow — we ranked the tools for it",
"viewAll": "All use cases"
},
"upvoted": {
"title": "Most upvoted this week",
"viewAll": "View all"
},
"workflows": {
"title": "Workflows & stacks",
"subtitle": "Batteries-included tool stacks for common dev tasks",
"viewAll": "All workflows"
},
"browseCategory": {
"title": "Browse by category"
},
"trendingNow": {
"title": "Trending now"
},
"seeRankings": "See full rankings",
"browse": "Browse tools",
"noLaunches": "No launches yet — be the first to submit.",
"searchPlaceholder": "Search AI tools...",
"reviewsCount": "{count} reviews",
"mastheadLabel": "INDEPENDENT AI COVERAGE",
"minimalTagline": "AI TOOL SEARCH",
"minimalPlaceholder": "I need to create marketing videos...",
"useCasesBlockTitle": "Use cases",
"goodForBlockTitle": "Good for",
"searchButton": "Search",
"trendingTitle": "Trending this week",
"trendingSubtitle": "Ranked by votes, reviews and engagement over the last 7 days",
"allRankings": "All rankings",
"categoriesTitle": "Browse by category",
"categoriesViewAll": "All tools",
"newTitle": "New AI tools",
"editorsTitle": "Editor's Choice",
"useCasesTitle": "Tools by use case",
"useCasesSubtitle": "What do you want to get done?",
"useCasesViewAll": "All use cases",
"bestListsTitle": "Best AI tool lists",
"bestListsSubtitle": "Curated rankings by our editors",
"bestListsViewAll": "All lists",
"reviewsTitle": "Latest community reviews",
"ctaTitle": "Built an AI tool?",
"ctaBody": "Submit it to {brand} and get discovered by thousands of users looking for tools like yours.",
"ctaButton": "Submit your tool",
"popularLabel": "Popular:",
"feedTitle": "Today's feed",
"feedBrowse": "Browse all products",
"leaderboardTitle": "Leaderboard",
"leaderboardFull": "Full leaderboard",
"picksTitle": "Editor's picks",
"collectionsTitle": "Collections",
"launchButton": "Launch your product",
"featuredLabel": "Featured story",
"featuredRead": "Read the story",
"picksEditorialLabel": "EDITOR'S PICKS",
"guidesTitle": "Buying guides",
"guidesSubtitle": "Deep dives that help you choose",
"guidesViewAll": "All guides",
"storiesTitle": "Latest stories",
"storiesViewAll": "All stories",
"communityTitle": "From the community",
"communityViewAll": "Browse directory",
"shipTitle": "Ship faster with AI",
"shipSubtitle": "Pick your workflow — we ranked the tools for it",
"upvotedTitle": "Most upvoted this week",
"upvotedViewAll": "View all",
"workflowsTitle": "Workflows & stacks",
"workflowsSubtitle": "Batteries-included tool stacks for common dev tasks",
"workflowsViewAll": "All workflows",
"browseCategoryTitle": "Browse by category",
"trendingNowTitle": "Trending now",
"browseButton": "Browse tools",
"toolsCompared": "{count} tools compared",
"popularSearches": [
"writing",
"video",
"coding",
"image"
]
},
"best": {
"indexTitle": "Best AI Tools — Curated Lists",
"indexSubtitle": "Hand-picked rankings, updated with community signal. Every list is labeled and never sold.",
"empty": "No collections published yet.",
"curated": "Curated list",
"faq": "Frequently asked questions",
"more": "More best lists",
"toolsCompared": "{count} tools"
},
"aiToolsFor": {
"indexTitle": "AI tools for what you do",
"indexSubtitle": "Skip the categories. Start from your task or your role — every page ranks tools by real community votes and reviews.",
"byTask": "By task",
"byAudience": "By audience",
"noData": "No use cases yet.",
"noAud": "No audiences yet.",
"noTools": "No tools tagged yet."
},
"submitPage": {
"title": "Submit your AI tool",
"subtitle": "Get a free listing with community votes, user reviews and analytics. Submissions are reviewed by our editors, usually within 48 hours."
},
"faq": {
"title": "Frequently asked questions"
},
"compare": {
"intro": "A side-by-side comparison based on community votes, ratings and reviews — not sponsored placement.",
"leaning": "Right now the community leans toward {name}.",
"label": "Compare",
"noReviews": "—",
"latest": "Latest review"
},
"alternatives": {
"noAlt": "No alternatives found yet.",
"thisTool": "{name} (this tool)",
"table": {
"tool": "Tool",
"rating": "Rating",
"votes": "Votes",
"pricing": "Pricing"
},
"zhTable": {
"tool": "Tool",
"rating": "Rating",
"votes": "Votes",
"pricing": "Pricing"
}
},
"notFound": {
"code": "404",
"title": "Page not found",
"body": "The page you are looking for doesn't exist or may have moved.",
"home": "Back to home",
"tools": "Browse tools"
},
"search": {
"placeholder": "Search 1,000+ AI tools...",
"button": "Search"
},
"siteFooter": {
"groups": [
{
"title": "Discover",
"links": [
{
"label": "All Tools",
"href": "/tools"
},
{
"label": "Use Cases",
"href": "/ai-tools-for"
},
{
"label": "Best Lists",
"href": "/best"
},
{
"label": "Rankings",
"href": "/rankings"
}
]
},
{
"title": "Community",
"links": [
{
"label": "Submit a Tool",
"href": "/submit"
},
{
"label": "Blog",
"href": "/blog"
},
{
"label": "Dashboard",
"href": "/dashboard"
}
]
},
{
"title": "Legal",
"links": [
{
"label": "Terms",
"href": "/terms"
},
{
"label": "Privacy",
"href": "/privacy"
},
{
"label": "Contact",
"href": "/contact"
}
]
}
]
},
"siteHeader": {
"searchPlaceholder": "Search AI tools..."
}
}
+302 -4
View File
@@ -53,12 +53,29 @@
"linkedin": "领英",
"github": "GitHub"
}
},
"site": {
"items": [
"发现",
"用例",
"最佳清单",
"排行榜",
"博客",
"提交工具"
],
"hrefs": [
"/tools",
"/ai-tools-for",
"/best",
"/rankings",
"/blog",
"/submit"
]
}
},
"hero": {
"badge": "发现最好的 AI 工具",
"title": "AI Stack — 由真实用户排名的 AI 工具目录",
"description": "专业 AI 工具导航:14 个分类、1000+ 工具,由社区投票、评论与互动数据实时排名。",
"title": "找到最好的 AI 工具,由真实用户排名",
"description": "14 个分类、1000+ AI 工具。对比投票、评论与定价,选出最适合你的。",
"cta": {
"primary": "探索工具",
"secondary": "阅读博客"
@@ -657,7 +674,9 @@
"latest": "最新",
"trending": "最热"
},
"clearFilters": "清除搜索与筛选"
"clearFilters": "清除搜索与筛选",
"useCasesTitle": "使用场景",
"goodForTitle": "适合人群"
},
"seo": {
"home": {
@@ -804,5 +823,284 @@
"new": "最新收录",
"editorsChoice": "编辑精选",
"empty": "该榜单暂无工具。"
},
"siteHome": {
"trending": {
"title": "本周趋势",
"subtitle": "按最近 7 天投票、评论与互动排名"
},
"categories": {
"title": "按分类浏览",
"viewAll": "全部工具"
},
"new": {
"title": "最新收录"
},
"editors": {
"title": "编辑精选"
},
"useCases": {
"title": "按用例找工具",
"subtitle": "你想完成什么?",
"viewAll": "全部用例"
},
"bestLists": {
"title": "最佳 AI 工具清单",
"subtitle": "编辑精选排名",
"viewAll": "全部清单"
},
"reviews": {
"title": "最新社区评论"
},
"cta": {
"title": "做了 AI 工具?",
"body": "提交到 {brand},让成千上万寻找工具的用户发现你。",
"button": "提交你的工具"
},
"popular": {
"label": "热门:"
},
"toolsCount": "{count} 个工具",
"feed": {
"title": "今日发布流",
"browse": "浏览全部产品"
},
"leaderboard": {
"title": "排行榜",
"full": "完整榜单"
},
"picks": {
"title": "编辑推荐"
},
"collections": {
"title": "合集"
},
"launch": {
"button": "发布你的产品"
},
"featured": {
"label": "头条文章",
"read": "阅读全文"
},
"picksEditorial": {
"label": "编辑推荐"
},
"guides": {
"title": "选购指南",
"subtitle": "帮你做决定的深度对比",
"viewAll": "全部指南"
},
"stories": {
"title": "最新文章",
"viewAll": "全部文章"
},
"community": {
"title": "来自社区",
"viewAll": "浏览目录"
},
"ship": {
"title": "用 AI 加速交付",
"subtitle": "选好工作流,我们为它排好了工具",
"viewAll": "全部用例"
},
"upvoted": {
"title": "本周最多投票",
"viewAll": "查看全部"
},
"workflows": {
"title": "工作流与技术栈",
"subtitle": "常见开发任务的完整工具组合",
"viewAll": "全部工作流"
},
"browseCategory": {
"title": "按分类浏览"
},
"trendingNow": {
"title": "当前趋势"
},
"seeRankings": "查看完整排行",
"browse": "浏览工具",
"noLaunches": "还没有发布——来做第一个。",
"searchPlaceholder": "搜索 AI 工具...",
"reviewsCount": "{count} 条评论",
"mastheadLabel": "独立 AI 报道",
"minimalTagline": "AI 工具搜索",
"minimalPlaceholder": "我需要制作营销视频……",
"useCasesBlockTitle": "使用场景",
"goodForBlockTitle": "适合人群",
"searchButton": "搜索",
"trendingTitle": "本周趋势",
"trendingSubtitle": "按最近 7 天投票、评论与互动排名",
"allRankings": "全部榜单",
"categoriesTitle": "按分类浏览",
"categoriesViewAll": "全部工具",
"newTitle": "最新收录",
"editorsTitle": "编辑精选",
"useCasesTitle": "按用例找工具",
"useCasesSubtitle": "你想完成什么?",
"useCasesViewAll": "全部用例",
"bestListsTitle": "最佳 AI 工具清单",
"bestListsSubtitle": "编辑精选排名",
"bestListsViewAll": "全部清单",
"reviewsTitle": "最新社区评论",
"ctaTitle": "做了 AI 工具?",
"ctaBody": "提交到 {brand},让成千上万寻找工具的用户发现你。",
"ctaButton": "提交你的工具",
"popularLabel": "热门:",
"feedTitle": "今日发布流",
"feedBrowse": "浏览全部产品",
"leaderboardTitle": "排行榜",
"leaderboardFull": "完整榜单",
"picksTitle": "编辑推荐",
"collectionsTitle": "合集",
"launchButton": "发布你的产品",
"featuredLabel": "头条文章",
"featuredRead": "阅读全文",
"picksEditorialLabel": "编辑推荐",
"guidesTitle": "选购指南",
"guidesSubtitle": "帮你做决定的深度对比",
"guidesViewAll": "全部指南",
"storiesTitle": "最新文章",
"storiesViewAll": "全部文章",
"communityTitle": "来自社区",
"communityViewAll": "浏览目录",
"shipTitle": "用 AI 加速交付",
"shipSubtitle": "选好工作流,我们为它排好了工具",
"upvotedTitle": "本周最多投票",
"upvotedViewAll": "查看全部",
"workflowsTitle": "工作流与技术栈",
"workflowsSubtitle": "常见开发任务的完整工具组合",
"workflowsViewAll": "全部工作流",
"browseCategoryTitle": "按分类浏览",
"trendingNowTitle": "当前趋势",
"browseButton": "浏览工具",
"toolsCompared": "{count} 个工具",
"popularSearches": [
"写作",
"视频",
"编程",
"图像"
]
},
"best": {
"indexTitle": "最佳 AI 工具——精选清单",
"indexSubtitle": "人工精选排名,持续吸收社区信号。每个清单都明确标注,永不售卖。",
"empty": "暂无合集。",
"curated": "编辑精选",
"faq": "常见问题",
"more": "更多最佳清单",
"toolsCompared": "{count} 个工具"
},
"aiToolsFor": {
"indexTitle": "按任务找 AI 工具",
"indexSubtitle": "别再翻分类。从你的任务或角色出发——每页都按真实社区投票与评论排名。",
"byTask": "按任务",
"byAudience": "按人群",
"noData": "暂无用例。",
"noAud": "暂无人群。",
"noTools": "暂无关联工具。"
},
"submitPage": {
"title": "提交你的 AI 工具",
"subtitle": "免费收录,获得社区投票、用户评论与数据分析。编辑通常在 48 小时内完成审核。"
},
"faq": {
"title": "常见问题"
},
"compare": {
"intro": "基于社区投票、评分与评论的并排对比——非赞助排名。",
"leaning": "当前社区更倾向 {name}。",
"label": "对比",
"noReviews": "—",
"latest": "最新评论"
},
"alternatives": {
"noAlt": "暂无替代品。",
"thisTool": "{name}(本工具)",
"table": {
"tool": "工具",
"rating": "评分",
"votes": "票数",
"pricing": "定价"
},
"zhTable": {
"tool": "工具",
"rating": "评分",
"votes": "票数",
"pricing": "定价"
}
},
"notFound": {
"code": "404",
"title": "页面不存在",
"body": "你访问的页面不存在或已被移动。",
"home": "返回首页",
"tools": "浏览工具"
},
"search": {
"placeholder": "搜索 1000+ AI 工具...",
"button": "搜索"
},
"siteFooter": {
"groups": [
{
"title": "发现",
"links": [
{
"label": "All Tools",
"href": "/tools"
},
{
"label": "Use Cases",
"href": "/ai-tools-for"
},
{
"label": "Best Lists",
"href": "/best"
},
{
"label": "Rankings",
"href": "/rankings"
}
]
},
{
"title": "社区",
"links": [
{
"label": "Submit a Tool",
"href": "/submit"
},
{
"label": "Blog",
"href": "/blog"
},
{
"label": "Dashboard",
"href": "/dashboard"
}
]
},
{
"title": "法律",
"links": [
{
"label": "Terms",
"href": "/terms"
},
{
"label": "Privacy",
"href": "/privacy"
},
{
"label": "Contact",
"href": "/contact"
}
]
}
]
},
"siteHeader": {
"searchPlaceholder": "搜索 1000+ AI 工具..."
}
}
@@ -0,0 +1,2 @@
// 自动生成:[locale]/not-found.tsx 的薄路由
export { default } from "@aidir/core/pages/[locale]/not-found";
+15 -3
View File
@@ -6,13 +6,25 @@ import { locales, defaultLocale, type Locale } from "@aidir/core/i18n.config";
const brandMessages = {
en: {
seo: { home: {"title":"AI Stack — Find the Best AI Tools, Ranked by Real Users","description":"The professional AI tools directory. 1,000+ AI tools across 14 categories ranked by community votes, reviews and engagement.","ogImage":"/og-image.png"} },
hero: { title: "AI Stack — Find the Best AI Tools, Ranked by Real Users", description: "The professional AI tools directory. 1,000+ AI tools across 14 categories ranked by community votes, reviews and engagement." },
hero: { title: "Find the best AI tools, ranked by real users", description: "1,000+ AI tools across 14 categories. Compare votes, reviews and pricing to pick the right one." },
common: { brand: { name: "AI Stack", tagline: "The professional AI tools directory — ranked by the community." } },
navigation: {
main: { tools: "Discover", blog: "Blog" },
site: { items: ["Discover","Use Cases","Best Lists","Rankings","Blog","Submit"], hrefs: ["/tools","/ai-tools-for","/best","/rankings","/blog","/submit"] },
},
siteFooter: { groups: [{"title":"Discover","links":[{"label":"All Tools","href":"/tools"},{"label":"Use Cases","href":"/ai-tools-for"},{"label":"Best Lists","href":"/best"},{"label":"Rankings","href":"/rankings"}]},{"title":"Community","links":[{"label":"Submit a Tool","href":"/submit"},{"label":"Blog","href":"/blog"},{"label":"Dashboard","href":"/dashboard"}]},{"title":"Legal","links":[{"label":"Terms","href":"/terms"},{"label":"Privacy","href":"/privacy"},{"label":"Contact","href":"/contact"}]}] },
siteHeader: { searchPlaceholder: "Search AI tools..." },
},
zh: {
seo: { home: {"title":"AI Stack — 由真实用户排名的 AI 工具目录","description":"专业 AI 工具导航:14 个分类、1000+ 工具,由社区投票、评论与互动数据实时排名。","ogImage":"/og-image.png"} },
hero: { title: "AI Stack — 由真实用户排名的 AI 工具目录", description: "专业 AI 工具导航:14 个分类、1000+ 工具,由社区投票、评论与互动数据实时排名。" },
common: { brand: { name: "AI Stack", tagline: "The professional AI tools directory — ranked by the community." } },
hero: { title: "找到最好的 AI 工具,由真实用户排名", description: "14 个分类、1000+ AI 工具。对比投票、评论与定价,选出最适合你的。" },
common: { brand: { name: "AI Stack", tagline: "由真实用户排名的专业 AI 工具目录。" } },
navigation: {
main: { tools: "发现", blog: "博客" },
site: { items: ["发现","用例","最佳清单","排行榜","博客","提交工具"], hrefs: ["/tools","/ai-tools-for","/best","/rankings","/blog","/submit"] },
},
siteFooter: { groups: [{"title":"发现","links":[{"label":"All Tools","href":"/tools"},{"label":"Use Cases","href":"/ai-tools-for"},{"label":"Best Lists","href":"/best"},{"label":"Rankings","href":"/rankings"}]},{"title":"社区","links":[{"label":"Submit a Tool","href":"/submit"},{"label":"Blog","href":"/blog"},{"label":"Dashboard","href":"/dashboard"}]},{"title":"法律","links":[{"label":"Terms","href":"/terms"},{"label":"Privacy","href":"/privacy"},{"label":"Contact","href":"/contact"}]}] },
siteHeader: { searchPlaceholder: "搜索 1000+ AI 工具..." },
},
} as const;
+20
View File
@@ -2,6 +2,26 @@ import type { SiteConfig } from "@/kit/types";
// AI Stack —— 独立站点的全部品牌/导航/页脚配置
export const siteConfig: SiteConfig = {
"brandZh": {
"name": "AI Stack",
"tagline": "由真实用户排名的专业 AI 工具目录。",
"headline": "找到最好的 AI 工具,由真实用户排名",
"subheadline": "14 个分类、1000+ AI 工具。对比投票、评论与定价,选出最适合你的。",
"nav": [
"发现",
"用例",
"最佳清单",
"排行榜",
"博客",
"提交工具"
],
"footer": [
"发现",
"社区",
"法律"
],
"searchPlaceholder": "搜索 1000+ AI 工具..."
},
"accent": "222 47% 11%",
"port": 3001,
"brand": {
+23 -20
View File
@@ -1,6 +1,7 @@
// 自动生成:站点首页组件(源自底座 classic 形态,品牌来自 @/site/brand)
import { siteConfig } from "@/site/brand";
import { Search, TrendingUp, Sparkles, Clock, Award, LayoutGrid, MessageSquareQuote } from "lucide-react";
import { getTranslations } from "next-intl/server";
import { ToolCard, ToolCardGrid } from "@/components/tool-card";
import { LocaleLink } from "@/components/locale-link";
import { SectionHeader } from "@/kit/section";
@@ -50,6 +51,8 @@ const footerGroups = [
];
async function SiteHome({ locale }: { locale: string }) {
const t = await getTranslations("siteHome");
const th = await getTranslations("hero");
const data = await getHomeDataSafe(8);
return (
@@ -58,17 +61,17 @@ async function SiteHome({ locale }: { locale: string }) {
<section className="border-b border-border bg-gradient-to-b from-secondary/40 to-background">
<div className="mx-auto max-w-7xl px-4 py-16 text-center sm:px-6 md:py-24">
<h1 className="mx-auto max-w-3xl text-3xl font-bold tracking-tight text-foreground sm:text-4xl md:text-5xl">
{siteConfig.brand.headline}
{th("title")}
</h1>
<p className="mx-auto mt-4 max-w-2xl text-sm text-muted-foreground sm:text-base">
{siteConfig.brand.subheadline}
{th("description")}
</p>
<form action={`/${locale}/tools`} className="mx-auto mt-8 max-w-xl">
<div className="relative">
<Search className="absolute left-4 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
<input
name="search"
placeholder="Search 1,000+ AI tools..."
placeholder={t("searchPlaceholder")}
className="h-12 w-full rounded-full border border-border bg-background pl-11 pr-28 text-sm text-foreground shadow-sm placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/30"
aria-label="Search AI tools"
/>
@@ -76,13 +79,13 @@ async function SiteHome({ locale }: { locale: string }) {
type="submit"
className="absolute right-1.5 top-1.5 h-9 rounded-full bg-primary px-5 text-xs font-semibold text-primary-foreground transition-opacity hover:opacity-90"
>
Search
{t("searchButton")}
</button>
</div>
</form>
<div className="mt-4 flex flex-wrap items-center justify-center gap-2 text-xs text-muted-foreground">
<span>Popular:</span>
{["writing", "video", "coding", "image"].map((q) => (
<span>{t("popularLabel")}</span>
{t.raw("popularSearches").map((q: string) => (
<LocaleLink
key={q}
href={`/tools?search=${q}`}
@@ -100,10 +103,10 @@ async function SiteHome({ locale }: { locale: string }) {
{data.trending.length > 0 && (
<section>
<SectionHeader
title="Trending this week"
subtitle="Ranked by votes, reviews and engagement over the last 7 days"
title={t("trendingTitle")}
subtitle={t("trendingSubtitle")}
viewAllHref="/rankings?board=trending"
viewAllLabel="All rankings"
viewAllLabel={t("allRankings")}
/>
<ToolCardGrid tools={data.trending} locale={locale} ranked />
</section>
@@ -113,9 +116,9 @@ async function SiteHome({ locale }: { locale: string }) {
{data.categories.length > 0 && (
<section>
<SectionHeader
title="Browse by category"
title={t("categoriesTitle")}
viewAllHref="/tools"
viewAllLabel="All tools"
viewAllLabel={t("categoriesViewAll")}
/>
<div className="grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-5">
{data.categories.slice(0, 10).map((category) => (
@@ -144,7 +147,7 @@ async function SiteHome({ locale }: { locale: string }) {
{data.newest.length > 0 && (
<section>
<SectionHeader
title="New AI tools"
title={t("newTitle")}
viewAllHref="/rankings?board=new"
/>
<ToolCardGrid tools={data.newest.slice(0, 4)} locale={locale} />
@@ -153,7 +156,7 @@ async function SiteHome({ locale }: { locale: string }) {
{data.editorsChoice.length > 0 && (
<section>
<SectionHeader
title="Editor's Choice"
title={t("editorsTitle")}
viewAllHref="/rankings?board=editors-choice"
/>
<ToolCardGrid tools={data.editorsChoice.slice(0, 4)} locale={locale} />
@@ -165,10 +168,10 @@ async function SiteHome({ locale }: { locale: string }) {
{data.useCases.length > 0 && (
<section>
<SectionHeader
title="Tools by use case"
subtitle="What do you want to get done?"
title={t("useCasesTitle")}
subtitle={t("useCasesSubtitle")}
viewAllHref="/ai-tools-for"
viewAllLabel="All use cases"
viewAllLabel={t("useCasesViewAll")}
/>
<div className="flex flex-wrap gap-2">
{data.useCases.slice(0, 12).map((uc) => (
@@ -191,10 +194,10 @@ async function SiteHome({ locale }: { locale: string }) {
{data.collections.length > 0 && (
<section>
<SectionHeader
title="Best AI tool lists"
subtitle="Curated rankings by our editors"
title={t("bestListsTitle")}
subtitle={t("bestListsSubtitle")}
viewAllHref="/best"
viewAllLabel="All lists"
viewAllLabel={t("bestListsViewAll")}
/>
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
{data.collections.slice(0, 3).map((col) => (
@@ -222,7 +225,7 @@ async function SiteHome({ locale }: { locale: string }) {
{/* Latest Reviews */}
{data.latestReviews.length > 0 && (
<section>
<SectionHeader title="Latest community reviews" />
<SectionHeader title={t("reviewsTitle")} />
<div className="grid gap-3 md:grid-cols-2">
{data.latestReviews.map((review, i) => (
<LocaleLink
+292 -3
View File
@@ -53,12 +53,28 @@
"linkedin": "LinkedIn",
"github": "GitHub"
}
},
"site": {
"items": [
"Tools",
"Workflows",
"Use Cases",
"Rankings",
"Submit"
],
"hrefs": [
"/tools",
"/best",
"/ai-tools-for",
"/rankings",
"/submit"
]
}
},
"hero": {
"badge": "Discover the Best AI Tools",
"title": "DevAI.tools — AI Tools for Developers",
"description": "Code assistants, DevOps automation and testing copilots — the AI tools developers actually upvote, ranked daily.",
"title": "AI tools for developers",
"description": "Code assistants, DevOps automation, testing copilots and everything else you ship with.",
"cta": {
"primary": "Explore Tools",
"secondary": "Read Blog"
@@ -649,7 +665,9 @@
"latest": "Latest",
"trending": "Trending"
},
"clearFilters": "Clear search & filters"
"clearFilters": "Clear search & filters",
"useCasesTitle": "Use cases",
"goodForTitle": "Good for"
},
"seo": {
"home": {
@@ -796,5 +814,276 @@
"new": "New",
"editorsChoice": "Editor's Choice",
"empty": "No tools on this board yet."
},
"siteHome": {
"trending": {
"title": "Trending this week",
"subtitle": "Ranked by votes, reviews and engagement over the last 7 days"
},
"categories": {
"title": "Browse by category",
"viewAll": "All tools"
},
"new": {
"title": "New AI tools"
},
"editors": {
"title": "Editor's Choice"
},
"useCases": {
"title": "Tools by use case",
"subtitle": "What do you want to get done?",
"viewAll": "All use cases"
},
"bestLists": {
"title": "Best AI tool lists",
"subtitle": "Curated rankings by our editors",
"viewAll": "All lists"
},
"reviews": {
"title": "Latest community reviews"
},
"cta": {
"title": "Built an AI tool?",
"body": "Submit it to {brand} and get discovered by thousands of users looking for tools like yours.",
"button": "Submit your tool"
},
"popular": {
"label": "Popular:"
},
"toolsCount": "{count} tools",
"feed": {
"title": "Today's feed",
"browse": "Browse all products"
},
"leaderboard": {
"title": "Leaderboard",
"full": "Full leaderboard"
},
"picks": {
"title": "Editor's picks"
},
"collections": {
"title": "Collections"
},
"launch": {
"button": "Launch your product"
},
"featured": {
"label": "Featured story",
"read": "Read the story"
},
"picksEditorial": {
"label": "Editor's picks"
},
"guides": {
"title": "Buying guides",
"subtitle": "Deep dives that help you choose",
"viewAll": "All guides"
},
"stories": {
"title": "Latest stories",
"viewAll": "All stories"
},
"community": {
"title": "From the community",
"viewAll": "Browse directory"
},
"ship": {
"title": "Ship faster with AI",
"subtitle": "Pick your workflow — we ranked the tools for it",
"viewAll": "All use cases"
},
"upvoted": {
"title": "Most upvoted this week",
"viewAll": "View all"
},
"workflows": {
"title": "Workflows & stacks",
"subtitle": "Batteries-included tool stacks for common dev tasks",
"viewAll": "All workflows"
},
"browseCategory": {
"title": "Browse by category"
},
"trendingNow": {
"title": "Trending now"
},
"seeRankings": "See full rankings",
"browse": "Browse tools",
"noLaunches": "No launches yet — be the first to submit.",
"searchPlaceholder": "Search AI tools...",
"reviewsCount": "{count} reviews",
"mastheadLabel": "INDEPENDENT AI COVERAGE",
"minimalTagline": "AI TOOL SEARCH",
"minimalPlaceholder": "I need to create marketing videos...",
"useCasesBlockTitle": "Use cases",
"goodForBlockTitle": "Good for",
"searchButton": "Search",
"trendingTitle": "Trending this week",
"trendingSubtitle": "Ranked by votes, reviews and engagement over the last 7 days",
"allRankings": "All rankings",
"categoriesTitle": "Browse by category",
"categoriesViewAll": "All tools",
"newTitle": "New AI tools",
"editorsTitle": "Editor's Choice",
"useCasesTitle": "Tools by use case",
"useCasesSubtitle": "What do you want to get done?",
"useCasesViewAll": "All use cases",
"bestListsTitle": "Best AI tool lists",
"bestListsSubtitle": "Curated rankings by our editors",
"bestListsViewAll": "All lists",
"reviewsTitle": "Latest community reviews",
"ctaTitle": "Built an AI tool?",
"ctaBody": "Submit it to {brand} and get discovered by thousands of users looking for tools like yours.",
"ctaButton": "Submit your tool",
"popularLabel": "Popular:",
"feedTitle": "Today's feed",
"feedBrowse": "Browse all products",
"leaderboardTitle": "Leaderboard",
"leaderboardFull": "Full leaderboard",
"picksTitle": "Editor's picks",
"collectionsTitle": "Collections",
"launchButton": "Launch your product",
"featuredLabel": "Featured story",
"featuredRead": "Read the story",
"picksEditorialLabel": "EDITOR'S PICKS",
"guidesTitle": "Buying guides",
"guidesSubtitle": "Deep dives that help you choose",
"guidesViewAll": "All guides",
"storiesTitle": "Latest stories",
"storiesViewAll": "All stories",
"communityTitle": "From the community",
"communityViewAll": "Browse directory",
"shipTitle": "Ship faster with AI",
"shipSubtitle": "Pick your workflow — we ranked the tools for it",
"upvotedTitle": "Most upvoted this week",
"upvotedViewAll": "View all",
"workflowsTitle": "Workflows & stacks",
"workflowsSubtitle": "Batteries-included tool stacks for common dev tasks",
"workflowsViewAll": "All workflows",
"browseCategoryTitle": "Browse by category",
"trendingNowTitle": "Trending now",
"browseButton": "Browse tools",
"toolsCompared": "{count} tools compared",
"popularSearches": [
"writing",
"video",
"coding",
"image"
]
},
"best": {
"indexTitle": "Best AI Tools — Curated Lists",
"indexSubtitle": "Hand-picked rankings, updated with community signal. Every list is labeled and never sold.",
"empty": "No collections published yet.",
"curated": "Curated list",
"faq": "Frequently asked questions",
"more": "More best lists",
"toolsCompared": "{count} tools"
},
"aiToolsFor": {
"indexTitle": "AI tools for what you do",
"indexSubtitle": "Skip the categories. Start from your task or your role — every page ranks tools by real community votes and reviews.",
"byTask": "By task",
"byAudience": "By audience",
"noData": "No use cases yet.",
"noAud": "No audiences yet.",
"noTools": "No tools tagged yet."
},
"submitPage": {
"title": "Submit your AI tool",
"subtitle": "Get a free listing with community votes, user reviews and analytics. Submissions are reviewed by our editors, usually within 48 hours."
},
"faq": {
"title": "Frequently asked questions"
},
"compare": {
"intro": "A side-by-side comparison based on community votes, ratings and reviews — not sponsored placement.",
"leaning": "Right now the community leans toward {name}.",
"label": "Compare",
"noReviews": "—",
"latest": "Latest review"
},
"alternatives": {
"noAlt": "No alternatives found yet.",
"thisTool": "{name} (this tool)",
"table": {
"tool": "Tool",
"rating": "Rating",
"votes": "Votes",
"pricing": "Pricing"
},
"zhTable": {
"tool": "Tool",
"rating": "Rating",
"votes": "Votes",
"pricing": "Pricing"
}
},
"notFound": {
"code": "404",
"title": "Page not found",
"body": "The page you are looking for doesn't exist or may have moved.",
"home": "Back to home",
"tools": "Browse tools"
},
"search": {
"placeholder": "Search 1,000+ AI tools...",
"button": "Search"
},
"siteFooter": {
"groups": [
{
"title": "Directory",
"links": [
{
"label": "All Tools",
"href": "/tools"
},
{
"label": "Rankings",
"href": "/rankings"
},
{
"label": "Use Cases",
"href": "/ai-tools-for"
}
]
},
{
"title": "Build",
"links": [
{
"label": "Workflows",
"href": "/best"
},
{
"label": "Submit a Tool",
"href": "/submit"
},
{
"label": "Blog",
"href": "/blog"
}
]
},
{
"title": "Legal",
"links": [
{
"label": "Terms",
"href": "/terms"
},
{
"label": "Privacy",
"href": "/privacy"
}
]
}
]
},
"siteHeader": {
"searchPlaceholder": "Search dev tools..."
}
}
+292 -4
View File
@@ -53,12 +53,27 @@
"linkedin": "领英",
"github": "GitHub"
}
},
"site": {
"items": [
"工具",
"工作流",
"用例",
"排行榜",
"提交"
],
"hrefs": [
"/tools",
"/best",
"/ai-tools-for",
"/rankings",
"/submit"
]
}
},
"hero": {
"badge": "发现最好的 AI 工具",
"title": "DevAI.tools — 开发者 AI 工具",
"description": "面向开发者的 AI 工具目录:代码助手、运维自动化、测试副驾驶,由社区每日排名。",
"title": "开发者的 AI 工具",
"description": "代码助手、运维自动化、测试副驾驶,你上手的一切。",
"cta": {
"primary": "探索工具",
"secondary": "阅读博客"
@@ -657,7 +672,9 @@
"latest": "最新",
"trending": "最热"
},
"clearFilters": "清除搜索与筛选"
"clearFilters": "清除搜索与筛选",
"useCasesTitle": "使用场景",
"goodForTitle": "适合人群"
},
"seo": {
"home": {
@@ -804,5 +821,276 @@
"new": "最新收录",
"editorsChoice": "编辑精选",
"empty": "该榜单暂无工具。"
},
"siteHome": {
"trending": {
"title": "本周趋势",
"subtitle": "按最近 7 天投票、评论与互动排名"
},
"categories": {
"title": "按分类浏览",
"viewAll": "全部工具"
},
"new": {
"title": "最新收录"
},
"editors": {
"title": "编辑精选"
},
"useCases": {
"title": "按用例找工具",
"subtitle": "你想完成什么?",
"viewAll": "全部用例"
},
"bestLists": {
"title": "最佳 AI 工具清单",
"subtitle": "编辑精选排名",
"viewAll": "全部清单"
},
"reviews": {
"title": "最新社区评论"
},
"cta": {
"title": "做了 AI 工具?",
"body": "提交到 {brand},让成千上万寻找工具的用户发现你。",
"button": "提交你的工具"
},
"popular": {
"label": "热门:"
},
"toolsCount": "{count} 个工具",
"feed": {
"title": "今日发布流",
"browse": "浏览全部产品"
},
"leaderboard": {
"title": "排行榜",
"full": "完整榜单"
},
"picks": {
"title": "编辑推荐"
},
"collections": {
"title": "合集"
},
"launch": {
"button": "发布你的产品"
},
"featured": {
"label": "头条文章",
"read": "阅读全文"
},
"picksEditorial": {
"label": "编辑推荐"
},
"guides": {
"title": "选购指南",
"subtitle": "帮你做决定的深度对比",
"viewAll": "全部指南"
},
"stories": {
"title": "最新文章",
"viewAll": "全部文章"
},
"community": {
"title": "来自社区",
"viewAll": "浏览目录"
},
"ship": {
"title": "用 AI 加速交付",
"subtitle": "选好工作流,我们为它排好了工具",
"viewAll": "全部用例"
},
"upvoted": {
"title": "本周最多投票",
"viewAll": "查看全部"
},
"workflows": {
"title": "工作流与技术栈",
"subtitle": "常见开发任务的完整工具组合",
"viewAll": "全部工作流"
},
"browseCategory": {
"title": "按分类浏览"
},
"trendingNow": {
"title": "当前趋势"
},
"seeRankings": "查看完整排行",
"browse": "浏览工具",
"noLaunches": "还没有发布——来做第一个。",
"searchPlaceholder": "搜索 AI 工具...",
"reviewsCount": "{count} 条评论",
"mastheadLabel": "独立 AI 报道",
"minimalTagline": "AI 工具搜索",
"minimalPlaceholder": "我需要制作营销视频……",
"useCasesBlockTitle": "使用场景",
"goodForBlockTitle": "适合人群",
"searchButton": "搜索",
"trendingTitle": "本周趋势",
"trendingSubtitle": "按最近 7 天投票、评论与互动排名",
"allRankings": "全部榜单",
"categoriesTitle": "按分类浏览",
"categoriesViewAll": "全部工具",
"newTitle": "最新收录",
"editorsTitle": "编辑精选",
"useCasesTitle": "按用例找工具",
"useCasesSubtitle": "你想完成什么?",
"useCasesViewAll": "全部用例",
"bestListsTitle": "最佳 AI 工具清单",
"bestListsSubtitle": "编辑精选排名",
"bestListsViewAll": "全部清单",
"reviewsTitle": "最新社区评论",
"ctaTitle": "做了 AI 工具?",
"ctaBody": "提交到 {brand},让成千上万寻找工具的用户发现你。",
"ctaButton": "提交你的工具",
"popularLabel": "热门:",
"feedTitle": "今日发布流",
"feedBrowse": "浏览全部产品",
"leaderboardTitle": "排行榜",
"leaderboardFull": "完整榜单",
"picksTitle": "编辑推荐",
"collectionsTitle": "合集",
"launchButton": "发布你的产品",
"featuredLabel": "头条文章",
"featuredRead": "阅读全文",
"picksEditorialLabel": "编辑推荐",
"guidesTitle": "选购指南",
"guidesSubtitle": "帮你做决定的深度对比",
"guidesViewAll": "全部指南",
"storiesTitle": "最新文章",
"storiesViewAll": "全部文章",
"communityTitle": "来自社区",
"communityViewAll": "浏览目录",
"shipTitle": "用 AI 加速交付",
"shipSubtitle": "选好工作流,我们为它排好了工具",
"upvotedTitle": "本周最多投票",
"upvotedViewAll": "查看全部",
"workflowsTitle": "工作流与技术栈",
"workflowsSubtitle": "常见开发任务的完整工具组合",
"workflowsViewAll": "全部工作流",
"browseCategoryTitle": "按分类浏览",
"trendingNowTitle": "当前趋势",
"browseButton": "浏览工具",
"toolsCompared": "{count} 个工具",
"popularSearches": [
"写作",
"视频",
"编程",
"图像"
]
},
"best": {
"indexTitle": "最佳 AI 工具——精选清单",
"indexSubtitle": "人工精选排名,持续吸收社区信号。每个清单都明确标注,永不售卖。",
"empty": "暂无合集。",
"curated": "编辑精选",
"faq": "常见问题",
"more": "更多最佳清单",
"toolsCompared": "{count} 个工具"
},
"aiToolsFor": {
"indexTitle": "按任务找 AI 工具",
"indexSubtitle": "别再翻分类。从你的任务或角色出发——每页都按真实社区投票与评论排名。",
"byTask": "按任务",
"byAudience": "按人群",
"noData": "暂无用例。",
"noAud": "暂无人群。",
"noTools": "暂无关联工具。"
},
"submitPage": {
"title": "提交你的 AI 工具",
"subtitle": "免费收录,获得社区投票、用户评论与数据分析。编辑通常在 48 小时内完成审核。"
},
"faq": {
"title": "常见问题"
},
"compare": {
"intro": "基于社区投票、评分与评论的并排对比——非赞助排名。",
"leaning": "当前社区更倾向 {name}。",
"label": "对比",
"noReviews": "—",
"latest": "最新评论"
},
"alternatives": {
"noAlt": "暂无替代品。",
"thisTool": "{name}(本工具)",
"table": {
"tool": "工具",
"rating": "评分",
"votes": "票数",
"pricing": "定价"
},
"zhTable": {
"tool": "工具",
"rating": "评分",
"votes": "票数",
"pricing": "定价"
}
},
"notFound": {
"code": "404",
"title": "页面不存在",
"body": "你访问的页面不存在或已被移动。",
"home": "返回首页",
"tools": "浏览工具"
},
"search": {
"placeholder": "搜索 1000+ AI 工具...",
"button": "搜索"
},
"siteFooter": {
"groups": [
{
"title": "目录",
"links": [
{
"label": "All Tools",
"href": "/tools"
},
{
"label": "Rankings",
"href": "/rankings"
},
{
"label": "Use Cases",
"href": "/ai-tools-for"
}
]
},
{
"title": "构建",
"links": [
{
"label": "Workflows",
"href": "/best"
},
{
"label": "Submit a Tool",
"href": "/submit"
},
{
"label": "Blog",
"href": "/blog"
}
]
},
{
"title": "法律",
"links": [
{
"label": "Terms",
"href": "/terms"
},
{
"label": "Privacy",
"href": "/privacy"
}
]
}
]
},
"siteHeader": {
"searchPlaceholder": "搜索开发者工具..."
}
}
@@ -0,0 +1,2 @@
// 自动生成:[locale]/not-found.tsx 的薄路由
export { default } from "@aidir/core/pages/[locale]/not-found";
+15 -3
View File
@@ -6,13 +6,25 @@ import { locales, defaultLocale, type Locale } from "@aidir/core/i18n.config";
const brandMessages = {
en: {
seo: { home: {"title":"DevAI.tools — AI Tools for Developers","description":"Code assistants, DevOps automation and testing copilots — the AI tools developers actually upvote, ranked daily.","ogImage":"/og-image.png"} },
hero: { title: "DevAI.tools — AI Tools for Developers", description: "Code assistants, DevOps automation and testing copilots — the AI tools developers actually upvote, ranked daily." },
hero: { title: "AI tools for developers", description: "Code assistants, DevOps automation, testing copilots and everything else you ship with." },
common: { brand: { name: "DevAI.tools", tagline: "AI tools for developers — curated, tested, ranked by the community." } },
navigation: {
main: { tools: "Tools", blog: "Blog" },
site: { items: ["Tools","Workflows","Use Cases","Rankings","Submit"], hrefs: ["/tools","/best","/ai-tools-for","/rankings","/submit"] },
},
siteFooter: { groups: [{"title":"Directory","links":[{"label":"All Tools","href":"/tools"},{"label":"Rankings","href":"/rankings"},{"label":"Use Cases","href":"/ai-tools-for"}]},{"title":"Build","links":[{"label":"Workflows","href":"/best"},{"label":"Submit a Tool","href":"/submit"},{"label":"Blog","href":"/blog"}]},{"title":"Legal","links":[{"label":"Terms","href":"/terms"},{"label":"Privacy","href":"/privacy"}]}] },
siteHeader: { searchPlaceholder: "Search dev tools..." },
},
zh: {
seo: { home: {"title":"DevAI.tools — 开发者 AI 工具","description":"面向开发者的 AI 工具目录:代码助手、运维自动化、测试副驾驶,由社区每日排名。","ogImage":"/og-image.png"} },
hero: { title: "DevAI.tools — 开发者 AI 工具", description: "面向开发者的 AI 工具目录:代码助手、运维自动化、测试副驾驶,由社区每日排名。" },
common: { brand: { name: "DevAI.tools", tagline: "AI tools for developers — curated, tested, ranked by the community." } },
hero: { title: "开发者的 AI 工具", description: "代码助手、运维自动化、测试副驾驶,你上手的一切。" },
common: { brand: { name: "DevAI.tools", tagline: "面向开发者的 AI 工具——精选、实测、社区排名。" } },
navigation: {
main: { tools: "工具", blog: "博客" },
site: { items: ["工具","工作流","用例","排行榜","提交"], hrefs: ["/tools","/best","/ai-tools-for","/rankings","/submit"] },
},
siteFooter: { groups: [{"title":"目录","links":[{"label":"All Tools","href":"/tools"},{"label":"Rankings","href":"/rankings"},{"label":"Use Cases","href":"/ai-tools-for"}]},{"title":"构建","links":[{"label":"Workflows","href":"/best"},{"label":"Submit a Tool","href":"/submit"},{"label":"Blog","href":"/blog"}]},{"title":"法律","links":[{"label":"Terms","href":"/terms"},{"label":"Privacy","href":"/privacy"}]}] },
siteHeader: { searchPlaceholder: "搜索开发者工具..." },
},
} as const;
+19
View File
@@ -2,6 +2,25 @@ import type { SiteConfig } from "@/kit/types";
// DevAI.tools —— 独立站点的全部品牌/导航/页脚配置
export const siteConfig: SiteConfig = {
"brandZh": {
"name": "DevAI.tools",
"tagline": "面向开发者的 AI 工具——精选、实测、社区排名。",
"headline": "开发者的 AI 工具",
"subheadline": "代码助手、运维自动化、测试副驾驶,你上手的一切。",
"nav": [
"工具",
"工作流",
"用例",
"排行榜",
"提交"
],
"footer": [
"目录",
"构建",
"法律"
],
"searchPlaceholder": "搜索开发者工具..."
},
"accent": "150 60% 30%",
"port": 3004,
"brand": {
+13 -10
View File
@@ -1,6 +1,7 @@
// 自动生成:站点首页组件(源自底座 niche 形态,品牌来自 @/site/brand)
import { siteConfig } from "@/site/brand";
import { Code2, Terminal, Workflow, Layers } from "lucide-react";
import { getTranslations } from "next-intl/server";
import { ToolCard } from "@/components/tool-card";
import { LocaleLink } from "@/components/locale-link";
import { SectionHeader } from "@/kit/section";
@@ -44,6 +45,8 @@ const footerGroups = [
];
async function SiteHome({ locale }: { locale: string }) {
const t = await getTranslations("siteHome");
const th = await getTranslations("hero");
const data = await getHomeDataSafe(6);
return (
@@ -65,10 +68,10 @@ async function SiteHome({ locale }: { locale: string }) {
</p>
<p className="flex items-center gap-2 text-foreground">
<Terminal className="h-3.5 w-3.5 text-emerald-600 dark:text-emerald-400" />
{siteConfig.brand.headline}
{th("title")}
</p>
<p className="text-xs leading-relaxed text-muted-foreground">
{siteConfig.brand.subheadline}
{th("description")}
</p>
<p className="pt-2">
<LocaleLink
@@ -88,10 +91,10 @@ async function SiteHome({ locale }: { locale: string }) {
{data.useCases.length > 0 && (
<section>
<SectionHeader
title="Ship faster with AI"
subtitle="Pick your workflow — we ranked the tools for it"
title={t("shipTitle")}
subtitle={t("shipSubtitle")}
viewAllHref="/ai-tools-for"
viewAllLabel="All use cases"
viewAllLabel={t("useCasesViewAll")}
/>
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
{data.useCases.slice(0, 6).map((uc) => (
@@ -119,7 +122,7 @@ async function SiteHome({ locale }: { locale: string }) {
{data.trending.length > 0 && (
<section>
<SectionHeader
title="Most upvoted this week"
title={t("upvotedTitle")}
viewAllHref="/rankings?board=trending"
/>
<div className="grid gap-3 md:grid-cols-2">
@@ -139,10 +142,10 @@ async function SiteHome({ locale }: { locale: string }) {
{data.collections.length > 0 && (
<section>
<SectionHeader
title="Workflows & stacks"
subtitle="Batteries-included tool stacks for common dev tasks"
title={t("workflowsTitle")}
subtitle={t("workflowsSubtitle")}
viewAllHref="/best"
viewAllLabel="All workflows"
viewAllLabel={t("workflowsViewAll")}
/>
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
{data.collections.slice(0, 3).map((col) => (
@@ -167,7 +170,7 @@ async function SiteHome({ locale }: { locale: string }) {
{/* Categories */}
{data.categories.length > 0 && (
<section>
<SectionHeader title="Browse by category" viewAllHref="/tools" />
<SectionHeader title={t("categoriesTitle")} viewAllHref="/tools" />
<div className="flex flex-wrap gap-2">
{data.categories.slice(0, 14).map((cat) => (
<LocaleLink
+276 -2
View File
@@ -53,11 +53,25 @@
"linkedin": "LinkedIn",
"github": "GitHub"
}
},
"site": {
"items": [
"Search",
"Use Cases",
"Rankings",
"Submit"
],
"hrefs": [
"/tools",
"/ai-tools-for",
"/rankings",
"/submit"
]
}
},
"hero": {
"badge": "Discover the Best AI Tools",
"title": "findai — The Minimal AI Tool Search Engine",
"title": "What are you trying to do?",
"description": "One search box, every AI tool. Describe what you're trying to do and find the right AI tool, ranked by the community.",
"cta": {
"primary": "Explore Tools",
@@ -649,7 +663,9 @@
"latest": "Latest",
"trending": "Trending"
},
"clearFilters": "Clear search & filters"
"clearFilters": "Clear search & filters",
"useCasesTitle": "Use cases",
"goodForTitle": "Good for"
},
"seo": {
"home": {
@@ -796,5 +812,263 @@
"new": "New",
"editorsChoice": "Editor's Choice",
"empty": "No tools on this board yet."
},
"siteHome": {
"trending": {
"title": "Trending this week",
"subtitle": "Ranked by votes, reviews and engagement over the last 7 days"
},
"categories": {
"title": "Browse by category",
"viewAll": "All tools"
},
"new": {
"title": "New AI tools"
},
"editors": {
"title": "Editor's Choice"
},
"useCases": {
"title": "Tools by use case",
"subtitle": "What do you want to get done?",
"viewAll": "All use cases"
},
"bestLists": {
"title": "Best AI tool lists",
"subtitle": "Curated rankings by our editors",
"viewAll": "All lists"
},
"reviews": {
"title": "Latest community reviews"
},
"cta": {
"title": "Built an AI tool?",
"body": "Submit it to {brand} and get discovered by thousands of users looking for tools like yours.",
"button": "Submit your tool"
},
"popular": {
"label": "Popular:"
},
"toolsCount": "{count} tools",
"feed": {
"title": "Today's feed",
"browse": "Browse all products"
},
"leaderboard": {
"title": "Leaderboard",
"full": "Full leaderboard"
},
"picks": {
"title": "Editor's picks"
},
"collections": {
"title": "Collections"
},
"launch": {
"button": "Launch your product"
},
"featured": {
"label": "Featured story",
"read": "Read the story"
},
"picksEditorial": {
"label": "Editor's picks"
},
"guides": {
"title": "Buying guides",
"subtitle": "Deep dives that help you choose",
"viewAll": "All guides"
},
"stories": {
"title": "Latest stories",
"viewAll": "All stories"
},
"community": {
"title": "From the community",
"viewAll": "Browse directory"
},
"ship": {
"title": "Ship faster with AI",
"subtitle": "Pick your workflow — we ranked the tools for it",
"viewAll": "All use cases"
},
"upvoted": {
"title": "Most upvoted this week",
"viewAll": "View all"
},
"workflows": {
"title": "Workflows & stacks",
"subtitle": "Batteries-included tool stacks for common dev tasks",
"viewAll": "All workflows"
},
"browseCategory": {
"title": "Browse by category"
},
"trendingNow": {
"title": "Trending now"
},
"seeRankings": "See full rankings",
"browse": "Browse tools",
"noLaunches": "No launches yet — be the first to submit.",
"searchPlaceholder": "Search AI tools...",
"reviewsCount": "{count} reviews",
"mastheadLabel": "INDEPENDENT AI COVERAGE",
"minimalTagline": "AI TOOL SEARCH",
"minimalPlaceholder": "I need to create marketing videos...",
"useCasesBlockTitle": "Use cases",
"goodForBlockTitle": "Good for",
"searchButton": "Search",
"trendingTitle": "Trending this week",
"trendingSubtitle": "Ranked by votes, reviews and engagement over the last 7 days",
"allRankings": "All rankings",
"categoriesTitle": "Browse by category",
"categoriesViewAll": "All tools",
"newTitle": "New AI tools",
"editorsTitle": "Editor's Choice",
"useCasesTitle": "Tools by use case",
"useCasesSubtitle": "What do you want to get done?",
"useCasesViewAll": "All use cases",
"bestListsTitle": "Best AI tool lists",
"bestListsSubtitle": "Curated rankings by our editors",
"bestListsViewAll": "All lists",
"reviewsTitle": "Latest community reviews",
"ctaTitle": "Built an AI tool?",
"ctaBody": "Submit it to {brand} and get discovered by thousands of users looking for tools like yours.",
"ctaButton": "Submit your tool",
"popularLabel": "Popular:",
"feedTitle": "Today's feed",
"feedBrowse": "Browse all products",
"leaderboardTitle": "Leaderboard",
"leaderboardFull": "Full leaderboard",
"picksTitle": "Editor's picks",
"collectionsTitle": "Collections",
"launchButton": "Launch your product",
"featuredLabel": "Featured story",
"featuredRead": "Read the story",
"picksEditorialLabel": "EDITOR'S PICKS",
"guidesTitle": "Buying guides",
"guidesSubtitle": "Deep dives that help you choose",
"guidesViewAll": "All guides",
"storiesTitle": "Latest stories",
"storiesViewAll": "All stories",
"communityTitle": "From the community",
"communityViewAll": "Browse directory",
"shipTitle": "Ship faster with AI",
"shipSubtitle": "Pick your workflow — we ranked the tools for it",
"upvotedTitle": "Most upvoted this week",
"upvotedViewAll": "View all",
"workflowsTitle": "Workflows & stacks",
"workflowsSubtitle": "Batteries-included tool stacks for common dev tasks",
"workflowsViewAll": "All workflows",
"browseCategoryTitle": "Browse by category",
"trendingNowTitle": "Trending now",
"browseButton": "Browse tools",
"toolsCompared": "{count} tools compared",
"popularSearches": [
"writing",
"video",
"coding",
"image"
]
},
"best": {
"indexTitle": "Best AI Tools — Curated Lists",
"indexSubtitle": "Hand-picked rankings, updated with community signal. Every list is labeled and never sold.",
"empty": "No collections published yet.",
"curated": "Curated list",
"faq": "Frequently asked questions",
"more": "More best lists",
"toolsCompared": "{count} tools"
},
"aiToolsFor": {
"indexTitle": "AI tools for what you do",
"indexSubtitle": "Skip the categories. Start from your task or your role — every page ranks tools by real community votes and reviews.",
"byTask": "By task",
"byAudience": "By audience",
"noData": "No use cases yet.",
"noAud": "No audiences yet.",
"noTools": "No tools tagged yet."
},
"submitPage": {
"title": "Submit your AI tool",
"subtitle": "Get a free listing with community votes, user reviews and analytics. Submissions are reviewed by our editors, usually within 48 hours."
},
"faq": {
"title": "Frequently asked questions"
},
"compare": {
"intro": "A side-by-side comparison based on community votes, ratings and reviews — not sponsored placement.",
"leaning": "Right now the community leans toward {name}.",
"label": "Compare",
"noReviews": "—",
"latest": "Latest review"
},
"alternatives": {
"noAlt": "No alternatives found yet.",
"thisTool": "{name} (this tool)",
"table": {
"tool": "Tool",
"rating": "Rating",
"votes": "Votes",
"pricing": "Pricing"
},
"zhTable": {
"tool": "Tool",
"rating": "Rating",
"votes": "Votes",
"pricing": "Pricing"
}
},
"notFound": {
"code": "404",
"title": "Page not found",
"body": "The page you are looking for doesn't exist or may have moved.",
"home": "Back to home",
"tools": "Browse tools"
},
"search": {
"placeholder": "Search 1,000+ AI tools...",
"button": "Search"
},
"siteFooter": {
"groups": [
{
"title": "Search",
"links": [
{
"label": "All Tools",
"href": "/tools"
},
{
"label": "Use Cases",
"href": "/ai-tools-for"
},
{
"label": "Rankings",
"href": "/rankings"
}
]
},
{
"title": "About",
"links": [
{
"label": "Submit a Tool",
"href": "/submit"
},
{
"label": "Privacy",
"href": "/privacy"
},
{
"label": "Terms",
"href": "/terms"
}
]
}
]
},
"siteHeader": {
"searchPlaceholder": "What do you need?"
}
}
+276 -3
View File
@@ -53,11 +53,24 @@
"linkedin": "领英",
"github": "GitHub"
}
},
"site": {
"items": [
"搜索",
"用例",
"排行榜",
"提交"
],
"hrefs": [
"/tools",
"/ai-tools-for",
"/rankings",
"/submit"
]
}
},
"hero": {
"badge": "发现最好的 AI 工具",
"title": "findai — 极简 AI 工具搜索引擎",
"title": "你想完成什么?",
"description": "一个搜索框,找遍全部 AI 工具。说出你想做的事,社区排名替你选。",
"cta": {
"primary": "探索工具",
@@ -657,7 +670,9 @@
"latest": "最新",
"trending": "最热"
},
"clearFilters": "清除搜索与筛选"
"clearFilters": "清除搜索与筛选",
"useCasesTitle": "使用场景",
"goodForTitle": "适合人群"
},
"seo": {
"home": {
@@ -804,5 +819,263 @@
"new": "最新收录",
"editorsChoice": "编辑精选",
"empty": "该榜单暂无工具。"
},
"siteHome": {
"trending": {
"title": "本周趋势",
"subtitle": "按最近 7 天投票、评论与互动排名"
},
"categories": {
"title": "按分类浏览",
"viewAll": "全部工具"
},
"new": {
"title": "最新收录"
},
"editors": {
"title": "编辑精选"
},
"useCases": {
"title": "按用例找工具",
"subtitle": "你想完成什么?",
"viewAll": "全部用例"
},
"bestLists": {
"title": "最佳 AI 工具清单",
"subtitle": "编辑精选排名",
"viewAll": "全部清单"
},
"reviews": {
"title": "最新社区评论"
},
"cta": {
"title": "做了 AI 工具?",
"body": "提交到 {brand},让成千上万寻找工具的用户发现你。",
"button": "提交你的工具"
},
"popular": {
"label": "热门:"
},
"toolsCount": "{count} 个工具",
"feed": {
"title": "今日发布流",
"browse": "浏览全部产品"
},
"leaderboard": {
"title": "排行榜",
"full": "完整榜单"
},
"picks": {
"title": "编辑推荐"
},
"collections": {
"title": "合集"
},
"launch": {
"button": "发布你的产品"
},
"featured": {
"label": "头条文章",
"read": "阅读全文"
},
"picksEditorial": {
"label": "编辑推荐"
},
"guides": {
"title": "选购指南",
"subtitle": "帮你做决定的深度对比",
"viewAll": "全部指南"
},
"stories": {
"title": "最新文章",
"viewAll": "全部文章"
},
"community": {
"title": "来自社区",
"viewAll": "浏览目录"
},
"ship": {
"title": "用 AI 加速交付",
"subtitle": "选好工作流,我们为它排好了工具",
"viewAll": "全部用例"
},
"upvoted": {
"title": "本周最多投票",
"viewAll": "查看全部"
},
"workflows": {
"title": "工作流与技术栈",
"subtitle": "常见开发任务的完整工具组合",
"viewAll": "全部工作流"
},
"browseCategory": {
"title": "按分类浏览"
},
"trendingNow": {
"title": "当前趋势"
},
"seeRankings": "查看完整排行",
"browse": "浏览工具",
"noLaunches": "还没有发布——来做第一个。",
"searchPlaceholder": "搜索 AI 工具...",
"reviewsCount": "{count} 条评论",
"mastheadLabel": "独立 AI 报道",
"minimalTagline": "AI 工具搜索",
"minimalPlaceholder": "我需要制作营销视频……",
"useCasesBlockTitle": "使用场景",
"goodForBlockTitle": "适合人群",
"searchButton": "搜索",
"trendingTitle": "本周趋势",
"trendingSubtitle": "按最近 7 天投票、评论与互动排名",
"allRankings": "全部榜单",
"categoriesTitle": "按分类浏览",
"categoriesViewAll": "全部工具",
"newTitle": "最新收录",
"editorsTitle": "编辑精选",
"useCasesTitle": "按用例找工具",
"useCasesSubtitle": "你想完成什么?",
"useCasesViewAll": "全部用例",
"bestListsTitle": "最佳 AI 工具清单",
"bestListsSubtitle": "编辑精选排名",
"bestListsViewAll": "全部清单",
"reviewsTitle": "最新社区评论",
"ctaTitle": "做了 AI 工具?",
"ctaBody": "提交到 {brand},让成千上万寻找工具的用户发现你。",
"ctaButton": "提交你的工具",
"popularLabel": "热门:",
"feedTitle": "今日发布流",
"feedBrowse": "浏览全部产品",
"leaderboardTitle": "排行榜",
"leaderboardFull": "完整榜单",
"picksTitle": "编辑推荐",
"collectionsTitle": "合集",
"launchButton": "发布你的产品",
"featuredLabel": "头条文章",
"featuredRead": "阅读全文",
"picksEditorialLabel": "编辑推荐",
"guidesTitle": "选购指南",
"guidesSubtitle": "帮你做决定的深度对比",
"guidesViewAll": "全部指南",
"storiesTitle": "最新文章",
"storiesViewAll": "全部文章",
"communityTitle": "来自社区",
"communityViewAll": "浏览目录",
"shipTitle": "用 AI 加速交付",
"shipSubtitle": "选好工作流,我们为它排好了工具",
"upvotedTitle": "本周最多投票",
"upvotedViewAll": "查看全部",
"workflowsTitle": "工作流与技术栈",
"workflowsSubtitle": "常见开发任务的完整工具组合",
"workflowsViewAll": "全部工作流",
"browseCategoryTitle": "按分类浏览",
"trendingNowTitle": "当前趋势",
"browseButton": "浏览工具",
"toolsCompared": "{count} 个工具",
"popularSearches": [
"写作",
"视频",
"编程",
"图像"
]
},
"best": {
"indexTitle": "最佳 AI 工具——精选清单",
"indexSubtitle": "人工精选排名,持续吸收社区信号。每个清单都明确标注,永不售卖。",
"empty": "暂无合集。",
"curated": "编辑精选",
"faq": "常见问题",
"more": "更多最佳清单",
"toolsCompared": "{count} 个工具"
},
"aiToolsFor": {
"indexTitle": "按任务找 AI 工具",
"indexSubtitle": "别再翻分类。从你的任务或角色出发——每页都按真实社区投票与评论排名。",
"byTask": "按任务",
"byAudience": "按人群",
"noData": "暂无用例。",
"noAud": "暂无人群。",
"noTools": "暂无关联工具。"
},
"submitPage": {
"title": "提交你的 AI 工具",
"subtitle": "免费收录,获得社区投票、用户评论与数据分析。编辑通常在 48 小时内完成审核。"
},
"faq": {
"title": "常见问题"
},
"compare": {
"intro": "基于社区投票、评分与评论的并排对比——非赞助排名。",
"leaning": "当前社区更倾向 {name}。",
"label": "对比",
"noReviews": "—",
"latest": "最新评论"
},
"alternatives": {
"noAlt": "暂无替代品。",
"thisTool": "{name}(本工具)",
"table": {
"tool": "工具",
"rating": "评分",
"votes": "票数",
"pricing": "定价"
},
"zhTable": {
"tool": "工具",
"rating": "评分",
"votes": "票数",
"pricing": "定价"
}
},
"notFound": {
"code": "404",
"title": "页面不存在",
"body": "你访问的页面不存在或已被移动。",
"home": "返回首页",
"tools": "浏览工具"
},
"search": {
"placeholder": "搜索 1000+ AI 工具...",
"button": "搜索"
},
"siteFooter": {
"groups": [
{
"title": "搜索",
"links": [
{
"label": "All Tools",
"href": "/tools"
},
{
"label": "Use Cases",
"href": "/ai-tools-for"
},
{
"label": "Rankings",
"href": "/rankings"
}
]
},
{
"title": "关于",
"links": [
{
"label": "Submit a Tool",
"href": "/submit"
},
{
"label": "Privacy",
"href": "/privacy"
},
{
"label": "Terms",
"href": "/terms"
}
]
}
]
},
"siteHeader": {
"searchPlaceholder": "你需要什么?"
}
}
@@ -0,0 +1,2 @@
// 自动生成:[locale]/not-found.tsx 的薄路由
export { default } from "@aidir/core/pages/[locale]/not-found";
+15 -3
View File
@@ -6,13 +6,25 @@ import { locales, defaultLocale, type Locale } from "@aidir/core/i18n.config";
const brandMessages = {
en: {
seo: { home: {"title":"findai — The Minimal AI Tool Search Engine","description":"One search box, every AI tool. Describe what you're trying to do and find the right AI tool, ranked by the community.","ogImage":"/og-image.png"} },
hero: { title: "findai — The Minimal AI Tool Search Engine", description: "One search box, every AI tool. Describe what you're trying to do and find the right AI tool, ranked by the community." },
hero: { title: "What are you trying to do?", description: "One search box, every AI tool. Describe what you're trying to do and find the right AI tool, ranked by the community." },
common: { brand: { name: "findai", tagline: "The minimal AI tool search engine." } },
navigation: {
main: { tools: "Search", blog: "Blog" },
site: { items: ["Search","Use Cases","Rankings","Submit"], hrefs: ["/tools","/ai-tools-for","/rankings","/submit"] },
},
siteFooter: { groups: [{"title":"Search","links":[{"label":"All Tools","href":"/tools"},{"label":"Use Cases","href":"/ai-tools-for"},{"label":"Rankings","href":"/rankings"}]},{"title":"About","links":[{"label":"Submit a Tool","href":"/submit"},{"label":"Privacy","href":"/privacy"},{"label":"Terms","href":"/terms"}]}] },
siteHeader: { searchPlaceholder: "What do you need?" },
},
zh: {
seo: { home: {"title":"findai — 极简 AI 工具搜索引擎","description":"一个搜索框,找遍全部 AI 工具。说出你想做的事,社区排名替你选。","ogImage":"/og-image.png"} },
hero: { title: "findai — 极简 AI 工具搜索引擎", description: "一个搜索框,找遍全部 AI 工具。说出你想做的事,社区排名替你选。" },
common: { brand: { name: "findai", tagline: "The minimal AI tool search engine." } },
hero: { title: "你想完成什么?", description: "一个搜索框,找遍全部 AI 工具。说出你想做的事,社区排名替你选。" },
common: { brand: { name: "findai", tagline: "极简 AI 工具搜索引擎。" } },
navigation: {
main: { tools: "搜索", blog: "博客" },
site: { items: ["搜索","用例","排行榜","提交"], hrefs: ["/tools","/ai-tools-for","/rankings","/submit"] },
},
siteFooter: { groups: [{"title":"搜索","links":[{"label":"All Tools","href":"/tools"},{"label":"Use Cases","href":"/ai-tools-for"},{"label":"Rankings","href":"/rankings"}]},{"title":"关于","links":[{"label":"Submit a Tool","href":"/submit"},{"label":"Privacy","href":"/privacy"},{"label":"Terms","href":"/terms"}]}] },
siteHeader: { searchPlaceholder: "你需要什么?" },
},
} as const;
+17
View File
@@ -2,6 +2,23 @@ import type { SiteConfig } from "@/kit/types";
// findai —— 独立站点的全部品牌/导航/页脚配置
export const siteConfig: SiteConfig = {
"brandZh": {
"name": "findai",
"tagline": "极简 AI 工具搜索引擎。",
"headline": "你想完成什么?",
"subheadline": "",
"nav": [
"搜索",
"用例",
"排行榜",
"提交"
],
"footer": [
"搜索",
"关于"
],
"searchPlaceholder": "你需要什么?"
},
"accent": "260 70% 52%",
"port": 3005,
"brand": {
+5 -2
View File
@@ -1,6 +1,7 @@
// 自动生成:站点首页组件(源自底座 minimal 形态,品牌来自 @/site/brand)
import { siteConfig } from "@/site/brand";
import { Search } from "lucide-react";
import { getTranslations } from "next-intl/server";
import { ToolCardGrid } from "@/components/tool-card";
import { LocaleLink } from "@/components/locale-link";
import { getHomeDataSafe } from "@/kit/home-data";
@@ -44,6 +45,8 @@ const popularSearches = [
];
async function SiteHome({ locale }: { locale: string }) {
const t = await getTranslations("siteHome");
const th = await getTranslations("hero");
const data = await getHomeDataSafe(6);
return (
@@ -63,7 +66,7 @@ async function SiteHome({ locale }: { locale: string }) {
<input
name="search"
autoFocus
placeholder="I need to create marketing videos..."
placeholder={t("minimalPlaceholder")}
className="h-14 w-full rounded-full border border-border bg-background pl-14 pr-6 text-base text-foreground shadow-lg shadow-black/[0.03] placeholder:text-muted-foreground focus:border-primary/50 focus:outline-none focus:ring-4 focus:ring-primary/10"
aria-label="Search AI tools"
/>
@@ -95,7 +98,7 @@ async function SiteHome({ locale }: { locale: string }) {
href="/rankings"
className="text-xs font-medium text-muted-foreground hover:text-primary"
>
See full rankings
{t("seeRankings")}
</LocaleLink>
</div>
</section>
+298 -3
View File
@@ -53,12 +53,30 @@
"linkedin": "LinkedIn",
"github": "GitHub"
}
},
"site": {
"items": [
"Today",
"Leaderboard",
"Categories",
"Collections",
"Blog",
"Submit"
],
"hrefs": [
"/",
"/rankings",
"/tools",
"/best",
"/blog",
"/submit"
]
}
},
"hero": {
"badge": "Discover the Best AI Tools",
"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.",
"title": "Today's AI product launches",
"description": "Fresh AI products, every day. Upvote the best ones to the top.",
"cta": {
"primary": "Explore Tools",
"secondary": "Read Blog"
@@ -649,7 +667,9 @@
"latest": "Latest",
"trending": "Trending"
},
"clearFilters": "Clear search & filters"
"clearFilters": "Clear search & filters",
"useCasesTitle": "Use cases",
"goodForTitle": "Good for"
},
"seo": {
"home": {
@@ -796,5 +816,280 @@
"new": "New",
"editorsChoice": "Editor's Choice",
"empty": "No tools on this board yet."
},
"siteHome": {
"trending": {
"title": "Trending this week",
"subtitle": "Ranked by votes, reviews and engagement over the last 7 days"
},
"categories": {
"title": "Browse by category",
"viewAll": "All tools"
},
"new": {
"title": "New AI tools"
},
"editors": {
"title": "Editor's Choice"
},
"useCases": {
"title": "Tools by use case",
"subtitle": "What do you want to get done?",
"viewAll": "All use cases"
},
"bestLists": {
"title": "Best AI tool lists",
"subtitle": "Curated rankings by our editors",
"viewAll": "All lists"
},
"reviews": {
"title": "Latest community reviews"
},
"cta": {
"title": "Built an AI tool?",
"body": "Submit it to {brand} and get discovered by thousands of users looking for tools like yours.",
"button": "Submit your tool"
},
"popular": {
"label": "Popular:"
},
"toolsCount": "{count} tools",
"feed": {
"title": "Today's feed",
"browse": "Browse all products"
},
"leaderboard": {
"title": "Leaderboard",
"full": "Full leaderboard"
},
"picks": {
"title": "Editor's picks"
},
"collections": {
"title": "Collections"
},
"launch": {
"button": "Launch your product"
},
"featured": {
"label": "Featured story",
"read": "Read the story"
},
"picksEditorial": {
"label": "Editor's picks"
},
"guides": {
"title": "Buying guides",
"subtitle": "Deep dives that help you choose",
"viewAll": "All guides"
},
"stories": {
"title": "Latest stories",
"viewAll": "All stories"
},
"community": {
"title": "From the community",
"viewAll": "Browse directory"
},
"ship": {
"title": "Ship faster with AI",
"subtitle": "Pick your workflow — we ranked the tools for it",
"viewAll": "All use cases"
},
"upvoted": {
"title": "Most upvoted this week",
"viewAll": "View all"
},
"workflows": {
"title": "Workflows & stacks",
"subtitle": "Batteries-included tool stacks for common dev tasks",
"viewAll": "All workflows"
},
"browseCategory": {
"title": "Browse by category"
},
"trendingNow": {
"title": "Trending now"
},
"seeRankings": "See full rankings",
"browse": "Browse tools",
"noLaunches": "No launches yet — be the first to submit.",
"searchPlaceholder": "Search AI tools...",
"reviewsCount": "{count} reviews",
"mastheadLabel": "INDEPENDENT AI COVERAGE",
"minimalTagline": "AI TOOL SEARCH",
"minimalPlaceholder": "I need to create marketing videos...",
"useCasesBlockTitle": "Use cases",
"goodForBlockTitle": "Good for",
"searchButton": "Search",
"trendingTitle": "Trending this week",
"trendingSubtitle": "Ranked by votes, reviews and engagement over the last 7 days",
"allRankings": "All rankings",
"categoriesTitle": "Browse by category",
"categoriesViewAll": "All tools",
"newTitle": "New AI tools",
"editorsTitle": "Editor's Choice",
"useCasesTitle": "Tools by use case",
"useCasesSubtitle": "What do you want to get done?",
"useCasesViewAll": "All use cases",
"bestListsTitle": "Best AI tool lists",
"bestListsSubtitle": "Curated rankings by our editors",
"bestListsViewAll": "All lists",
"reviewsTitle": "Latest community reviews",
"ctaTitle": "Built an AI tool?",
"ctaBody": "Submit it to {brand} and get discovered by thousands of users looking for tools like yours.",
"ctaButton": "Submit your tool",
"popularLabel": "Popular:",
"feedTitle": "Today's feed",
"feedBrowse": "Browse all products",
"leaderboardTitle": "Leaderboard",
"leaderboardFull": "Full leaderboard",
"picksTitle": "Editor's picks",
"collectionsTitle": "Collections",
"launchButton": "Launch your product",
"featuredLabel": "Featured story",
"featuredRead": "Read the story",
"picksEditorialLabel": "EDITOR'S PICKS",
"guidesTitle": "Buying guides",
"guidesSubtitle": "Deep dives that help you choose",
"guidesViewAll": "All guides",
"storiesTitle": "Latest stories",
"storiesViewAll": "All stories",
"communityTitle": "From the community",
"communityViewAll": "Browse directory",
"shipTitle": "Ship faster with AI",
"shipSubtitle": "Pick your workflow — we ranked the tools for it",
"upvotedTitle": "Most upvoted this week",
"upvotedViewAll": "View all",
"workflowsTitle": "Workflows & stacks",
"workflowsSubtitle": "Batteries-included tool stacks for common dev tasks",
"workflowsViewAll": "All workflows",
"browseCategoryTitle": "Browse by category",
"trendingNowTitle": "Trending now",
"browseButton": "Browse tools",
"toolsCompared": "{count} tools compared",
"popularSearches": [
"writing",
"video",
"coding",
"image"
]
},
"best": {
"indexTitle": "Best AI Tools — Curated Lists",
"indexSubtitle": "Hand-picked rankings, updated with community signal. Every list is labeled and never sold.",
"empty": "No collections published yet.",
"curated": "Curated list",
"faq": "Frequently asked questions",
"more": "More best lists",
"toolsCompared": "{count} tools"
},
"aiToolsFor": {
"indexTitle": "AI tools for what you do",
"indexSubtitle": "Skip the categories. Start from your task or your role — every page ranks tools by real community votes and reviews.",
"byTask": "By task",
"byAudience": "By audience",
"noData": "No use cases yet.",
"noAud": "No audiences yet.",
"noTools": "No tools tagged yet."
},
"submitPage": {
"title": "Submit your AI tool",
"subtitle": "Get a free listing with community votes, user reviews and analytics. Submissions are reviewed by our editors, usually within 48 hours."
},
"faq": {
"title": "Frequently asked questions"
},
"compare": {
"intro": "A side-by-side comparison based on community votes, ratings and reviews — not sponsored placement.",
"leaning": "Right now the community leans toward {name}.",
"label": "Compare",
"noReviews": "—",
"latest": "Latest review"
},
"alternatives": {
"noAlt": "No alternatives found yet.",
"thisTool": "{name} (this tool)",
"table": {
"tool": "Tool",
"rating": "Rating",
"votes": "Votes",
"pricing": "Pricing"
},
"zhTable": {
"tool": "Tool",
"rating": "Rating",
"votes": "Votes",
"pricing": "Pricing"
}
},
"notFound": {
"code": "404",
"title": "Page not found",
"body": "The page you are looking for doesn't exist or may have moved.",
"home": "Back to home",
"tools": "Browse tools"
},
"search": {
"placeholder": "Search 1,000+ AI tools...",
"button": "Search"
},
"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..."
}
}
+298 -4
View File
@@ -53,12 +53,29 @@
"linkedin": "领英",
"github": "GitHub"
}
},
"site": {
"items": [
"今日",
"排行榜",
"分类",
"合集",
"博客",
"提交产品"
],
"hrefs": [
"/",
"/rankings",
"/tools",
"/best",
"/blog",
"/submit"
]
}
},
"hero": {
"badge": "发现最好的 AI 工具",
"title": "LaunchAI — 发现并投票今日 AI 新品",
"description": "AI 产品每日发布社区:为最好的 AI 工具投票,登上社区排行榜。",
"title": "今日 AI 产品发布",
"description": "每天上新。为最好的产品投票,冲上排行榜。",
"cta": {
"primary": "探索工具",
"secondary": "阅读博客"
@@ -657,7 +674,9 @@
"latest": "最新",
"trending": "最热"
},
"clearFilters": "清除搜索与筛选"
"clearFilters": "清除搜索与筛选",
"useCasesTitle": "使用场景",
"goodForTitle": "适合人群"
},
"seo": {
"home": {
@@ -804,5 +823,280 @@
"new": "最新收录",
"editorsChoice": "编辑精选",
"empty": "该榜单暂无工具。"
},
"siteHome": {
"trending": {
"title": "本周趋势",
"subtitle": "按最近 7 天投票、评论与互动排名"
},
"categories": {
"title": "按分类浏览",
"viewAll": "全部工具"
},
"new": {
"title": "最新收录"
},
"editors": {
"title": "编辑精选"
},
"useCases": {
"title": "按用例找工具",
"subtitle": "你想完成什么?",
"viewAll": "全部用例"
},
"bestLists": {
"title": "最佳 AI 工具清单",
"subtitle": "编辑精选排名",
"viewAll": "全部清单"
},
"reviews": {
"title": "最新社区评论"
},
"cta": {
"title": "做了 AI 工具?",
"body": "提交到 {brand},让成千上万寻找工具的用户发现你。",
"button": "提交你的工具"
},
"popular": {
"label": "热门:"
},
"toolsCount": "{count} 个工具",
"feed": {
"title": "今日发布流",
"browse": "浏览全部产品"
},
"leaderboard": {
"title": "排行榜",
"full": "完整榜单"
},
"picks": {
"title": "编辑推荐"
},
"collections": {
"title": "合集"
},
"launch": {
"button": "发布你的产品"
},
"featured": {
"label": "头条文章",
"read": "阅读全文"
},
"picksEditorial": {
"label": "编辑推荐"
},
"guides": {
"title": "选购指南",
"subtitle": "帮你做决定的深度对比",
"viewAll": "全部指南"
},
"stories": {
"title": "最新文章",
"viewAll": "全部文章"
},
"community": {
"title": "来自社区",
"viewAll": "浏览目录"
},
"ship": {
"title": "用 AI 加速交付",
"subtitle": "选好工作流,我们为它排好了工具",
"viewAll": "全部用例"
},
"upvoted": {
"title": "本周最多投票",
"viewAll": "查看全部"
},
"workflows": {
"title": "工作流与技术栈",
"subtitle": "常见开发任务的完整工具组合",
"viewAll": "全部工作流"
},
"browseCategory": {
"title": "按分类浏览"
},
"trendingNow": {
"title": "当前趋势"
},
"seeRankings": "查看完整排行",
"browse": "浏览工具",
"noLaunches": "还没有发布——来做第一个。",
"searchPlaceholder": "搜索 AI 工具...",
"reviewsCount": "{count} 条评论",
"mastheadLabel": "独立 AI 报道",
"minimalTagline": "AI 工具搜索",
"minimalPlaceholder": "我需要制作营销视频……",
"useCasesBlockTitle": "使用场景",
"goodForBlockTitle": "适合人群",
"searchButton": "搜索",
"trendingTitle": "本周趋势",
"trendingSubtitle": "按最近 7 天投票、评论与互动排名",
"allRankings": "全部榜单",
"categoriesTitle": "按分类浏览",
"categoriesViewAll": "全部工具",
"newTitle": "最新收录",
"editorsTitle": "编辑精选",
"useCasesTitle": "按用例找工具",
"useCasesSubtitle": "你想完成什么?",
"useCasesViewAll": "全部用例",
"bestListsTitle": "最佳 AI 工具清单",
"bestListsSubtitle": "编辑精选排名",
"bestListsViewAll": "全部清单",
"reviewsTitle": "最新社区评论",
"ctaTitle": "做了 AI 工具?",
"ctaBody": "提交到 {brand},让成千上万寻找工具的用户发现你。",
"ctaButton": "提交你的工具",
"popularLabel": "热门:",
"feedTitle": "今日发布流",
"feedBrowse": "浏览全部产品",
"leaderboardTitle": "排行榜",
"leaderboardFull": "完整榜单",
"picksTitle": "编辑推荐",
"collectionsTitle": "合集",
"launchButton": "发布你的产品",
"featuredLabel": "头条文章",
"featuredRead": "阅读全文",
"picksEditorialLabel": "编辑推荐",
"guidesTitle": "选购指南",
"guidesSubtitle": "帮你做决定的深度对比",
"guidesViewAll": "全部指南",
"storiesTitle": "最新文章",
"storiesViewAll": "全部文章",
"communityTitle": "来自社区",
"communityViewAll": "浏览目录",
"shipTitle": "用 AI 加速交付",
"shipSubtitle": "选好工作流,我们为它排好了工具",
"upvotedTitle": "本周最多投票",
"upvotedViewAll": "查看全部",
"workflowsTitle": "工作流与技术栈",
"workflowsSubtitle": "常见开发任务的完整工具组合",
"workflowsViewAll": "全部工作流",
"browseCategoryTitle": "按分类浏览",
"trendingNowTitle": "当前趋势",
"browseButton": "浏览工具",
"toolsCompared": "{count} 个工具",
"popularSearches": [
"写作",
"视频",
"编程",
"图像"
]
},
"best": {
"indexTitle": "最佳 AI 工具——精选清单",
"indexSubtitle": "人工精选排名,持续吸收社区信号。每个清单都明确标注,永不售卖。",
"empty": "暂无合集。",
"curated": "编辑精选",
"faq": "常见问题",
"more": "更多最佳清单",
"toolsCompared": "{count} 个工具"
},
"aiToolsFor": {
"indexTitle": "按任务找 AI 工具",
"indexSubtitle": "别再翻分类。从你的任务或角色出发——每页都按真实社区投票与评论排名。",
"byTask": "按任务",
"byAudience": "按人群",
"noData": "暂无用例。",
"noAud": "暂无人群。",
"noTools": "暂无关联工具。"
},
"submitPage": {
"title": "提交你的 AI 工具",
"subtitle": "免费收录,获得社区投票、用户评论与数据分析。编辑通常在 48 小时内完成审核。"
},
"faq": {
"title": "常见问题"
},
"compare": {
"intro": "基于社区投票、评分与评论的并排对比——非赞助排名。",
"leaning": "当前社区更倾向 {name}。",
"label": "对比",
"noReviews": "—",
"latest": "最新评论"
},
"alternatives": {
"noAlt": "暂无替代品。",
"thisTool": "{name}(本工具)",
"table": {
"tool": "工具",
"rating": "评分",
"votes": "票数",
"pricing": "定价"
},
"zhTable": {
"tool": "工具",
"rating": "评分",
"votes": "票数",
"pricing": "定价"
}
},
"notFound": {
"code": "404",
"title": "页面不存在",
"body": "你访问的页面不存在或已被移动。",
"home": "返回首页",
"tools": "浏览工具"
},
"search": {
"placeholder": "搜索 1000+ AI 工具...",
"button": "搜索"
},
"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 产品..."
}
}
@@ -0,0 +1,2 @@
// 自动生成:[locale]/not-found.tsx 的薄路由
export { default } from "@aidir/core/pages/[locale]/not-found";
+15 -3
View File
@@ -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;
+20
View File
@@ -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": {
+11 -8
View File
@@ -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}>
+298 -3
View File
@@ -53,12 +53,30 @@
"linkedin": "LinkedIn",
"github": "GitHub"
}
},
"site": {
"items": [
"Stories",
"Guides",
"Best Picks",
"Directory",
"Rankings",
"Submit"
],
"hrefs": [
"/blog",
"/ai-tools-for",
"/best",
"/tools",
"/rankings",
"/submit"
]
}
},
"hero": {
"badge": "Discover the Best AI Tools",
"title": "The AI Ledger — AI Tools, Reviewed and Ranked",
"description": "Independent journalism on AI: buying guides, comparisons and a community-ranked directory of the best AI tools.",
"title": "AI tools, reviewed and ranked",
"description": "Guides, comparisons and a community-powered directory of the best AI tools.",
"cta": {
"primary": "Explore Tools",
"secondary": "Read Blog"
@@ -649,7 +667,9 @@
"latest": "Latest",
"trending": "Trending"
},
"clearFilters": "Clear search & filters"
"clearFilters": "Clear search & filters",
"useCasesTitle": "Use cases",
"goodForTitle": "Good for"
},
"seo": {
"home": {
@@ -796,5 +816,280 @@
"new": "New",
"editorsChoice": "Editor's Choice",
"empty": "No tools on this board yet."
},
"siteHome": {
"trending": {
"title": "Trending this week",
"subtitle": "Ranked by votes, reviews and engagement over the last 7 days"
},
"categories": {
"title": "Browse by category",
"viewAll": "All tools"
},
"new": {
"title": "New AI tools"
},
"editors": {
"title": "Editor's Choice"
},
"useCases": {
"title": "Tools by use case",
"subtitle": "What do you want to get done?",
"viewAll": "All use cases"
},
"bestLists": {
"title": "Best AI tool lists",
"subtitle": "Curated rankings by our editors",
"viewAll": "All lists"
},
"reviews": {
"title": "Latest community reviews"
},
"cta": {
"title": "Built an AI tool?",
"body": "Submit it to {brand} and get discovered by thousands of users looking for tools like yours.",
"button": "Submit your tool"
},
"popular": {
"label": "Popular:"
},
"toolsCount": "{count} tools",
"feed": {
"title": "Today's feed",
"browse": "Browse all products"
},
"leaderboard": {
"title": "Leaderboard",
"full": "Full leaderboard"
},
"picks": {
"title": "Editor's picks"
},
"collections": {
"title": "Collections"
},
"launch": {
"button": "Launch your product"
},
"featured": {
"label": "Featured story",
"read": "Read the story"
},
"picksEditorial": {
"label": "Editor's picks"
},
"guides": {
"title": "Buying guides",
"subtitle": "Deep dives that help you choose",
"viewAll": "All guides"
},
"stories": {
"title": "Latest stories",
"viewAll": "All stories"
},
"community": {
"title": "From the community",
"viewAll": "Browse directory"
},
"ship": {
"title": "Ship faster with AI",
"subtitle": "Pick your workflow — we ranked the tools for it",
"viewAll": "All use cases"
},
"upvoted": {
"title": "Most upvoted this week",
"viewAll": "View all"
},
"workflows": {
"title": "Workflows & stacks",
"subtitle": "Batteries-included tool stacks for common dev tasks",
"viewAll": "All workflows"
},
"browseCategory": {
"title": "Browse by category"
},
"trendingNow": {
"title": "Trending now"
},
"seeRankings": "See full rankings",
"browse": "Browse tools",
"noLaunches": "No launches yet — be the first to submit.",
"searchPlaceholder": "Search AI tools...",
"reviewsCount": "{count} reviews",
"mastheadLabel": "INDEPENDENT AI COVERAGE",
"minimalTagline": "AI TOOL SEARCH",
"minimalPlaceholder": "I need to create marketing videos...",
"useCasesBlockTitle": "Use cases",
"goodForBlockTitle": "Good for",
"searchButton": "Search",
"trendingTitle": "Trending this week",
"trendingSubtitle": "Ranked by votes, reviews and engagement over the last 7 days",
"allRankings": "All rankings",
"categoriesTitle": "Browse by category",
"categoriesViewAll": "All tools",
"newTitle": "New AI tools",
"editorsTitle": "Editor's Choice",
"useCasesTitle": "Tools by use case",
"useCasesSubtitle": "What do you want to get done?",
"useCasesViewAll": "All use cases",
"bestListsTitle": "Best AI tool lists",
"bestListsSubtitle": "Curated rankings by our editors",
"bestListsViewAll": "All lists",
"reviewsTitle": "Latest community reviews",
"ctaTitle": "Built an AI tool?",
"ctaBody": "Submit it to {brand} and get discovered by thousands of users looking for tools like yours.",
"ctaButton": "Submit your tool",
"popularLabel": "Popular:",
"feedTitle": "Today's feed",
"feedBrowse": "Browse all products",
"leaderboardTitle": "Leaderboard",
"leaderboardFull": "Full leaderboard",
"picksTitle": "Editor's picks",
"collectionsTitle": "Collections",
"launchButton": "Launch your product",
"featuredLabel": "Featured story",
"featuredRead": "Read the story",
"picksEditorialLabel": "EDITOR'S PICKS",
"guidesTitle": "Buying guides",
"guidesSubtitle": "Deep dives that help you choose",
"guidesViewAll": "All guides",
"storiesTitle": "Latest stories",
"storiesViewAll": "All stories",
"communityTitle": "From the community",
"communityViewAll": "Browse directory",
"shipTitle": "Ship faster with AI",
"shipSubtitle": "Pick your workflow — we ranked the tools for it",
"upvotedTitle": "Most upvoted this week",
"upvotedViewAll": "View all",
"workflowsTitle": "Workflows & stacks",
"workflowsSubtitle": "Batteries-included tool stacks for common dev tasks",
"workflowsViewAll": "All workflows",
"browseCategoryTitle": "Browse by category",
"trendingNowTitle": "Trending now",
"browseButton": "Browse tools",
"toolsCompared": "{count} tools compared",
"popularSearches": [
"writing",
"video",
"coding",
"image"
]
},
"best": {
"indexTitle": "Best AI Tools — Curated Lists",
"indexSubtitle": "Hand-picked rankings, updated with community signal. Every list is labeled and never sold.",
"empty": "No collections published yet.",
"curated": "Curated list",
"faq": "Frequently asked questions",
"more": "More best lists",
"toolsCompared": "{count} tools"
},
"aiToolsFor": {
"indexTitle": "AI tools for what you do",
"indexSubtitle": "Skip the categories. Start from your task or your role — every page ranks tools by real community votes and reviews.",
"byTask": "By task",
"byAudience": "By audience",
"noData": "No use cases yet.",
"noAud": "No audiences yet.",
"noTools": "No tools tagged yet."
},
"submitPage": {
"title": "Submit your AI tool",
"subtitle": "Get a free listing with community votes, user reviews and analytics. Submissions are reviewed by our editors, usually within 48 hours."
},
"faq": {
"title": "Frequently asked questions"
},
"compare": {
"intro": "A side-by-side comparison based on community votes, ratings and reviews — not sponsored placement.",
"leaning": "Right now the community leans toward {name}.",
"label": "Compare",
"noReviews": "—",
"latest": "Latest review"
},
"alternatives": {
"noAlt": "No alternatives found yet.",
"thisTool": "{name} (this tool)",
"table": {
"tool": "Tool",
"rating": "Rating",
"votes": "Votes",
"pricing": "Pricing"
},
"zhTable": {
"tool": "Tool",
"rating": "Rating",
"votes": "Votes",
"pricing": "Pricing"
}
},
"notFound": {
"code": "404",
"title": "Page not found",
"body": "The page you are looking for doesn't exist or may have moved.",
"home": "Back to home",
"tools": "Browse tools"
},
"search": {
"placeholder": "Search 1,000+ AI tools...",
"button": "Search"
},
"siteFooter": {
"groups": [
{
"title": "Read",
"links": [
{
"label": "Latest Stories",
"href": "/blog"
},
{
"label": "Buying Guides",
"href": "/best"
},
{
"label": "Use Case Guides",
"href": "/ai-tools-for"
}
]
},
{
"title": "Directory",
"links": [
{
"label": "All Tools",
"href": "/tools"
},
{
"label": "Community Rankings",
"href": "/rankings"
},
{
"label": "Submit a Tool",
"href": "/submit"
}
]
},
{
"title": "Company",
"links": [
{
"label": "Contact",
"href": "/contact"
},
{
"label": "Terms",
"href": "/terms"
},
{
"label": "Privacy",
"href": "/privacy"
}
]
}
]
},
"siteHeader": {
"searchPlaceholder": "Search the directory..."
}
}
+298 -4
View File
@@ -53,12 +53,29 @@
"linkedin": "领英",
"github": "GitHub"
}
},
"site": {
"items": [
"文章",
"指南",
"最佳推荐",
"目录",
"排行榜",
"提交"
],
"hrefs": [
"/blog",
"/ai-tools-for",
"/best",
"/tools",
"/rankings",
"/submit"
]
}
},
"hero": {
"badge": "发现最好的 AI 工具",
"title": "The AI Ledger — AI 工具评测与排名",
"description": "独立 AI 媒体:深度指南、横向对比,以及由社区排名的 AI 工具目录。",
"title": "AI 工具,评测与排名",
"description": "指南、对比,以及由社区驱动的最佳 AI 工具目录。",
"cta": {
"primary": "探索工具",
"secondary": "阅读博客"
@@ -657,7 +674,9 @@
"latest": "最新",
"trending": "最热"
},
"clearFilters": "清除搜索与筛选"
"clearFilters": "清除搜索与筛选",
"useCasesTitle": "使用场景",
"goodForTitle": "适合人群"
},
"seo": {
"home": {
@@ -804,5 +823,280 @@
"new": "最新收录",
"editorsChoice": "编辑精选",
"empty": "该榜单暂无工具。"
},
"siteHome": {
"trending": {
"title": "本周趋势",
"subtitle": "按最近 7 天投票、评论与互动排名"
},
"categories": {
"title": "按分类浏览",
"viewAll": "全部工具"
},
"new": {
"title": "最新收录"
},
"editors": {
"title": "编辑精选"
},
"useCases": {
"title": "按用例找工具",
"subtitle": "你想完成什么?",
"viewAll": "全部用例"
},
"bestLists": {
"title": "最佳 AI 工具清单",
"subtitle": "编辑精选排名",
"viewAll": "全部清单"
},
"reviews": {
"title": "最新社区评论"
},
"cta": {
"title": "做了 AI 工具?",
"body": "提交到 {brand},让成千上万寻找工具的用户发现你。",
"button": "提交你的工具"
},
"popular": {
"label": "热门:"
},
"toolsCount": "{count} 个工具",
"feed": {
"title": "今日发布流",
"browse": "浏览全部产品"
},
"leaderboard": {
"title": "排行榜",
"full": "完整榜单"
},
"picks": {
"title": "编辑推荐"
},
"collections": {
"title": "合集"
},
"launch": {
"button": "发布你的产品"
},
"featured": {
"label": "头条文章",
"read": "阅读全文"
},
"picksEditorial": {
"label": "编辑推荐"
},
"guides": {
"title": "选购指南",
"subtitle": "帮你做决定的深度对比",
"viewAll": "全部指南"
},
"stories": {
"title": "最新文章",
"viewAll": "全部文章"
},
"community": {
"title": "来自社区",
"viewAll": "浏览目录"
},
"ship": {
"title": "用 AI 加速交付",
"subtitle": "选好工作流,我们为它排好了工具",
"viewAll": "全部用例"
},
"upvoted": {
"title": "本周最多投票",
"viewAll": "查看全部"
},
"workflows": {
"title": "工作流与技术栈",
"subtitle": "常见开发任务的完整工具组合",
"viewAll": "全部工作流"
},
"browseCategory": {
"title": "按分类浏览"
},
"trendingNow": {
"title": "当前趋势"
},
"seeRankings": "查看完整排行",
"browse": "浏览工具",
"noLaunches": "还没有发布——来做第一个。",
"searchPlaceholder": "搜索 AI 工具...",
"reviewsCount": "{count} 条评论",
"mastheadLabel": "独立 AI 报道",
"minimalTagline": "AI 工具搜索",
"minimalPlaceholder": "我需要制作营销视频……",
"useCasesBlockTitle": "使用场景",
"goodForBlockTitle": "适合人群",
"searchButton": "搜索",
"trendingTitle": "本周趋势",
"trendingSubtitle": "按最近 7 天投票、评论与互动排名",
"allRankings": "全部榜单",
"categoriesTitle": "按分类浏览",
"categoriesViewAll": "全部工具",
"newTitle": "最新收录",
"editorsTitle": "编辑精选",
"useCasesTitle": "按用例找工具",
"useCasesSubtitle": "你想完成什么?",
"useCasesViewAll": "全部用例",
"bestListsTitle": "最佳 AI 工具清单",
"bestListsSubtitle": "编辑精选排名",
"bestListsViewAll": "全部清单",
"reviewsTitle": "最新社区评论",
"ctaTitle": "做了 AI 工具?",
"ctaBody": "提交到 {brand},让成千上万寻找工具的用户发现你。",
"ctaButton": "提交你的工具",
"popularLabel": "热门:",
"feedTitle": "今日发布流",
"feedBrowse": "浏览全部产品",
"leaderboardTitle": "排行榜",
"leaderboardFull": "完整榜单",
"picksTitle": "编辑推荐",
"collectionsTitle": "合集",
"launchButton": "发布你的产品",
"featuredLabel": "头条文章",
"featuredRead": "阅读全文",
"picksEditorialLabel": "编辑推荐",
"guidesTitle": "选购指南",
"guidesSubtitle": "帮你做决定的深度对比",
"guidesViewAll": "全部指南",
"storiesTitle": "最新文章",
"storiesViewAll": "全部文章",
"communityTitle": "来自社区",
"communityViewAll": "浏览目录",
"shipTitle": "用 AI 加速交付",
"shipSubtitle": "选好工作流,我们为它排好了工具",
"upvotedTitle": "本周最多投票",
"upvotedViewAll": "查看全部",
"workflowsTitle": "工作流与技术栈",
"workflowsSubtitle": "常见开发任务的完整工具组合",
"workflowsViewAll": "全部工作流",
"browseCategoryTitle": "按分类浏览",
"trendingNowTitle": "当前趋势",
"browseButton": "浏览工具",
"toolsCompared": "{count} 个工具",
"popularSearches": [
"写作",
"视频",
"编程",
"图像"
]
},
"best": {
"indexTitle": "最佳 AI 工具——精选清单",
"indexSubtitle": "人工精选排名,持续吸收社区信号。每个清单都明确标注,永不售卖。",
"empty": "暂无合集。",
"curated": "编辑精选",
"faq": "常见问题",
"more": "更多最佳清单",
"toolsCompared": "{count} 个工具"
},
"aiToolsFor": {
"indexTitle": "按任务找 AI 工具",
"indexSubtitle": "别再翻分类。从你的任务或角色出发——每页都按真实社区投票与评论排名。",
"byTask": "按任务",
"byAudience": "按人群",
"noData": "暂无用例。",
"noAud": "暂无人群。",
"noTools": "暂无关联工具。"
},
"submitPage": {
"title": "提交你的 AI 工具",
"subtitle": "免费收录,获得社区投票、用户评论与数据分析。编辑通常在 48 小时内完成审核。"
},
"faq": {
"title": "常见问题"
},
"compare": {
"intro": "基于社区投票、评分与评论的并排对比——非赞助排名。",
"leaning": "当前社区更倾向 {name}。",
"label": "对比",
"noReviews": "—",
"latest": "最新评论"
},
"alternatives": {
"noAlt": "暂无替代品。",
"thisTool": "{name}(本工具)",
"table": {
"tool": "工具",
"rating": "评分",
"votes": "票数",
"pricing": "定价"
},
"zhTable": {
"tool": "工具",
"rating": "评分",
"votes": "票数",
"pricing": "定价"
}
},
"notFound": {
"code": "404",
"title": "页面不存在",
"body": "你访问的页面不存在或已被移动。",
"home": "返回首页",
"tools": "浏览工具"
},
"search": {
"placeholder": "搜索 1000+ AI 工具...",
"button": "搜索"
},
"siteFooter": {
"groups": [
{
"title": "阅读",
"links": [
{
"label": "Latest Stories",
"href": "/blog"
},
{
"label": "Buying Guides",
"href": "/best"
},
{
"label": "Use Case Guides",
"href": "/ai-tools-for"
}
]
},
{
"title": "目录",
"links": [
{
"label": "All Tools",
"href": "/tools"
},
{
"label": "Community Rankings",
"href": "/rankings"
},
{
"label": "Submit a Tool",
"href": "/submit"
}
]
},
{
"title": "公司",
"links": [
{
"label": "Contact",
"href": "/contact"
},
{
"label": "Terms",
"href": "/terms"
},
{
"label": "Privacy",
"href": "/privacy"
}
]
}
]
},
"siteHeader": {
"searchPlaceholder": "搜索目录..."
}
}
@@ -0,0 +1,2 @@
// 自动生成:[locale]/not-found.tsx 的薄路由
export { default } from "@aidir/core/pages/[locale]/not-found";
+15 -3
View File
@@ -6,13 +6,25 @@ import { locales, defaultLocale, type Locale } from "@aidir/core/i18n.config";
const brandMessages = {
en: {
seo: { home: {"title":"The AI Ledger — AI Tools, Reviewed and Ranked","description":"Independent journalism on AI: buying guides, comparisons and a community-ranked directory of the best AI tools.","ogImage":"/og-image.png"} },
hero: { title: "The AI Ledger — AI Tools, Reviewed and Ranked", description: "Independent journalism on AI: buying guides, comparisons and a community-ranked directory of the best AI tools." },
hero: { title: "AI tools, reviewed and ranked", description: "Guides, comparisons and a community-powered directory of the best AI tools." },
common: { brand: { name: "The AI Ledger", tagline: "Independent journalism on AI tools, with a community-ranked directory." } },
navigation: {
main: { tools: "Stories", blog: "Stories" },
site: { items: ["Stories","Guides","Best Picks","Directory","Rankings","Submit"], hrefs: ["/blog","/ai-tools-for","/best","/tools","/rankings","/submit"] },
},
siteFooter: { groups: [{"title":"Read","links":[{"label":"Latest Stories","href":"/blog"},{"label":"Buying Guides","href":"/best"},{"label":"Use Case Guides","href":"/ai-tools-for"}]},{"title":"Directory","links":[{"label":"All Tools","href":"/tools"},{"label":"Community Rankings","href":"/rankings"},{"label":"Submit a Tool","href":"/submit"}]},{"title":"Company","links":[{"label":"Contact","href":"/contact"},{"label":"Terms","href":"/terms"},{"label":"Privacy","href":"/privacy"}]}] },
siteHeader: { searchPlaceholder: "Search the directory..." },
},
zh: {
seo: { home: {"title":"The AI Ledger — AI 工具评测与排名","description":"独立 AI 媒体:深度指南、横向对比,以及由社区排名的 AI 工具目录。","ogImage":"/og-image.png"} },
hero: { title: "The AI Ledger — AI 工具评测与排名", description: "独立 AI 媒体:深度指南、横向对比,以及由社区排名的 AI 工具目录。" },
common: { brand: { name: "The AI Ledger", tagline: "Independent journalism on AI tools, with a community-ranked directory." } },
hero: { title: "AI 工具,评测与排名", description: "指南、对比,以及由社区驱动的最佳 AI 工具目录。" },
common: { brand: { name: "The AI Ledger", tagline: "独立 AI 报道,附社区排名目录。" } },
navigation: {
main: { tools: "文章", blog: "文章" },
site: { items: ["文章","指南","最佳推荐","目录","排行榜","提交"], hrefs: ["/blog","/ai-tools-for","/best","/tools","/rankings","/submit"] },
},
siteFooter: { groups: [{"title":"阅读","links":[{"label":"Latest Stories","href":"/blog"},{"label":"Buying Guides","href":"/best"},{"label":"Use Case Guides","href":"/ai-tools-for"}]},{"title":"目录","links":[{"label":"All Tools","href":"/tools"},{"label":"Community Rankings","href":"/rankings"},{"label":"Submit a Tool","href":"/submit"}]},{"title":"公司","links":[{"label":"Contact","href":"/contact"},{"label":"Terms","href":"/terms"},{"label":"Privacy","href":"/privacy"}]}] },
siteHeader: { searchPlaceholder: "搜索目录..." },
},
} as const;
+20
View File
@@ -2,6 +2,26 @@ import type { SiteConfig } from "@/kit/types";
// The AI Ledger —— 独立站点的全部品牌/导航/页脚配置
export const siteConfig: SiteConfig = {
"brandZh": {
"name": "The AI Ledger",
"tagline": "独立 AI 报道,附社区排名目录。",
"headline": "AI 工具,评测与排名",
"subheadline": "指南、对比,以及由社区驱动的最佳 AI 工具目录。",
"nav": [
"文章",
"指南",
"最佳推荐",
"目录",
"排行榜",
"提交"
],
"footer": [
"阅读",
"目录",
"公司"
],
"searchPlaceholder": "搜索目录..."
},
"accent": "36 70% 40%",
"port": 3003,
"brand": {
+16 -13
View File
@@ -1,6 +1,7 @@
// 自动生成:站点首页组件(源自底座 editorial 形态,品牌来自 @/site/brand)
import { siteConfig } from "@/site/brand";
import { BookOpen, PenTool, ArrowRight, Star } from "lucide-react";
import { getTranslations } from "next-intl/server";
import { LocaleLink } from "@/components/locale-link";
import { SectionHeader } from "@/kit/section";
import { getHomeDataSafe } from "@/kit/home-data";
@@ -45,6 +46,8 @@ const footerGroups = [
];
async function SiteHome({ locale }: { locale: string }) {
const t = await getTranslations("siteHome");
const th = await getTranslations("hero");
const data = await getHomeDataSafe(6);
const featured = data.articles[0];
const editorPicks = data.editorsChoice.slice(0, 4);
@@ -55,13 +58,13 @@ async function SiteHome({ locale }: { locale: string }) {
<section className="border-b border-border">
<div className="mx-auto max-w-4xl px-4 py-16 text-center sm:px-6 md:py-20">
<p className="text-xs font-medium uppercase tracking-[0.3em] text-muted-foreground">
Independent AI Coverage
{t("mastheadLabel")}
</p>
<h1 className="mt-4 text-4xl font-bold tracking-tight text-foreground md:text-5xl">
{siteConfig.brand.headline}
{th("title")}
</h1>
<p className="mx-auto mt-4 max-w-xl text-base leading-relaxed text-muted-foreground">
{siteConfig.brand.subheadline}
{th("description")}
</p>
</div>
</section>
@@ -72,7 +75,7 @@ async function SiteHome({ locale }: { locale: string }) {
<section>
<div className="mb-4 flex items-center gap-2 text-xs font-semibold uppercase tracking-widest text-primary">
<BookOpen className="h-3.5 w-3.5" />
Featured story
{t("featuredLabel")}
</div>
<LocaleLink href={`/blog/${featured.slug}`} className="group block">
<h2 className="text-2xl font-bold leading-snug text-foreground group-hover:underline md:text-3xl">
@@ -84,7 +87,7 @@ async function SiteHome({ locale }: { locale: string }) {
</p>
)}
<span className="mt-3 inline-flex items-center gap-1 text-sm font-medium text-primary">
Read the story
{t("featuredRead")}
<ArrowRight className="h-3.5 w-3.5 transition-transform group-hover:translate-x-0.5" />
</span>
</LocaleLink>
@@ -96,7 +99,7 @@ async function SiteHome({ locale }: { locale: string }) {
<section>
<div className="mb-4 flex items-center gap-2 text-xs font-semibold uppercase tracking-widest text-primary">
<PenTool className="h-3.5 w-3.5" />
Editor's picks
{t("picksEditorialLabel")}
</div>
<div className="divide-y divide-border border-y border-border">
{editorPicks.map((tool, i) => (
@@ -132,10 +135,10 @@ async function SiteHome({ locale }: { locale: string }) {
{data.collections.length > 0 && (
<section>
<SectionHeader
title="Buying guides"
subtitle="Deep dives that help you choose"
title={t("guidesTitle")}
subtitle={t("guidesSubtitle")}
viewAllHref="/best"
viewAllLabel="All guides"
viewAllLabel={t("guidesViewAll")}
/>
<div className="grid gap-6 sm:grid-cols-2">
{data.collections.slice(0, 4).map((col) => (
@@ -151,7 +154,7 @@ async function SiteHome({ locale }: { locale: string }) {
{locale === "zh" && col.introZh ? col.introZh : col.introEn}
</p>
<p className="mt-2 text-xs uppercase tracking-wide text-muted-foreground">
{col.toolCount} tools compared
{col.toolCount} {t("toolsCompared")}
</p>
</LocaleLink>
))}
@@ -162,7 +165,7 @@ async function SiteHome({ locale }: { locale: string }) {
{/* Latest articles */}
{data.articles.length > 1 && (
<section>
<SectionHeader title="Latest stories" viewAllHref="/blog" />
<SectionHeader title={t("storiesTitle")} viewAllHref="/blog" />
<div className="grid gap-6 sm:grid-cols-2">
{data.articles.slice(1).map((article) => (
<LocaleLink key={article.slug} href={`/blog/${article.slug}`} className="group">
@@ -184,9 +187,9 @@ async function SiteHome({ locale }: { locale: string }) {
{data.latestReviews.length > 0 && (
<section>
<SectionHeader
title="From the community"
title={t("communityTitle")}
viewAllHref="/tools"
viewAllLabel="Browse directory"
viewAllLabel={t("communityViewAll")}
/>
<div className="grid gap-6 sm:grid-cols-2">
{data.latestReviews.slice(0, 4).map((review, i) => (