diff --git a/frontend/src/app/about/page.tsx b/frontend/src/app/about/page.tsx index 73e7cbd..6e7f50f 100644 --- a/frontend/src/app/about/page.tsx +++ b/frontend/src/app/about/page.tsx @@ -1,62 +1,11 @@ -import Link from "next/link"; import type { Metadata } from "next"; -import SiteShell from "@/components/SiteShell"; +import AboutClient from "@/components/AboutClient"; export const metadata: Metadata = { - title: "关于 nomadro", + title: "关于 nomadro · About", description: "nomadro —— 用一行代码环游世界,数字游民旅居指南", }; export default function AboutPage() { - return ( - -
- -
- 🌏 ABOUT -

关于 nomadro

-

用一行代码环游世界

- -
-

我们是谁

-

- nomadro 是一个面向数字游民的旅居决策平台。我们把目的地数据、签证信息、费用估算、时区协作与社区经验集中在一起,帮你更快做出「下一站去哪」的决定。 -

-
- -
-

能做什么

-
    -
  • 🗺️ 探索全球热门游民城市与对比工具
  • -
  • 🧮 计算生活成本、机票与启动金目标
  • -
  • 📋 签证匹配、保险与上网方案参考
  • -
  • 🗓️ 规划多城行程并分享给伙伴
  • -
  • 🗣️ 落地短语、行李清单与活动日历
  • -
-
- -
-

联系我们

-

- 合作与反馈: - hello@nomadro.com -

-

- 开源仓库: - - gitea.dsx2020.com/eric/nomadweb - -

-
- -
- 开始探索 🌍 - 加入社区 -
-
-
-
- ); + return ; } diff --git a/frontend/src/app/changelog/page.tsx b/frontend/src/app/changelog/page.tsx index ebbd279..84fa1cc 100644 --- a/frontend/src/app/changelog/page.tsx +++ b/frontend/src/app/changelog/page.tsx @@ -8,6 +8,14 @@ export const metadata: Metadata = { }; const LOGS = [ + { + date: "2026-09-04", + tag: "首页/关于页", + items: [ + "首页恢复「游民一天」与「游民心声」区块,中英文化并直达七天指南/活动/社区", + "关于页改为可切换语言,CTA 指向下一站、社区与旅居计划;静态帮助页主按钮高亮", + ], + }, { date: "2026-09-04", tag: "学院/博客/社区/电子书", diff --git a/frontend/src/components/AboutClient.tsx b/frontend/src/components/AboutClient.tsx new file mode 100644 index 0000000..a55f6e8 --- /dev/null +++ b/frontend/src/components/AboutClient.tsx @@ -0,0 +1,68 @@ +"use client"; + +import Link from "next/link"; +import SiteShell from "@/components/SiteShell"; +import { useI18n } from "@/lib/i18n"; + +export default function AboutClient() { + const { t } = useI18n(); + const A = t.aboutPage; + + return ( + +
+ +
+ {A.tag} +

{A.title}

+

{A.subtitle}

+ +
+

{A.whoTitle}

+

{A.whoBody}

+
+ +
+

{A.whatTitle}

+
    +
  • {A.what1}
  • +
  • {A.what2}
  • +
  • {A.what3}
  • +
  • {A.what4}
  • +
  • {A.what5}
  • +
+
+ +
+

{A.contactTitle}

+

+ {A.contactBody} + hello@nomadro.com +

+

+ {A.repoBody} + + {" "} + gitea.dsx2020.com/eric/nomadweb + +

+
+ +
+ + {A.ctaExplore} + + + {A.ctaCommunity} + + + {A.ctaPlan} + +
+
+
+
+ ); +} diff --git a/frontend/src/components/Community.tsx b/frontend/src/components/Community.tsx index 92c5fbb..6b9a117 100644 --- a/frontend/src/components/Community.tsx +++ b/frontend/src/components/Community.tsx @@ -1,102 +1,97 @@ "use client"; import { useEffect, useState } from "react"; -import { useToast } from "@/lib/toast"; -import { api } from "@/lib/api"; +import Link from "next/link"; +import { useI18n } from "@/lib/i18n"; +import NewsletterSubscribe from "@/components/NewsletterSubscribe"; import type { Testimonial } from "@/lib/types"; export default function Community({ testimonials }: { testimonials: Testimonial[] }) { - const { toast } = useToast(); + const { t } = useI18n(); + const C = t.homeCommunity; const [active, setActive] = useState(0); const [paused, setPaused] = useState(false); useEffect(() => { if (testimonials.length <= 1 || paused) return; - const t = setInterval(() => setActive((i) => (i + 1) % testimonials.length), 4500); - return () => clearInterval(t); + const id = setInterval(() => setActive((i) => (i + 1) % testimonials.length), 4500); + return () => clearInterval(id); }, [testimonials.length, paused]); - const handleSubscribe = async (e: React.FormEvent) => { - e.preventDefault(); - const form = e.currentTarget; - const email = (form.elements.namedItem("email") as HTMLInputElement).value; - try { - await api.subscribe(email); - toast("订阅成功!欢迎加入 nomadro 社区 🎉"); - form.reset(); - } catch { /* ignore */ } - }; - - const t = testimonials[active]; + const cur = testimonials[active]; return ( - <> -
-
-
- 🤝 COMMUNITY -

游民心声

-

来自全球数字游民的真实故事

-
- - {t && ( -
setPaused(true)} - onMouseLeave={() => setPaused(false)} - > -
-
{t.avatar}
-
-
{"⭐".repeat(t.rating)}
-

“{t.content}”

-
- {t.author} - {t.role} -
-
-
-
- {testimonials.map((item, i) => ( -
-
- )} - -
- {testimonials.map((item) => ( -
-
{item.avatar}
-
-
{"⭐".repeat(item.rating)}
-

“{item.content}”

-
- {item.author} - {item.role} -
-
-
- ))} -
- -
-
-

准备好开始你的旅居之旅了吗? 🌍

-

加入 50,000+ 数字游民社区,获取目的地指南、签证攻略与独家优惠

-
-
- - -
-
+
+
+
+ {C.tag} +

{C.title}

+

{C.subtitle}

-
- + + {cur && ( +
setPaused(true)} + onMouseLeave={() => setPaused(false)} + > +
+
{cur.avatar}
+
+
{"⭐".repeat(cur.rating)}
+

“{cur.content}”

+
+ {cur.author} + {cur.role} +
+
+
+
+ {testimonials.map((item, i) => ( +
+
+ )} + +
+ {testimonials.map((item) => ( +
+
{item.avatar}
+
+
{"⭐".repeat(item.rating)}
+

“{item.content}”

+
+ {item.author} + {item.role} +
+
+
+ ))} +
+ +
+
+

{C.ctaTitle}

+

{C.ctaDesc}

+
+ + {t.nav.community} + + + {t.nav.meetups} + +
+
+ +
+
+
); } diff --git a/frontend/src/components/HomeClient.tsx b/frontend/src/components/HomeClient.tsx index 643a732..5c43314 100644 --- a/frontend/src/components/HomeClient.tsx +++ b/frontend/src/components/HomeClient.tsx @@ -46,6 +46,8 @@ const NomadTips = lazy(() => import("./NomadTips")); const FeedbackWidget = lazy(() => import("./FeedbackWidget")); const OnboardingTour = lazy(() => import("./OnboardingTour")); const ToolsStrip = lazy(() => import("./ToolsStrip")); +const Lifestyle = lazy(() => import("./Lifestyle")); +const Community = lazy(() => import("./Community")); const KeyboardShortcuts = lazy(() => import("./KeyboardShortcuts")); const PwaInstallHint = lazy(() => import("./PwaInstallHint")); const RecentlyViewed = lazy(() => import("./RecentlyViewed")); @@ -166,6 +168,10 @@ export default function HomeClient(props: Props) { + + + +
@@ -176,6 +182,11 @@ export default function HomeClient(props: Props) { + {props.testimonials.length > 0 && ( + + + + )} diff --git a/frontend/src/components/Lifestyle.tsx b/frontend/src/components/Lifestyle.tsx index 5981145..2c64129 100644 --- a/frontend/src/components/Lifestyle.tsx +++ b/frontend/src/components/Lifestyle.tsx @@ -1,41 +1,59 @@ "use client"; import { useState } from "react"; +import Link from "next/link"; +import { useI18n } from "@/lib/i18n"; -const ITEMS = [ - { time: "🌅 07:00", icon: "🧘", title: "晨间仪式", desc: "瑜伽、冥想或海边跑步,用身心唤醒开启新的一天", tip: "推荐:清迈河边晨跑、巴厘岛海滩瑜伽" }, - { time: "☕ 09:00", icon: "💻", title: "深度工作", desc: "在 Co-working Space 或咖啡馆专注 4 小时,完成核心任务", tip: "推荐:Punspace 清迈、Outsite 里斯本" }, - { time: "🍜 13:00", icon: "🍽️", title: "本地探索", desc: "品尝地道美食,与当地人交流,感受文化碰撞", tip: "推荐:清迈夜市、巴塞罗那 Tapas 之旅" }, - { time: "🌇 17:00", icon: "📞", title: "跨时区协作", desc: "与全球团队视频会议,灵活安排跨时区沟通", tip: "工具:Calendly 排时区、World Time Buddy" }, - { time: "🌙 20:00", icon: "🎉", title: "社群社交", desc: "参加 Nomad Meetup,结识来自世界各地的同行者", tip: "平台:Nomad List Meetups、Facebook 群组" }, -]; +const ITEM_KEYS = ["morning", "work", "explore", "collab", "social"] as const; export default function Lifestyle() { + const { t } = useI18n(); + const L = t.lifestyle; const [active, setActive] = useState(0); + const items = ITEM_KEYS.map((key) => ({ + key, + time: L[`time_${key}` as keyof typeof L] as string, + icon: L[`icon_${key}` as keyof typeof L] as string, + title: L[`title_${key}` as keyof typeof L] as string, + desc: L[`desc_${key}` as keyof typeof L] as string, + tip: L[`tip_${key}` as keyof typeof L] as string, + })); + const cur = items[active]; return (
- 💻 NOMAD LIFE -

数字游民的一天

-

自由不等于散漫,高效工作才能尽情探索 — 点击卡片查看详情

+ {L.tag} +

{L.title}

+

{L.subtitle}

- + - + + +
- {ITEMS.map((item, i) => ( + {items.map((item, i) => (
setActive(i)} role="button" @@ -54,21 +72,30 @@ export default function Lifestyle() {
- {ITEMS[active].icon} - {ITEMS[active].time} -

{ITEMS[active].title}

-

{ITEMS[active].desc}

+ {cur.icon} + {cur.time} +

{cur.title}

+

{cur.desc}

💡 -

{ITEMS[active].tip}

+

{cur.tip}

+
+
+ + {L.ctaDay} + + + {L.ctaMeetups} +
- {ITEMS.map((_, i) => ( + {items.map((item, i) => (
diff --git a/frontend/src/components/StaticPage.tsx b/frontend/src/components/StaticPage.tsx index a8bf935..6f3b22b 100644 --- a/frontend/src/components/StaticPage.tsx +++ b/frontend/src/components/StaticPage.tsx @@ -1,13 +1,15 @@ "use client"; import Link from "next/link"; +import { useI18n } from "@/lib/i18n"; import type { StaticPageDef } from "@/lib/staticPages"; export default function StaticPage({ page }: { page: StaticPageDef }) { + const { t } = useI18n(); return (
{page.tag} @@ -19,15 +21,19 @@ export default function StaticPage({ page }: { page: StaticPageDef }) { {s.body &&

{s.body}

} {s.list && (
    - {s.list.map((item) =>
  • {item}
  • )} + {s.list.map((item) => ( +
  • {item}
  • + ))}
)}
))} {page.cta && page.cta.length > 0 && (
- {page.cta.map((c) => ( - {c.label} + {page.cta.map((c, i) => ( + + {c.label} + ))}
)} diff --git a/frontend/src/lib/i18n/dictionaries.ts b/frontend/src/lib/i18n/dictionaries.ts index 88e6e49..1d49cdd 100644 --- a/frontend/src/lib/i18n/dictionaries.ts +++ b/frontend/src/lib/i18n/dictionaries.ts @@ -147,6 +147,65 @@ export const zh = { sortCostDesc: "💰 价格从高到低", sortSpeed: "📶 网速排序", }, + lifestyle: { + tag: "💻 NOMAD LIFE", + title: "数字游民的一天", + subtitle: "自由不等于散漫,高效工作才能尽情探索 — 点击卡片查看详情", + ctaDay: "七天落地指南", + ctaMeetups: "去参加活动", + time_morning: "🌅 07:00", + icon_morning: "🧘", + title_morning: "晨间仪式", + desc_morning: "瑜伽、冥想或海边跑步,用身心唤醒开启新的一天", + tip_morning: "推荐:清迈河边晨跑、巴厘岛海滩瑜伽", + time_work: "☕ 09:00", + icon_work: "💻", + title_work: "深度工作", + desc_work: "在 Co-working Space 或咖啡馆专注 4 小时,完成核心任务", + tip_work: "推荐:Punspace 清迈、Outsite 里斯本", + time_explore: "🍜 13:00", + icon_explore: "🍽️", + title_explore: "本地探索", + desc_explore: "品尝地道美食,与当地人交流,感受文化碰撞", + tip_explore: "推荐:清迈夜市、巴塞罗那 Tapas 之旅", + time_collab: "🌇 17:00", + icon_collab: "📞", + title_collab: "跨时区协作", + desc_collab: "与全球团队视频会议,灵活安排跨时区沟通", + tip_collab: "工具:Calendly 排时区、World Time Buddy", + time_social: "🌙 20:00", + icon_social: "🎉", + title_social: "社群社交", + desc_social: "参加 Nomad Meetup,结识来自世界各地的同行者", + tip_social: "平台:nomadro 活动、同城社群", + }, + homeCommunity: { + tag: "🤝 COMMUNITY", + title: "游民心声", + subtitle: "来自全球数字游民的真实故事", + ctaTitle: "准备好开始你的旅居之旅了吗?", + ctaDesc: "订阅周报,或直接走进社区与活动", + }, + aboutPage: { + tag: "🌏 ABOUT", + title: "关于 nomadro", + subtitle: "用一行代码环游世界", + whoTitle: "我们是谁", + whoBody: + "nomadro 是一个面向数字游民的旅居决策平台。我们把目的地数据、签证信息、费用估算、时区协作与社区经验集中在一起,帮你更快做出「下一站去哪」的决定。", + whatTitle: "能做什么", + what1: "🗺️ 探索全球热门游民城市与对比工具", + what2: "🧮 计算生活成本、机票与启动金目标", + what3: "📋 签证匹配、保险与上网方案参考", + what4: "🗓️ 规划多城行程并分享给伙伴", + what5: "🗣️ 落地短语、行李清单与活动日历", + contactTitle: "联系我们", + contactBody: "合作与反馈:", + repoBody: "开源仓库:", + ctaExplore: "智能下一站", + ctaCommunity: "加入社区", + ctaPlan: "打开计划", + }, common: { backHome: "← 返回首页", browseDest: "浏览目的地", @@ -1483,6 +1542,65 @@ export const en: { [K in keyof typeof zh]: typeof zh[K] extends string ? string sortCostDesc: "💰 Cost high → low", sortSpeed: "📶 Wi‑Fi speed", }, + lifestyle: { + tag: "💻 NOMAD LIFE", + title: "A day in nomad life", + subtitle: "Freedom still needs rhythm — tap a card for detail", + ctaDay: "7-day landing guide", + ctaMeetups: "Find meetups", + time_morning: "🌅 07:00", + icon_morning: "🧘", + title_morning: "Morning ritual", + desc_morning: "Yoga, meditation, or a run by the water to wake body and mind", + tip_morning: "Try: riverside run in Chiang Mai, beach yoga in Bali", + time_work: "☕ 09:00", + icon_work: "💻", + title_work: "Deep work", + desc_work: "Four focused hours in a coworking space or café for core tasks", + tip_work: "Try: Punspace Chiang Mai, Outsite Lisbon", + time_explore: "🍜 13:00", + icon_explore: "🍽️", + title_explore: "Local explore", + desc_explore: "Eat local food, talk to people, feel the culture shift", + tip_explore: "Try: Chiang Mai night market, Barcelona tapas walk", + time_collab: "🌇 17:00", + icon_collab: "📞", + title_collab: "Cross-timezone sync", + desc_collab: "Video with a global team — schedule around the clock", + tip_collab: "Tools: Calendly time zones, World Time Buddy", + time_social: "🌙 20:00", + icon_social: "🎉", + title_social: "Community night", + desc_social: "Join a nomad meetup and meet people from everywhere", + tip_social: "Platforms: nomadro events, local groups", + }, + homeCommunity: { + tag: "🤝 COMMUNITY", + title: "Nomad voices", + subtitle: "Real stories from digital nomads worldwide", + ctaTitle: "Ready for your next move?", + ctaDesc: "Subscribe to the weekly brief, or jump into community and events", + }, + aboutPage: { + tag: "🌏 ABOUT", + title: "About nomadro", + subtitle: "Travel the world with one line of code", + whoTitle: "Who we are", + whoBody: + "nomadro is a move-decision platform for digital nomads. We bring destination data, visas, cost estimates, timezone collaboration, and community experience into one place so you can decide where to go next faster.", + whatTitle: "What you can do", + what1: "🗺️ Explore top nomad cities and comparison tools", + what2: "🧮 Estimate living costs, flights, and startup runway", + what3: "📋 Visa matching, insurance, and connectivity references", + what4: "🗓️ Plan multi-city trips and share with partners", + what5: "🗣️ Landing phrases, packing lists, and event calendars", + contactTitle: "Contact", + contactBody: "Partnerships & feedback:", + repoBody: "Open source:", + ctaExplore: "Smart next stop", + ctaCommunity: "Join community", + ctaPlan: "Open plan", + }, common: { backHome: "← Back home", browseDest: "Destinations",