'grok建议功能'
This commit is contained in:
81
app/[locale]/ai/page.tsx
Normal file
81
app/[locale]/ai/page.tsx
Normal file
@@ -0,0 +1,81 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "@/i18n/navigation";
|
||||
import Footer from "@/app/components/Footer";
|
||||
|
||||
export default function AIPage() {
|
||||
const { t } = useTranslation("ai");
|
||||
const [query, setQuery] = useState("");
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const exampleQueries = [
|
||||
"我预算每月800刀,想亚洲热带,有稳定网,适合freelance写作,推荐城市+理由",
|
||||
"帮我写下一站介绍文案",
|
||||
"生成3月清迈行程计划",
|
||||
];
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!query.trim()) return;
|
||||
setLoading(true);
|
||||
setTimeout(() => setLoading(false), 1500);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-[#fafafa] dark:bg-gray-950">
|
||||
<div className="max-w-2xl mx-auto px-4 sm:px-6 py-12 sm:py-16">
|
||||
<div className="text-center mb-10">
|
||||
<h1 className="text-2xl sm:text-3xl font-bold text-gray-900 dark:text-gray-100">
|
||||
{t("title")}
|
||||
</h1>
|
||||
<p className="mt-2 text-gray-500 dark:text-gray-400">
|
||||
{t("subtitle")}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="rounded-2xl bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 p-6">
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<textarea
|
||||
value={query}
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
placeholder={t("placeholder")}
|
||||
rows={4}
|
||||
className="w-full rounded-xl border border-gray-200 dark:border-gray-700 px-4 py-3 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 placeholder-gray-400 resize-none focus:outline-none focus:ring-2 focus:ring-[#ff4d4f]/20 focus:border-[#ff4d4f]"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading || !query.trim()}
|
||||
className="w-full py-3 rounded-xl bg-[#ff4d4f] text-white font-medium hover:bg-[#ff3333] disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||||
>
|
||||
{loading ? t("thinking") : t("ask")}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div className="mt-6 pt-6 border-t border-gray-100 dark:border-gray-800">
|
||||
<p className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-3">
|
||||
{t("examples")}
|
||||
</p>
|
||||
<div className="space-y-2">
|
||||
{exampleQueries.map((q, i) => (
|
||||
<button
|
||||
key={i}
|
||||
type="button"
|
||||
onClick={() => setQuery(q)}
|
||||
className="block w-full text-left px-4 py-3 rounded-xl bg-gray-50 dark:bg-gray-800 text-sm text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 hover:text-gray-900 dark:hover:text-gray-100 transition-colors"
|
||||
>
|
||||
{q}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="mt-6 text-center text-xs text-gray-400 dark:text-gray-500">
|
||||
{t("comingSoon")}
|
||||
</p>
|
||||
</div>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
217
app/[locale]/dashboard/page.tsx
Normal file
217
app/[locale]/dashboard/page.tsx
Normal file
@@ -0,0 +1,217 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useMemo } from "react";
|
||||
import { useTranslation } from "@/i18n/navigation";
|
||||
import { Link } from "@/i18n/navigation";
|
||||
import { cities } from "@/app/data/cities";
|
||||
import CityCard from "@/app/components/CityCard";
|
||||
import CityModal from "@/app/components/CityModal";
|
||||
import Footer from "@/app/components/Footer";
|
||||
import type { City } from "@/app/data/cities";
|
||||
|
||||
const TAGS = ["远程工作者", "创业者", "设计师", "开发者", "内容创作者", "带宠物"];
|
||||
const TIMEZONES = ["UTC+8 中国", "UTC+7 东南亚", "UTC+1 欧洲", "UTC-5 美东", "UTC-8 美西", "任意"];
|
||||
const VISA_OPTIONS = ["免签优先", "落地签可", "需提前办", "任意"];
|
||||
|
||||
export default function DashboardPage() {
|
||||
const { t } = useTranslation("dashboard");
|
||||
const [budget, setBudget] = useState(5000);
|
||||
const [internet, setInternet] = useState(50);
|
||||
const [timezone, setTimezone] = useState("UTC+8 中国");
|
||||
const [visa, setVisa] = useState("任意");
|
||||
const [selectedTags, setSelectedTags] = useState<string[]>([]);
|
||||
const [cityModal, setCityModal] = useState<City | null>(null);
|
||||
|
||||
const filteredCities = useMemo(() => {
|
||||
return [...cities]
|
||||
.filter((c) => c.costPerMonth <= budget * 1.2 && c.internetSpeed >= internet)
|
||||
.sort((a, b) => b.overallScore - a.overallScore)
|
||||
.slice(0, 12);
|
||||
}, [budget, internet]);
|
||||
|
||||
const toggleTag = (tag: string) => {
|
||||
setSelectedTags((prev) =>
|
||||
prev.includes(tag) ? prev.filter((t) => t !== tag) : [...prev, tag]
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-[#fafafa] dark:bg-gray-950">
|
||||
<div className="max-w-6xl mx-auto px-4 sm:px-6 py-8 sm:py-12">
|
||||
<div className="mb-8">
|
||||
<h1 className="text-2xl sm:text-3xl font-bold text-gray-900 dark:text-gray-100">
|
||||
{t("title")}
|
||||
</h1>
|
||||
<p className="mt-1 text-gray-500 dark:text-gray-400">
|
||||
{t("subtitle")}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||
<aside className="lg:col-span-1 space-y-6">
|
||||
<div className="rounded-2xl bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 p-4 sm:p-6">
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
{t("budget")}
|
||||
</label>
|
||||
<input
|
||||
type="range"
|
||||
min="2000"
|
||||
max="15000"
|
||||
step="500"
|
||||
value={budget}
|
||||
onChange={(e) => setBudget(Number(e.target.value))}
|
||||
className="w-full accent-[#ff4d4f]"
|
||||
/>
|
||||
<p className="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
||||
¥{budget.toLocaleString()}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="rounded-2xl bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 p-4 sm:p-6">
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
{t("timezone")}
|
||||
</label>
|
||||
<select
|
||||
value={timezone}
|
||||
onChange={(e) => setTimezone(e.target.value)}
|
||||
className="w-full rounded-lg border border-gray-200 dark:border-gray-700 px-4 py-2 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100"
|
||||
>
|
||||
{TIMEZONES.map((z) => (
|
||||
<option key={z} value={z}>{z}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="rounded-2xl bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 p-4 sm:p-6">
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
{t("visa")}
|
||||
</label>
|
||||
<select
|
||||
value={visa}
|
||||
onChange={(e) => setVisa(e.target.value)}
|
||||
className="w-full rounded-lg border border-gray-200 dark:border-gray-700 px-4 py-2 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100"
|
||||
>
|
||||
{VISA_OPTIONS.map((v) => (
|
||||
<option key={v} value={v}>{v}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="rounded-2xl bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 p-4 sm:p-6">
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
{t("internet")}
|
||||
</label>
|
||||
<input
|
||||
type="range"
|
||||
min="30"
|
||||
max="150"
|
||||
step="10"
|
||||
value={internet}
|
||||
onChange={(e) => setInternet(Number(e.target.value))}
|
||||
className="w-full accent-[#ff4d4f]"
|
||||
/>
|
||||
<p className="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
||||
{internet} Mbps
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="rounded-2xl bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 p-4 sm:p-6">
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-3">
|
||||
{t("tags")}
|
||||
</label>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{TAGS.map((tag) => (
|
||||
<button
|
||||
key={tag}
|
||||
onClick={() => toggleTag(tag)}
|
||||
className={`px-3 py-1.5 rounded-full text-xs font-medium transition-colors ${
|
||||
selectedTags.includes(tag)
|
||||
? "bg-[#ff4d4f] text-white"
|
||||
: "bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-700"
|
||||
}`}
|
||||
>
|
||||
{tag}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="rounded-2xl bg-gradient-to-br from-amber-50 to-orange-50 dark:from-amber-950/30 dark:to-orange-950/30 border border-amber-200 dark:border-amber-800/50 p-4">
|
||||
<p className="text-sm font-medium text-amber-800 dark:text-amber-200">
|
||||
🤖 {t("aiTip")}
|
||||
</p>
|
||||
<p className="mt-1 text-xs text-amber-700 dark:text-amber-300">
|
||||
你和 80% 大理用户重合度高,推荐 3 月去清迈…
|
||||
</p>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<main className="lg:col-span-2">
|
||||
<div className="rounded-2xl bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 p-4 sm:p-6 mb-6">
|
||||
<h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4">
|
||||
{t("search")}
|
||||
</h2>
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3 gap-3" style={{ gridAutoRows: "160px" }}>
|
||||
{filteredCities.map((city, i) => (
|
||||
<div key={city.id} className="h-full min-h-[140px]">
|
||||
<CityCard
|
||||
city={city}
|
||||
rank={i + 1}
|
||||
onClick={() => setCityModal(city)}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{filteredCities.length === 0 && (
|
||||
<p className="text-center py-8 text-gray-500 dark:text-gray-400">
|
||||
{t("noMatch")}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div className="rounded-2xl bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 p-4 h-40 flex items-center justify-center">
|
||||
<span className="text-gray-400 dark:text-gray-500 text-sm">
|
||||
{t("costCurve")} {t("devSuffix")}
|
||||
</span>
|
||||
</div>
|
||||
<div className="rounded-2xl bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 p-4 h-40 flex items-center justify-center">
|
||||
<span className="text-gray-400 dark:text-gray-500 text-sm">
|
||||
{t("nomadHeatmap")} {t("devSuffix")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 rounded-2xl bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 p-4">
|
||||
<h3 className="font-medium text-gray-900 dark:text-gray-100 mb-2">
|
||||
{t("nearbyEvents")}
|
||||
</h3>
|
||||
<div className="flex gap-2 overflow-x-auto pb-2">
|
||||
{[
|
||||
{ city: "大理", date: "3月9日", emoji: "🏯" },
|
||||
{ city: "成都", date: "3月12日", emoji: "🐼" },
|
||||
{ city: "深圳", date: "3月14日", emoji: "🌃" },
|
||||
].map((e) => (
|
||||
<Link
|
||||
key={e.city}
|
||||
href="/meetups"
|
||||
className="shrink-0 px-4 py-2 rounded-xl bg-gray-100 dark:bg-gray-800 text-sm font-medium text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-700"
|
||||
>
|
||||
{e.emoji} {e.city} {e.date}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<CityModal
|
||||
city={cityModal}
|
||||
isOpen={!!cityModal}
|
||||
onClose={() => setCityModal(null)}
|
||||
/>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "@/i18n/navigation";
|
||||
import Footer from "@/app/components/Footer";
|
||||
|
||||
type TabType = "friends" | "dating";
|
||||
type TabType = "friends" | "dating" | "partner" | "roommate" | "cofounder" | "explore";
|
||||
|
||||
interface Profile {
|
||||
id: string;
|
||||
@@ -44,7 +46,10 @@ const MOCK_MATCHES: MatchCard[] = [
|
||||
{ id: "m5", name: "赵梦琪", location: "厦门", timeAgo: "1年前", initial: "赵", color: "bg-rose-400", photo: "https://i.pravatar.cc/150?img=41" },
|
||||
];
|
||||
|
||||
const TAB_ORDER: TabType[] = ["friends", "dating", "partner", "roommate", "cofounder", "explore"];
|
||||
|
||||
export default function DatingPage() {
|
||||
const { t } = useTranslation("match");
|
||||
const [activeTab, setActiveTab] = useState<TabType>("friends");
|
||||
const [currentIndex, setCurrentIndex] = useState(0);
|
||||
|
||||
@@ -59,36 +64,41 @@ export default function DatingPage() {
|
||||
setCurrentIndex((i) => (hasMoreProfiles ? i + 1 : 0));
|
||||
};
|
||||
|
||||
const badgeText = activeTab === "dating" ? "你们都在寻找 🌹 约会" : "你们都在寻找 🤝 交友";
|
||||
const badgeMap: Record<TabType, string> = {
|
||||
friends: t("badgeFriends"),
|
||||
dating: t("badgeDating"),
|
||||
partner: t("badgePartner"),
|
||||
roommate: t("badgeRoommate"),
|
||||
cofounder: t("badgeCofounder"),
|
||||
explore: t("badgeExplore"),
|
||||
};
|
||||
const badgeText = badgeMap[activeTab];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-[#fafafa] dark:bg-gray-950">
|
||||
<div className="flex justify-center pt-6 pb-4 px-4">
|
||||
<div className="inline-flex rounded-full bg-white dark:bg-gray-900 p-1 shadow-sm border border-gray-100 dark:border-gray-800">
|
||||
<button
|
||||
onClick={() => setActiveTab("friends")}
|
||||
className={`px-6 py-2.5 rounded-full text-sm font-medium transition-all duration-200 ${
|
||||
activeTab === "friends" ? "bg-[#ff4d4f] text-white shadow-sm" : "text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100"
|
||||
}`}
|
||||
>
|
||||
交友
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab("dating")}
|
||||
className={`px-6 py-2.5 rounded-full text-sm font-medium transition-all duration-200 ${
|
||||
activeTab === "dating" ? "bg-[#ff4d4f] text-white shadow-sm" : "text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100"
|
||||
}`}
|
||||
>
|
||||
约会
|
||||
</button>
|
||||
<div className="flex flex-wrap justify-center gap-2 max-w-2xl">
|
||||
{TAB_ORDER.map((tab) => (
|
||||
<button
|
||||
key={tab}
|
||||
onClick={() => setActiveTab(tab)}
|
||||
className={`px-4 py-2 rounded-full text-xs sm:text-sm font-medium transition-all duration-200 ${
|
||||
activeTab === tab
|
||||
? "bg-[#ff4d4f] text-white shadow-sm"
|
||||
: "bg-white dark:bg-gray-900 text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 border border-gray-100 dark:border-gray-800"
|
||||
}`}
|
||||
>
|
||||
{t(tab)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col md:flex-row gap-6 max-w-7xl mx-auto px-4 pb-12">
|
||||
<aside className="hidden md:block w-72 flex-shrink-0">
|
||||
<div className="sticky top-24">
|
||||
<h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-1">约会匹配 💕</h2>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400 mb-4">这些人喜欢了你</p>
|
||||
<h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-1">{t("title")} 💕</h2>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400 mb-4">{t("likedYou")}</p>
|
||||
<div className="space-y-3">
|
||||
{MOCK_MATCHES.map((match) => (
|
||||
<div key={match.id} className="flex items-center gap-3 p-3 rounded-xl bg-white dark:bg-gray-900 border border-gray-100 dark:border-gray-800 shadow-sm hover:shadow-md transition-shadow cursor-pointer">
|
||||
@@ -109,7 +119,7 @@ export default function DatingPage() {
|
||||
<div className={`relative rounded-2xl overflow-hidden bg-gradient-to-br ${currentProfile.gradient} p-8 pb-6 shadow-xl border border-white/20`}>
|
||||
<div className="absolute top-4 right-4 flex flex-col items-end gap-1">
|
||||
<span className="px-3 py-1.5 rounded-full bg-white/90 text-gray-800 text-xs font-medium backdrop-blur-sm">{badgeText}</span>
|
||||
<a href="#" className="text-[#ff4d4f] hover:text-red-600 text-sm font-medium">举报资料</a>
|
||||
<a href="#" className="text-[#ff4d4f] hover:text-red-600 text-sm font-medium">{t("report")}</a>
|
||||
</div>
|
||||
<div className="flex justify-center mt-12 mb-6">
|
||||
<img src={currentProfile.photo} alt={currentProfile.name} className="w-28 h-28 rounded-full shadow-lg border-4 border-white/50 object-cover" />
|
||||
@@ -119,7 +129,7 @@ export default function DatingPage() {
|
||||
<p className="text-white/90 text-sm mt-1">📍 {currentProfile.location}</p>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<p className="text-white/90 text-xs font-medium mb-2">匹配标签:</p>
|
||||
<p className="text-white/90 text-xs font-medium mb-2">{t("matchTags")}:</p>
|
||||
<div className="flex flex-wrap gap-2 justify-center">
|
||||
{currentProfile.tags.map((tag) => (
|
||||
<span key={tag} className="px-3 py-1 rounded-full bg-white/80 text-gray-800 text-xs font-medium">{tag}</span>
|
||||
@@ -129,16 +139,16 @@ export default function DatingPage() {
|
||||
<p className="text-white/95 text-sm leading-relaxed text-center mb-8 px-2">{currentProfile.bio}</p>
|
||||
<div className="flex items-center justify-center gap-8">
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<button onClick={handleDislike} className="w-16 h-16 rounded-full bg-white shadow-lg flex items-center justify-center text-[#ff4d4f] hover:bg-red-50 hover:scale-105 active:scale-95 transition-all" aria-label="不感兴趣">
|
||||
<button onClick={handleDislike} className="w-16 h-16 rounded-full bg-white shadow-lg flex items-center justify-center text-[#ff4d4f] hover:bg-red-50 hover:scale-105 active:scale-95 transition-all" aria-label={t("dislike")}>
|
||||
<span className="text-2xl">✕</span>
|
||||
</button>
|
||||
<span className="text-xs text-white/80 font-medium">不感兴趣</span>
|
||||
<span className="text-xs text-white/80 font-medium">{t("dislike")}</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<button onClick={handleLike} className="w-16 h-16 rounded-full bg-[#22c55e] shadow-lg flex items-center justify-center text-white hover:bg-[#16a34a] hover:scale-105 active:scale-95 transition-all" aria-label="喜欢">
|
||||
<button onClick={handleLike} className="w-16 h-16 rounded-full bg-[#22c55e] shadow-lg flex items-center justify-center text-white hover:bg-[#16a34a] hover:scale-105 active:scale-95 transition-all" aria-label={t("like")}>
|
||||
<span className="text-2xl">💚</span>
|
||||
</button>
|
||||
<span className="text-xs text-white/80 font-medium">喜欢</span>
|
||||
<span className="text-xs text-white/80 font-medium">{t("like")}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -147,6 +157,7 @@ export default function DatingPage() {
|
||||
</main>
|
||||
<div className="hidden lg:block w-72 flex-shrink-0" />
|
||||
</div>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
63
app/[locale]/gigs/page.tsx
Normal file
63
app/[locale]/gigs/page.tsx
Normal file
@@ -0,0 +1,63 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslation } from "@/i18n/navigation";
|
||||
import Footer from "@/app/components/Footer";
|
||||
|
||||
const MOCK_GIGS = [
|
||||
{ id: "1", title: "帮我 P 图", city: "大理", reward: "¥50", author: "林**" },
|
||||
{ id: "2", title: "找惠州 loft 民宿", city: "深圳", reward: "¥100", author: "陈**" },
|
||||
{ id: "3", title: "代收快递", city: "成都", reward: "¥30", author: "王**" },
|
||||
{ id: "4", title: "一起 citywalk 拍 neo2 航拍", city: "杭州", reward: "面议", author: "张**" },
|
||||
{ id: "5", title: "翻译英文文档", city: "上海", reward: "¥200", author: "李**" },
|
||||
];
|
||||
|
||||
export default function GigsPage() {
|
||||
const { t } = useTranslation("gigs");
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-[#fafafa] dark:bg-gray-950">
|
||||
<div className="max-w-4xl mx-auto px-4 sm:px-6 py-12">
|
||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-8">
|
||||
<div>
|
||||
<h1 className="text-2xl sm:text-3xl font-bold text-gray-900 dark:text-gray-100">
|
||||
{t("title")}
|
||||
</h1>
|
||||
<p className="mt-1 text-gray-500 dark:text-gray-400">
|
||||
{t("subtitle")} · {t("platformFee")}
|
||||
</p>
|
||||
</div>
|
||||
<button className="shrink-0 px-6 py-3 rounded-xl bg-[#ff4d4f] text-white font-medium hover:bg-[#ff3333] transition-colors">
|
||||
{t("publish")}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
{MOCK_GIGS.map((gig) => (
|
||||
<div
|
||||
key={gig.id}
|
||||
className="rounded-2xl bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 p-4 sm:p-5 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3"
|
||||
>
|
||||
<div className="flex-1 min-w-0">
|
||||
<h3 className="font-medium text-gray-900 dark:text-gray-100">
|
||||
{gig.title}
|
||||
</h3>
|
||||
<p className="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
||||
📍 {gig.city} · {gig.author}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-lg font-semibold text-[#ff4d4f]">
|
||||
{gig.reward}
|
||||
</span>
|
||||
<button className="px-4 py-2 rounded-lg bg-gray-100 dark:bg-gray-800 text-sm font-medium text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-700">
|
||||
{t("browse")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -8,9 +8,9 @@ import Navbar from "../components/Navbar";
|
||||
const LOCALES: Locale[] = ["zh", "en"];
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "游牧中国 - 数字游民的理想目的地",
|
||||
title: "游牧中国 NomadCNA - 数字游民生活操作系统",
|
||||
description:
|
||||
"发现全球最适合远程工作和生活的城市,加入中国数字游民社区,探索远程办公新方式",
|
||||
"数字游民的一站式生活操作系统:城市决策、税务签证、智能匹配、赏金任务。从 Free 到 Pro,开启你的游牧人生",
|
||||
};
|
||||
|
||||
export function generateStaticParams() {
|
||||
|
||||
109
app/[locale]/pricing/page.tsx
Normal file
109
app/[locale]/pricing/page.tsx
Normal file
@@ -0,0 +1,109 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "@/i18n/navigation";
|
||||
import { Link } from "@/i18n/navigation";
|
||||
import Footer from "@/app/components/Footer";
|
||||
|
||||
const PLANS = [
|
||||
{ id: "free", key: "free", price: 0, priceYear: 0, features: ["browse"], highlight: false },
|
||||
{ id: "basic", key: "basic", price: 39, priceYear: 390, features: ["browse", "noAds", "advancedFilter", "unlimitedSwipe", "priorityEvent"], highlight: true },
|
||||
{ id: "pro", key: "pro", price: 149, priceYear: 1490, features: ["browse", "noAds", "advancedFilter", "unlimitedSwipe", "priorityEvent", "crmRoster", "oneOnOne", "gigAccess", "dashboard"], highlight: false },
|
||||
] as const;
|
||||
|
||||
export default function PricingPage() {
|
||||
const { t } = useTranslation("pricing");
|
||||
const [yearly, setYearly] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-[#fafafa] dark:bg-gray-950">
|
||||
<div className="max-w-6xl mx-auto px-4 sm:px-6 py-12 sm:py-16">
|
||||
<div className="text-center mb-12">
|
||||
<h1 className="text-2xl sm:text-3xl md:text-4xl font-bold text-gray-900 dark:text-gray-100">
|
||||
{t("title")}
|
||||
</h1>
|
||||
<p className="mt-2 text-gray-500 dark:text-gray-400">
|
||||
{t("subtitle")}
|
||||
</p>
|
||||
<div className="mt-6 flex items-center justify-center gap-3">
|
||||
<span className={`text-sm font-medium ${!yearly ? "text-gray-900 dark:text-gray-100" : "text-gray-500 dark:text-gray-400"}`}>
|
||||
{t("monthly")}
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setYearly(!yearly)}
|
||||
className={`relative w-12 h-6 rounded-full transition-colors ${yearly ? "bg-[#ff4d4f]" : "bg-gray-200 dark:bg-gray-700"}`}
|
||||
>
|
||||
<span className={`absolute top-1 w-4 h-4 rounded-full bg-white shadow transition-transform ${yearly ? "left-7" : "left-1"}`} />
|
||||
</button>
|
||||
<span className={`text-sm font-medium ${yearly ? "text-gray-900 dark:text-gray-100" : "text-gray-500 dark:text-gray-400"}`}>
|
||||
{t("yearly")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 lg:gap-8">
|
||||
{PLANS.map((plan) => {
|
||||
const displayPrice = yearly ? plan.priceYear : plan.price;
|
||||
const priceKey = yearly ? "perYear" : "perMonth";
|
||||
return (
|
||||
<div
|
||||
key={plan.id}
|
||||
className={`rounded-2xl border p-6 sm:p-8 flex flex-col ${
|
||||
plan.highlight
|
||||
? "border-[#ff4d4f] bg-white dark:bg-gray-900 shadow-xl dark:shadow-none ring-2 ring-[#ff4d4f]/20"
|
||||
: "border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900"
|
||||
}`}
|
||||
>
|
||||
<h2 className="text-xl font-bold text-gray-900 dark:text-gray-100">
|
||||
{t(plan.key)}
|
||||
</h2>
|
||||
<div className="mt-4 flex items-baseline gap-1">
|
||||
<span className="text-3xl sm:text-4xl font-bold text-gray-900 dark:text-gray-100">
|
||||
{displayPrice === 0 ? "¥0" : `¥${displayPrice.toLocaleString()}`}
|
||||
</span>
|
||||
{displayPrice > 0 && (
|
||||
<span className="text-gray-500 dark:text-gray-400">
|
||||
{t(priceKey)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{yearly && plan.priceYear > 0 && (
|
||||
<p className="mt-1 text-xs text-green-600 dark:text-green-400">
|
||||
省 ¥{(plan.price * 12 - plan.priceYear).toLocaleString()}
|
||||
</p>
|
||||
)}
|
||||
<ul className="mt-6 space-y-3 flex-1">
|
||||
{plan.features.map((f) => (
|
||||
<li
|
||||
key={f}
|
||||
className="flex items-center gap-2 text-sm text-gray-600 dark:text-gray-300"
|
||||
>
|
||||
<span className="text-green-500">✓</span>
|
||||
{t(`features.${f}`)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<Link
|
||||
href="/join"
|
||||
className={`mt-6 w-full py-3 rounded-lg text-center font-medium transition-colors ${
|
||||
plan.highlight
|
||||
? "bg-[#ff4d4f] text-white hover:bg-[#ff3333]"
|
||||
: "bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-gray-100 hover:bg-gray-200 dark:hover:bg-gray-700"
|
||||
}`}
|
||||
>
|
||||
{plan.id === "free" ? t("getStarted") : t("upgrade")}
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<p className="mt-8 text-center text-sm text-gray-500 dark:text-gray-400">
|
||||
{t("features.experienceCamp")}
|
||||
</p>
|
||||
</div>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
79
app/[locale]/report/page.tsx
Normal file
79
app/[locale]/report/page.tsx
Normal file
@@ -0,0 +1,79 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "@/i18n/navigation";
|
||||
import Footer from "@/app/components/Footer";
|
||||
|
||||
export default function ReportPage() {
|
||||
const { t } = useTranslation("report");
|
||||
const [generated, setGenerated] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-[#fafafa] dark:bg-gray-950">
|
||||
<div className="max-w-2xl mx-auto px-4 sm:px-6 py-12">
|
||||
<div className="text-center mb-10">
|
||||
<h1 className="text-2xl sm:text-3xl font-bold text-gray-900 dark:text-gray-100">
|
||||
{t("title")}
|
||||
</h1>
|
||||
<p className="mt-2 text-gray-500 dark:text-gray-400">
|
||||
{t("subtitle")}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{!generated ? (
|
||||
<div className="rounded-2xl bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 p-8 text-center">
|
||||
<div className="w-20 h-20 mx-auto rounded-full bg-gradient-to-br from-amber-400 to-orange-500 flex items-center justify-center text-3xl mb-6">
|
||||
📊
|
||||
</div>
|
||||
<p className="text-gray-600 dark:text-gray-400 mb-6">
|
||||
{t("desc")}
|
||||
</p>
|
||||
<button
|
||||
onClick={() => setGenerated(true)}
|
||||
className="px-6 py-3 rounded-xl bg-[#ff4d4f] text-white font-medium hover:bg-[#ff3333] transition-colors"
|
||||
>
|
||||
{t("generate")}
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="rounded-2xl bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 overflow-hidden">
|
||||
<div className="h-2 bg-gradient-to-r from-amber-400 via-orange-500 to-red-500" />
|
||||
<div className="p-6 sm:p-8">
|
||||
<h2 className="text-xl font-bold text-gray-900 dark:text-gray-100 mb-6">
|
||||
{t("yearTitle")}
|
||||
</h2>
|
||||
<div className="grid grid-cols-2 gap-4 mb-6">
|
||||
<div className="p-4 rounded-xl bg-gray-50 dark:bg-gray-800">
|
||||
<p className="text-2xl font-bold text-[#ff4d4f]">8</p>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">{t("countries")}</p>
|
||||
</div>
|
||||
<div className="p-4 rounded-xl bg-gray-50 dark:bg-gray-800">
|
||||
<p className="text-2xl font-bold text-[#ff4d4f]">¥5,200</p>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">{t("avgCost")}</p>
|
||||
</div>
|
||||
<div className="p-4 rounded-xl bg-gray-50 dark:bg-gray-800">
|
||||
<p className="text-2xl font-bold text-[#ff4d4f]">12</p>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">{t("coworkingCheckins")}</p>
|
||||
</div>
|
||||
<div className="p-4 rounded-xl bg-gray-50 dark:bg-gray-800">
|
||||
<p className="text-2xl font-bold text-[#ff4d4f]">3</p>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">{t("meetups")}</p>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-xs text-gray-400 dark:text-gray-500 text-center">
|
||||
{t("watermark")}
|
||||
</p>
|
||||
<button
|
||||
onClick={() => setGenerated(false)}
|
||||
className="mt-4 w-full py-2 rounded-lg border border-gray-200 dark:border-gray-700 text-sm text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800"
|
||||
>
|
||||
{t("regenerate")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
131
app/[locale]/tools/page.tsx
Normal file
131
app/[locale]/tools/page.tsx
Normal file
@@ -0,0 +1,131 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "@/i18n/navigation";
|
||||
import Footer from "@/app/components/Footer";
|
||||
|
||||
const RATES = [
|
||||
{ from: "CNY", to: "USD", rate: 0.14 },
|
||||
{ from: "CNY", to: "EUR", rate: 0.13 },
|
||||
{ from: "CNY", to: "THB", rate: 5.2 },
|
||||
{ from: "USD", to: "CNY", rate: 7.2 },
|
||||
];
|
||||
|
||||
export default function ToolsPage() {
|
||||
const { t } = useTranslation("tools");
|
||||
const [income, setIncome] = useState(100000);
|
||||
const [cnyAmount, setCnyAmount] = useState(10000);
|
||||
const [visaDate, setVisaDate] = useState("2025-06-15");
|
||||
|
||||
const taxEstimate = Math.round(income * 0.15);
|
||||
const cnyToUsd = (v: number) => (v * 0.14).toFixed(2);
|
||||
const daysLeft = Math.ceil(
|
||||
(new Date(visaDate).getTime() - Date.now()) / (1000 * 60 * 60 * 24)
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-[#fafafa] dark:bg-gray-950">
|
||||
<div className="max-w-4xl mx-auto px-4 sm:px-6 py-12">
|
||||
<div className="mb-10">
|
||||
<h1 className="text-2xl sm:text-3xl font-bold text-gray-900 dark:text-gray-100">
|
||||
{t("title")}
|
||||
</h1>
|
||||
<p className="mt-1 text-gray-500 dark:text-gray-400">
|
||||
{t("subtitle")}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
<section className="rounded-2xl bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 p-6">
|
||||
<h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4">
|
||||
💱 {t("exchange")}
|
||||
</h2>
|
||||
<div className="grid grid-cols-2 sm:grid-cols-4 gap-4">
|
||||
{RATES.map((r) => (
|
||||
<div
|
||||
key={`${r.from}-${r.to}`}
|
||||
className="p-4 rounded-xl bg-gray-50 dark:bg-gray-800"
|
||||
>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">
|
||||
{r.from} → {r.to}
|
||||
</p>
|
||||
<p className="mt-1 text-lg font-bold text-gray-900 dark:text-gray-100">
|
||||
{r.rate}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="mt-4 p-4 rounded-xl bg-gray-50 dark:bg-gray-800">
|
||||
<label className="block text-xs text-gray-500 dark:text-gray-400 mb-2">CNY → USD</label>
|
||||
<div className="flex gap-2 items-center">
|
||||
<input
|
||||
type="number"
|
||||
value={cnyAmount}
|
||||
onChange={(e) => setCnyAmount(Number(e.target.value) || 0)}
|
||||
className="flex-1 rounded-lg border border-gray-200 dark:border-gray-700 px-3 py-2 text-sm bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100"
|
||||
/>
|
||||
<span className="text-gray-400">≈</span>
|
||||
<span className="font-semibold text-gray-900 dark:text-gray-100">${cnyToUsd(cnyAmount)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<p className="mt-3 text-xs text-gray-400 dark:text-gray-500">
|
||||
{t("disclaimer")}
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="rounded-2xl bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 p-6">
|
||||
<h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4">
|
||||
📊 {t("tax")}
|
||||
</h2>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
{t("incomeCountry")} (年收入 ¥)
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
value={income}
|
||||
onChange={(e) => setIncome(Number(e.target.value))}
|
||||
className="w-full rounded-lg border border-gray-200 dark:border-gray-700 px-4 py-2 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100"
|
||||
/>
|
||||
</div>
|
||||
<div className="p-4 rounded-xl bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800/50">
|
||||
<p className="text-sm text-amber-800 dark:text-amber-200">
|
||||
{t("estimate")}: 约 ¥{taxEstimate.toLocaleString()}/年
|
||||
</p>
|
||||
<p className="mt-1 text-xs text-amber-600 dark:text-amber-300">
|
||||
{t("taxDisclaimer")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="rounded-2xl bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 p-6">
|
||||
<h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4">
|
||||
🛂 {t("visa")}
|
||||
</h2>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
{t("visaExpiry")}
|
||||
</label>
|
||||
<input
|
||||
type="date"
|
||||
value={visaDate}
|
||||
onChange={(e) => setVisaDate(e.target.value)}
|
||||
className="w-full rounded-lg border border-gray-200 dark:border-gray-700 px-4 py-2 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100"
|
||||
/>
|
||||
</div>
|
||||
<div className="p-4 rounded-xl bg-emerald-50 dark:bg-emerald-900/20 border border-emerald-200 dark:border-emerald-800/50">
|
||||
<p className="text-lg font-semibold text-emerald-800 dark:text-emerald-200">
|
||||
{daysLeft > 0 ? `${daysLeft} ${t("daysLeft")}` : t("expired")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -15,8 +15,9 @@ const footerSections = [
|
||||
{
|
||||
titleKey: "tools" as const,
|
||||
links: [
|
||||
{ labelKey: "climate" as const, href: "#" },
|
||||
{ labelKey: "cost" as const, href: "#" },
|
||||
{ labelKey: "dashboard" as const, href: "/dashboard" },
|
||||
{ labelKey: "cost" as const, href: "/tools" },
|
||||
{ labelKey: "ai" as const, href: "/ai" },
|
||||
{ labelKey: "coworking" as const, href: "#" },
|
||||
{ labelKey: "compare" as const, href: "#" },
|
||||
],
|
||||
@@ -27,6 +28,7 @@ const footerSections = [
|
||||
{ labelKey: "guide" as const, href: "#" },
|
||||
{ labelKey: "cityGuide" as const, href: "#" },
|
||||
{ labelKey: "stories" as const, href: "#" },
|
||||
{ labelKey: "report" as const, href: "/report" },
|
||||
{ labelKey: "faq" as const, href: "#" },
|
||||
],
|
||||
},
|
||||
@@ -34,6 +36,8 @@ const footerSections = [
|
||||
titleKey: "about" as const,
|
||||
links: [
|
||||
{ labelKey: "aboutUs" as const, href: "/about" },
|
||||
{ labelKey: "pricing" as const, href: "/pricing" },
|
||||
{ labelKey: "gigs" as const, href: "/gigs" },
|
||||
{ labelKey: "recruit" as const, href: "/jobs" },
|
||||
{ labelKey: "terms" as const, href: "#" },
|
||||
{ labelKey: "privacy" as const, href: "#" },
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import { useState } from "react";
|
||||
import { Link } from "@/i18n/navigation";
|
||||
import { useTranslation } from "@/i18n/navigation";
|
||||
|
||||
const avatarPhotos = [
|
||||
"https://i.pravatar.cc/150?img=32",
|
||||
@@ -18,14 +19,16 @@ const avatarPhotos = [
|
||||
];
|
||||
|
||||
const features = [
|
||||
{ icon: "🍹", text: "每年在 30+ 城市参加线下聚会" },
|
||||
{ icon: "❤️", text: "结识新朋友,约会交友" },
|
||||
{ icon: "🧪", text: "研究目的地,找到最适合居住和工作的地方" },
|
||||
{ icon: "🌎", text: "追踪你的旅行,记录你去过的地方" },
|
||||
{ icon: "💬", text: "加入社群聊天,在旅途中找到你的归属" },
|
||||
{ icon: "📍", key: "feature1" as const },
|
||||
{ icon: "📊", key: "feature2" as const },
|
||||
{ icon: "👥", key: "feature3" as const },
|
||||
{ icon: "💰", key: "feature4" as const },
|
||||
{ icon: "📈", key: "feature5" as const },
|
||||
];
|
||||
|
||||
export default function HeroSection() {
|
||||
const { t } = useTranslation("common");
|
||||
const { t: tHero } = useTranslation("hero");
|
||||
const [email, setEmail] = useState("");
|
||||
|
||||
const handleJoin = (e: React.FormEvent) => {
|
||||
@@ -57,29 +60,28 @@ export default function HeroSection() {
|
||||
<div className="flex flex-col lg:flex-row lg:items-start lg:gap-10 xl:gap-16">
|
||||
{/* Left Column */}
|
||||
<div className="flex-1 min-w-0">
|
||||
<span className="inline-flex items-center gap-1.5 text-xs font-semibold text-orange-700 bg-orange-100 px-3 py-1.5 rounded-full mb-5">
|
||||
🏆 中国数字游民社区 · 始于 2024
|
||||
<span className="inline-flex items-center gap-1.5 text-xs font-semibold text-orange-700 dark:text-orange-400 bg-orange-100 dark:bg-orange-900/30 px-3 py-1.5 rounded-full mb-5">
|
||||
🏆 {tHero("badge")}
|
||||
</span>
|
||||
|
||||
<h1 className="text-3xl sm:text-4xl md:text-5xl lg:text-6xl font-bold tracking-tight leading-tight mb-5">
|
||||
<span className="hero-gradient-text">探索中国</span>
|
||||
<span className="hero-gradient-text">{tHero("title")}</span>
|
||||
<br />
|
||||
<span className="text-gray-900">远程工作,自由生活</span>
|
||||
<span className="text-gray-900 dark:text-gray-100">{tHero("titleSuffix")}</span>
|
||||
</h1>
|
||||
|
||||
<p className="text-base sm:text-lg text-gray-500 max-w-2xl mb-7 leading-relaxed">
|
||||
加入全球华人数字游民社区,发现中国最适合远程工作和生活的城市,
|
||||
从大理到成都,从深圳到杭州,开启你的游牧人生
|
||||
<p className="text-base sm:text-lg text-gray-500 dark:text-gray-400 max-w-2xl mb-7 leading-relaxed">
|
||||
{tHero("subtitle")}
|
||||
</p>
|
||||
|
||||
<div className="space-y-2.5 mb-8">
|
||||
{features.map((f) => (
|
||||
<div key={f.text} className="flex items-start gap-2.5">
|
||||
<div key={f.key} className="flex items-start gap-2.5">
|
||||
<span className="text-base sm:text-lg shrink-0 mt-0.5">
|
||||
{f.icon}
|
||||
</span>
|
||||
<span className="text-sm sm:text-base text-gray-600 underline decoration-gray-300 underline-offset-2">
|
||||
{f.text}
|
||||
<span className="text-sm sm:text-base text-gray-600 dark:text-gray-400 underline decoration-gray-300 dark:decoration-gray-600 underline-offset-2">
|
||||
{tHero(f.key)}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
@@ -92,26 +94,26 @@ export default function HeroSection() {
|
||||
key={i}
|
||||
src={src}
|
||||
alt=""
|
||||
className="w-8 h-8 rounded-full border-2 border-white shadow-sm object-cover"
|
||||
className="w-8 h-8 rounded-full border-2 border-white dark:border-gray-800 shadow-sm object-cover"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<span className="text-sm text-gray-500">
|
||||
<strong className="text-gray-900">5,000+</strong> 成员已加入
|
||||
<span className="text-sm text-gray-500 dark:text-gray-400">
|
||||
<strong className="text-gray-900 dark:text-gray-100">5,000+</strong> 成员已加入
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="pt-7 border-t border-gray-100">
|
||||
<p className="text-xs text-gray-400 uppercase tracking-wider mb-3">
|
||||
<div className="pt-7 border-t border-gray-100 dark:border-gray-800">
|
||||
<p className="text-xs text-gray-400 dark:text-gray-500 uppercase tracking-wider mb-3">
|
||||
媒体报道
|
||||
</p>
|
||||
<div className="flex flex-wrap items-center gap-x-8 gap-y-2">
|
||||
{["36氪", "少数派", "极客公园", "虎嗅", "创业邦", "澎湃新闻"].map(
|
||||
(name) => (
|
||||
<span
|
||||
key={name}
|
||||
className="text-sm font-semibold text-gray-300 hover:text-gray-400 transition-colors cursor-default"
|
||||
>
|
||||
<span
|
||||
key={name}
|
||||
className="text-sm font-semibold text-gray-300 dark:text-gray-600 hover:text-gray-400 dark:hover:text-gray-500 transition-colors cursor-default"
|
||||
>
|
||||
{name}
|
||||
</span>
|
||||
),
|
||||
@@ -122,7 +124,7 @@ export default function HeroSection() {
|
||||
|
||||
{/* Right Column: Video + Email */}
|
||||
<div className="lg:w-[360px] xl:w-[400px] shrink-0 mt-10 lg:mt-4">
|
||||
<div className="bg-white rounded-2xl shadow-xl overflow-hidden border border-gray-100">
|
||||
<div className="bg-white dark:bg-gray-900 rounded-2xl shadow-xl overflow-hidden border border-gray-100 dark:border-gray-800">
|
||||
<div className="relative h-48 sm:h-56 bg-gradient-to-br from-emerald-400 via-teal-500 to-cyan-600 flex items-center justify-center cursor-pointer group overflow-hidden">
|
||||
<div className="absolute inset-0 opacity-20">
|
||||
<div className="absolute top-4 left-4 w-20 h-16 rounded-lg bg-white/20" />
|
||||
@@ -147,7 +149,7 @@ export default function HeroSection() {
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
placeholder="输入你的邮箱..."
|
||||
className="w-full border border-gray-200 rounded-lg px-4 py-3 text-sm placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-[#ff4d4f]/20 focus:border-[#ff4d4f] transition-colors mb-3"
|
||||
className="w-full border border-gray-200 dark:border-gray-700 rounded-lg px-4 py-3 text-sm placeholder-gray-400 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-[#ff4d4f]/20 focus:border-[#ff4d4f] transition-colors mb-3"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
@@ -155,29 +157,35 @@ export default function HeroSection() {
|
||||
>
|
||||
加入游牧中国 →
|
||||
</button>
|
||||
<p className="text-xs text-gray-400 text-center mt-3">
|
||||
<p className="text-xs text-gray-400 dark:text-gray-500 text-center mt-3">
|
||||
已有账号?输入邮箱即可自动登录
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 flex flex-wrap gap-2 justify-center">
|
||||
<Link
|
||||
href="/meetups"
|
||||
className="text-xs text-gray-400 hover:text-gray-600 bg-white rounded-full px-3 py-1.5 border border-gray-100 transition-colors"
|
||||
>
|
||||
<Link href="/dashboard" className="text-xs text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 bg-white dark:bg-gray-800 rounded-full px-3 py-1.5 border border-gray-100 dark:border-gray-700 transition-colors">
|
||||
📍 下一站
|
||||
</Link>
|
||||
<Link href="/meetups" className="text-xs text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 bg-white dark:bg-gray-800 rounded-full px-3 py-1.5 border border-gray-100 dark:border-gray-700 transition-colors">
|
||||
🍹 线下聚会
|
||||
</Link>
|
||||
<Link
|
||||
href="/dating"
|
||||
className="text-xs text-gray-400 hover:text-gray-600 bg-white rounded-full px-3 py-1.5 border border-gray-100 transition-colors"
|
||||
>
|
||||
❤️ 交友约会
|
||||
<Link href="/dating" className="text-xs text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 bg-white dark:bg-gray-800 rounded-full px-3 py-1.5 border border-gray-100 dark:border-gray-700 transition-colors">
|
||||
❤️ 智能匹配
|
||||
</Link>
|
||||
<Link
|
||||
href="/join"
|
||||
className="text-xs text-gray-400 hover:text-gray-600 bg-white rounded-full px-3 py-1.5 border border-gray-100 transition-colors"
|
||||
>
|
||||
<Link href="/tools" className="text-xs text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 bg-white dark:bg-gray-800 rounded-full px-3 py-1.5 border border-gray-100 dark:border-gray-700 transition-colors">
|
||||
📊 工具箱
|
||||
</Link>
|
||||
<Link href="/ai" className="text-xs text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 bg-white dark:bg-gray-800 rounded-full px-3 py-1.5 border border-gray-100 dark:border-gray-700 transition-colors">
|
||||
🤖 AI 助理
|
||||
</Link>
|
||||
<Link href="/gigs" className="text-xs text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 bg-white dark:bg-gray-800 rounded-full px-3 py-1.5 border border-gray-100 dark:border-gray-700 transition-colors">
|
||||
💰 赏金墙
|
||||
</Link>
|
||||
<Link href="/report" className="text-xs text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 bg-white dark:bg-gray-800 rounded-full px-3 py-1.5 border border-gray-100 dark:border-gray-700 transition-colors">
|
||||
📊 年报
|
||||
</Link>
|
||||
<Link href="/join" className="text-xs text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 bg-white dark:bg-gray-800 rounded-full px-3 py-1.5 border border-gray-100 dark:border-gray-700 transition-colors">
|
||||
💬 加入社群
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
@@ -8,8 +8,13 @@ import ThemeToggle from "./ThemeToggle";
|
||||
|
||||
const navLinks = [
|
||||
{ labelKey: "cities" as const, icon: "🏙️", href: "/" },
|
||||
{ labelKey: "dashboard" as const, icon: "📍", href: "/dashboard" },
|
||||
{ labelKey: "travel" as const, icon: "✈️", href: "/meetups" },
|
||||
{ labelKey: "community" as const, icon: "💬", href: "/dating" },
|
||||
{ labelKey: "tools" as const, icon: "📊", href: "/tools" },
|
||||
{ labelKey: "ai" as const, icon: "🤖", href: "/ai" },
|
||||
{ labelKey: "gigs" as const, icon: "💰", href: "/gigs" },
|
||||
{ labelKey: "pricing" as const, icon: "👑", href: "/pricing" },
|
||||
{ labelKey: "explore" as const, icon: "🎒", href: "/#community" },
|
||||
];
|
||||
|
||||
@@ -48,7 +53,7 @@ export default function Navbar() {
|
||||
</span>
|
||||
</Link>
|
||||
|
||||
<div className="hidden md:flex items-center gap-1">
|
||||
<div className="hidden md:flex items-center gap-1 overflow-x-auto max-w-[calc(100vw-280px)] scrollbar-hide">
|
||||
{navLinks.map((link) => (
|
||||
<Link
|
||||
key={link.labelKey}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
/**
|
||||
* 游牧中国站点配置
|
||||
* 游牧中国站点配置 - Nomad OS
|
||||
*/
|
||||
|
||||
export const SITE_CONFIG = {
|
||||
meta: {
|
||||
title: "游牧中国 - 数字游民的理想目的地",
|
||||
title: "游牧中国 NomadCNA - 数字游民生活操作系统",
|
||||
description:
|
||||
"发现全球最适合远程工作和生活的城市,加入中国数字游民社区,探索远程办公新方式",
|
||||
"数字游民的一站式生活操作系统:城市决策、税务签证、智能匹配、赏金任务。从 Free 到 Pro,开启你的游牧人生",
|
||||
},
|
||||
tagline: "Nomad OS · 你的下一站,由数据决定",
|
||||
};
|
||||
|
||||
export function getSiteConfig() {
|
||||
|
||||
136
messages/en.json
136
messages/en.json
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"common": {
|
||||
"siteName": "Nomad China",
|
||||
"tagline": "Nomad OS · Your next stop, data-driven",
|
||||
"siteNameShort": "NomadCNA",
|
||||
"logout": "Logout",
|
||||
"loginRegister": "Login / Sign up",
|
||||
@@ -9,15 +10,26 @@
|
||||
},
|
||||
"nav": {
|
||||
"cities": "Cities",
|
||||
"ai": "AI",
|
||||
"travel": "Travel",
|
||||
"community": "Community",
|
||||
"explore": "Explore"
|
||||
"explore": "Explore",
|
||||
"dashboard": "Next Stop",
|
||||
"tools": "Tools",
|
||||
"pricing": "Pricing",
|
||||
"gigs": "Gigs",
|
||||
"report": "Report"
|
||||
},
|
||||
"hero": {
|
||||
"badge": "China Digital Nomad Community · Since 2024",
|
||||
"title": "Explore China",
|
||||
"titleSuffix": "Remote Work, Free Life",
|
||||
"subtitle": "Join the global Chinese digital nomad community, discover the best cities in China for remote work and living",
|
||||
"badge": "Digital Nomad Life OS · Nomad OS",
|
||||
"title": "Your Next Stop",
|
||||
"titleSuffix": "Data-Driven",
|
||||
"subtitle": "City decisions, tax & visa tools, smart matching, gig board. One-stop solution for nomad life",
|
||||
"feature1": "Next stop: input budget, timezone, internet → get city rankings",
|
||||
"feature2": "Tax & visa toolkit: exchange, tax estimate, visa countdown",
|
||||
"feature3": "Smart match: partner, roommate, co-founder, explore buddy",
|
||||
"feature4": "Gig board: post/claim tasks, platform fee 10-15%",
|
||||
"feature5": "Nomad report: countries visited? Generate share card",
|
||||
"ctaJoin": "Join Nomad China →",
|
||||
"stats": {
|
||||
"cities": "Cities",
|
||||
@@ -32,9 +44,11 @@
|
||||
"resources": "Resources",
|
||||
"about": "About",
|
||||
"links": {
|
||||
"ai": "AI Assistant",
|
||||
"join": "Join Community",
|
||||
"meetups": "Offline Meetups",
|
||||
"dating": "Dating",
|
||||
"dating": "Smart Match",
|
||||
"dashboard": "Next Stop",
|
||||
"map": "Member Map",
|
||||
"climate": "Climate Query",
|
||||
"cost": "Cost Calculator",
|
||||
@@ -43,8 +57,11 @@
|
||||
"guide": "Remote Work Guide",
|
||||
"cityGuide": "City Guide",
|
||||
"stories": "Nomad Stories",
|
||||
"report": "Nomad Report",
|
||||
"faq": "FAQ",
|
||||
"aboutUs": "About Us",
|
||||
"pricing": "Pricing",
|
||||
"gigs": "Gig Board",
|
||||
"recruit": "Careers",
|
||||
"terms": "Terms",
|
||||
"privacy": "Privacy",
|
||||
@@ -134,5 +151,112 @@
|
||||
"goLogin": "Go to login",
|
||||
"passwordMismatch": "Passwords do not match",
|
||||
"passwordMinLength": "Password must be at least 8 characters"
|
||||
},
|
||||
"pricing": {
|
||||
"title": "Membership",
|
||||
"monthly": "Monthly",
|
||||
"yearly": "Yearly",
|
||||
"subtitle": "From Free to Pro, unlock more nomad power",
|
||||
"free": "Free",
|
||||
"basic": "Basic",
|
||||
"pro": "Pro",
|
||||
"perMonth": "/mo",
|
||||
"perYear": "/yr",
|
||||
"currentPlan": "Current plan",
|
||||
"getStarted": "Get started",
|
||||
"upgrade": "Upgrade",
|
||||
"features": {
|
||||
"browse": "Basic browse + public data",
|
||||
"noAds": "Ad-free",
|
||||
"advancedFilter": "Advanced filters",
|
||||
"unlimitedSwipe": "Unlimited swipe",
|
||||
"priorityEvent": "Priority event signup",
|
||||
"crmRoster": "CRM roster visible",
|
||||
"oneOnOne": "1v1 with paid guests",
|
||||
"gigAccess": "Internal gig earnings",
|
||||
"dashboard": "Data dashboard",
|
||||
"experienceCamp": "Experience camp sold separately"
|
||||
}
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Next Stop Dashboard",
|
||||
"noMatch": "No matching cities, try relaxing filters",
|
||||
"devCostCurve": "Cost estimate curve",
|
||||
"devHeatmap": "Nomad density heatmap",
|
||||
"devSuffix": "(dev)",
|
||||
"subtitle": "Input preferences, get city rankings",
|
||||
"budget": "Monthly budget (¥)",
|
||||
"timezone": "Timezone preference",
|
||||
"internet": "Min internet (Mbps)",
|
||||
"visa": "Visa friendliness",
|
||||
"tags": "People to meet",
|
||||
"search": "Recommend cities",
|
||||
"costCurve": "Cost estimate curve",
|
||||
"nomadHeatmap": "Nomad density heatmap",
|
||||
"nearbyEvents": "Nearby events",
|
||||
"aiTip": "AI recommendation"
|
||||
},
|
||||
"tools": {
|
||||
"title": "Toolkit",
|
||||
"disclaimer": "Data for reference only, actual rates may vary",
|
||||
"taxDisclaimer": "Simplified estimate, not professional tax advice",
|
||||
"subtitle": "Exchange · Tax · Visa",
|
||||
"exchange": "Live exchange rates",
|
||||
"tax": "Tax calculator",
|
||||
"visa": "Visa countdown",
|
||||
"incomeCountry": "Income country",
|
||||
"estimate": "Estimate tax",
|
||||
"visaExpiry": "Visa expiry",
|
||||
"daysLeft": "days left",
|
||||
"expired": "Expired"
|
||||
},
|
||||
"match": {
|
||||
"title": "Smart Match",
|
||||
"likedYou": "People who liked you",
|
||||
"dislike": "Pass",
|
||||
"like": "Like",
|
||||
"report": "Report",
|
||||
"matchTags": "Match tags",
|
||||
"dating": "Dating",
|
||||
"friends": "Friends",
|
||||
"partner": "Partner",
|
||||
"roommate": "Roommate",
|
||||
"cofounder": "Co-founder",
|
||||
"explore": "Explore buddy",
|
||||
"badgeDating": "Both looking for 🌹 Dating",
|
||||
"badgeFriends": "Both looking for 🤝 Friends",
|
||||
"badgePartner": "Both looking for 👥 Partner",
|
||||
"badgeRoommate": "Both looking for 🏠 Roommate",
|
||||
"badgeCofounder": "Both looking for 🚀 Co-founder",
|
||||
"badgeExplore": "Both looking for 🍜 Explore"
|
||||
},
|
||||
"gigs": {
|
||||
"title": "Gig Board",
|
||||
"subtitle": "Post tasks · Earn money",
|
||||
"publish": "Post task",
|
||||
"browse": "Browse tasks",
|
||||
"platformFee": "Platform fee 10-15%"
|
||||
},
|
||||
"ai": {
|
||||
"title": "AI Assistant",
|
||||
"subtitle": "Ask for city recommendations, travel plans, intro copy",
|
||||
"placeholder": "Enter your question, e.g. Budget $800/mo, Asia tropical, stable internet...",
|
||||
"ask": "Ask",
|
||||
"thinking": "Thinking…",
|
||||
"examples": "Example questions",
|
||||
"comingSoon": "AI feature coming soon"
|
||||
},
|
||||
"report": {
|
||||
"title": "Nomad Year Report",
|
||||
"desc": "Auto stats: countries visited, avg monthly cost, coworking check-ins",
|
||||
"yearTitle": "2025 Nomad Report",
|
||||
"countries": "Countries",
|
||||
"avgCost": "Avg monthly cost",
|
||||
"coworkingCheckins": "Coworking check-ins",
|
||||
"meetups": "Meetups",
|
||||
"watermark": "Nomad China · Share card with watermark",
|
||||
"regenerate": "Regenerate",
|
||||
"subtitle": "Countries visited? Avg monthly cost?",
|
||||
"generate": "Generate report"
|
||||
}
|
||||
}
|
||||
|
||||
136
messages/zh.json
136
messages/zh.json
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"common": {
|
||||
"siteName": "游牧中国",
|
||||
"tagline": "Nomad OS · 你的下一站,由数据决定",
|
||||
"siteNameShort": "游牧中国",
|
||||
"logout": "退出",
|
||||
"loginRegister": "登录 / 注册",
|
||||
@@ -9,15 +10,26 @@
|
||||
},
|
||||
"nav": {
|
||||
"cities": "城市",
|
||||
"ai": "AI",
|
||||
"travel": "旅行",
|
||||
"community": "社区",
|
||||
"explore": "探索"
|
||||
"explore": "探索",
|
||||
"dashboard": "下一站",
|
||||
"tools": "工具箱",
|
||||
"pricing": "会员",
|
||||
"gigs": "赏金",
|
||||
"report": "年报"
|
||||
},
|
||||
"hero": {
|
||||
"badge": "中国数字游民社区 · 始于 2024",
|
||||
"title": "探索中国",
|
||||
"titleSuffix": "远程工作,自由生活",
|
||||
"subtitle": "加入全球华人数字游民社区,发现中国最适合远程工作和生活的城市",
|
||||
"badge": "数字游民生活操作系统 · Nomad OS",
|
||||
"title": "你的下一站",
|
||||
"titleSuffix": "由数据决定",
|
||||
"subtitle": "城市决策、税务签证、智能匹配、赏金任务。一站式解决游牧生活的核心痛点",
|
||||
"feature1": "下一站决策:输入预算、时区、网速,输出城市排名",
|
||||
"feature2": "税务签证工具箱:汇率、税负估算、签证倒计时",
|
||||
"feature3": "智能匹配:搭档、室友、合伙人、探店 buddy",
|
||||
"feature4": "赏金墙:发布/接单小任务,平台抽成 10-15%",
|
||||
"feature5": "Nomad 年报:今年飞了多少国家?生成分享卡片",
|
||||
"ctaJoin": "加入游牧中国 →",
|
||||
"stats": {
|
||||
"cities": "个城市",
|
||||
@@ -32,9 +44,11 @@
|
||||
"resources": "资源",
|
||||
"about": "关于",
|
||||
"links": {
|
||||
"ai": "AI 助理",
|
||||
"join": "加入社群",
|
||||
"meetups": "线下聚会",
|
||||
"dating": "交友约会",
|
||||
"dating": "智能匹配",
|
||||
"dashboard": "下一站",
|
||||
"map": "成员地图",
|
||||
"climate": "气候查询",
|
||||
"cost": "生活成本计算",
|
||||
@@ -43,8 +57,11 @@
|
||||
"guide": "远程工作指南",
|
||||
"cityGuide": "城市落地攻略",
|
||||
"stories": "游民故事",
|
||||
"report": "Nomad 年报",
|
||||
"faq": "常见问题",
|
||||
"aboutUs": "关于我们",
|
||||
"pricing": "会员定价",
|
||||
"gigs": "赏金墙",
|
||||
"recruit": "招聘",
|
||||
"terms": "使用条款",
|
||||
"privacy": "隐私政策",
|
||||
@@ -134,5 +151,112 @@
|
||||
"goLogin": "去登录",
|
||||
"passwordMismatch": "两次密码不一致",
|
||||
"passwordMinLength": "密码至少 8 位"
|
||||
},
|
||||
"pricing": {
|
||||
"title": "会员体系",
|
||||
"monthly": "月付",
|
||||
"yearly": "年付",
|
||||
"subtitle": "从 Free 到 Pro,解锁更多游牧能力",
|
||||
"free": "Free",
|
||||
"basic": "Basic",
|
||||
"pro": "Pro",
|
||||
"perMonth": "/月",
|
||||
"perYear": "/年",
|
||||
"currentPlan": "当前方案",
|
||||
"getStarted": "开始使用",
|
||||
"upgrade": "升级",
|
||||
"features": {
|
||||
"browse": "基础浏览 + 公开资料",
|
||||
"noAds": "去除广告",
|
||||
"advancedFilter": "高级筛选",
|
||||
"unlimitedSwipe": "无限滑动匹配",
|
||||
"priorityEvent": "优先活动报名",
|
||||
"crmRoster": "专属花名册可见",
|
||||
"oneOnOne": "付费嘉宾 1v1",
|
||||
"gigAccess": "内部小任务赚钱",
|
||||
"dashboard": "数据仪表盘",
|
||||
"experienceCamp": "体验营/共居单独售卖"
|
||||
}
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "下一站决策仪表盘",
|
||||
"noMatch": "暂无匹配城市,放宽筛选条件试试",
|
||||
"devCostCurve": "成本预估曲线",
|
||||
"devHeatmap": "游民密度热力图",
|
||||
"devSuffix": "(开发中)",
|
||||
"subtitle": "输入偏好,输出最适合你的城市排名",
|
||||
"budget": "月预算 (¥)",
|
||||
"timezone": "时区偏好",
|
||||
"internet": "最低网速 (Mbps)",
|
||||
"visa": "签证友好度",
|
||||
"tags": "想认识的人群",
|
||||
"search": "推荐城市",
|
||||
"costCurve": "成本预估曲线",
|
||||
"nomadHeatmap": "游民密度热力图",
|
||||
"nearbyEvents": "最近同城活动",
|
||||
"aiTip": "AI 推荐"
|
||||
},
|
||||
"tools": {
|
||||
"title": "工具箱",
|
||||
"disclaimer": "数据仅供参考,以实际汇率为准",
|
||||
"taxDisclaimer": "简化估算,非专业税务建议",
|
||||
"subtitle": "汇率 · 税务 · 签证",
|
||||
"exchange": "实时汇率",
|
||||
"tax": "税务计算器",
|
||||
"visa": "签证倒计时",
|
||||
"incomeCountry": "收入国家",
|
||||
"estimate": "估算税负",
|
||||
"visaExpiry": "签证到期",
|
||||
"daysLeft": "天剩余",
|
||||
"expired": "已过期"
|
||||
},
|
||||
"match": {
|
||||
"title": "智能匹配",
|
||||
"likedYou": "这些人喜欢了你",
|
||||
"dislike": "不感兴趣",
|
||||
"like": "喜欢",
|
||||
"report": "举报资料",
|
||||
"matchTags": "匹配标签",
|
||||
"dating": "约会",
|
||||
"friends": "交友",
|
||||
"partner": "搭档",
|
||||
"roommate": "室友",
|
||||
"cofounder": "项目合伙人",
|
||||
"explore": "探店 buddy",
|
||||
"badgeDating": "你们都在寻找 🌹 约会",
|
||||
"badgeFriends": "你们都在寻找 🤝 交友",
|
||||
"badgePartner": "你们都在寻找 👥 搭档",
|
||||
"badgeRoommate": "你们都在寻找 🏠 室友",
|
||||
"badgeCofounder": "你们都在寻找 🚀 合伙人",
|
||||
"badgeExplore": "你们都在寻找 🍜 探店"
|
||||
},
|
||||
"gigs": {
|
||||
"title": "赏金墙",
|
||||
"subtitle": "发布需求 · 接单赚钱",
|
||||
"publish": "发布任务",
|
||||
"browse": "浏览任务",
|
||||
"platformFee": "平台抽成 10-15%"
|
||||
},
|
||||
"ai": {
|
||||
"title": "AI 助理",
|
||||
"subtitle": "问城市推荐、写行程计划、生成介绍文案",
|
||||
"placeholder": "输入你的问题,例如:预算每月800刀,想亚洲热带,有稳定网...",
|
||||
"ask": "提问",
|
||||
"thinking": "思考中…",
|
||||
"examples": "示例问题",
|
||||
"comingSoon": "AI 功能即将上线,敬请期待"
|
||||
},
|
||||
"report": {
|
||||
"title": "Nomad 年报",
|
||||
"desc": "自动统计:今年飞了多少国家、平均月生活成本、在多少个 co-working 打卡",
|
||||
"yearTitle": "2025 Nomad 年报",
|
||||
"countries": "国家/地区",
|
||||
"avgCost": "月均生活成本",
|
||||
"coworkingCheckins": "Co-working 打卡",
|
||||
"meetups": "线下聚会",
|
||||
"watermark": "游牧中国 NomadCNA · 生成分享卡片带水印引流",
|
||||
"regenerate": "重新生成",
|
||||
"subtitle": "今年飞了多少国家?平均月生活成本?",
|
||||
"generate": "生成年报"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user