nomadweb/frontend/src/components/Phrasebook.tsx
eric d1bc4af87f Add jet lag, housing guide, phrasebook, and feedback widget
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 23:28:52 -05:00

136 lines
4.8 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import { useMemo, useState } from "react";
import { useToast } from "@/lib/toast";
import type { Destination } from "@/lib/types";
interface Phrase {
local: string;
pinyin?: string;
zh: string;
}
const PHRASES: Record<string, { lang: string; flag: string; items: Phrase[] }> = {
bali: {
lang: "印尼语", flag: "🇮🇩",
items: [
{ local: "Halo", zh: "你好" },
{ local: "Terima kasih", zh: "谢谢" },
{ local: "Berapa harganya?", zh: "这个多少钱?" },
{ local: "WiFi-nya bagus?", zh: "网速好吗?" },
{ local: "Saya mau sewa sebulan", zh: "我想租一个月" },
{ local: "Di mana toilet?", zh: "洗手间在哪?" },
],
},
chiangmai: {
lang: "泰语", flag: "🇹🇭",
items: [
{ local: "สวัสดี", pinyin: "Sawatdee", zh: "你好" },
{ local: "ขอบคุณ", pinyin: "Khop khun", zh: "谢谢" },
{ local: "เท่าไหร่", pinyin: "Tao rai", zh: "多少钱?" },
{ local: "WiFi แรงไหม", pinyin: "WiFi raeng mai", zh: "网快吗?" },
{ local: "อร่อยมาก", pinyin: "Aroi mak", zh: "很好吃" },
{ local: "ไม่เผ็ด", pinyin: "Mai phet", zh: "不要辣" },
],
},
lisbon: {
lang: "葡萄牙语", flag: "🇵🇹",
items: [
{ local: "Olá", zh: "你好" },
{ local: "Obrigado / Obrigada", zh: "谢谢(男/女)" },
{ local: "Quanto custa?", zh: "多少钱?" },
{ local: "O WiFi é bom?", zh: "网好吗?" },
{ local: "Uma cerveja, por favor", zh: "请来一杯啤酒" },
{ local: "Onde fica o coworking?", zh: "联合办公在哪?" },
],
},
barcelona: {
lang: "西班牙语", flag: "🇪🇸",
items: [
{ local: "Hola", zh: "你好" },
{ local: "Gracias", zh: "谢谢" },
{ local: "¿Cuánto cuesta?", zh: "多少钱?" },
{ local: "¿Hay buen WiFi?", zh: "有好网吗?" },
{ local: "La cuenta, por favor", zh: "买单" },
{ local: "¿Dónde está el metro?", zh: "地铁在哪?" },
],
},
mexico: {
lang: "西班牙语", flag: "🇲🇽",
items: [
{ local: "Qué onda", zh: "嘿(口语)" },
{ local: "Gracias", zh: "谢谢" },
{ local: "¿Cuánto cuesta?", zh: "多少钱?" },
{ local: "Sin chile, por favor", zh: "不要辣" },
{ local: "¿Tienen WiFi?", zh: "有 WiFi 吗?" },
{ local: "La cuenta, por favor", zh: "买单" },
],
},
tokyo: {
lang: "日语", flag: "🇯🇵",
items: [
{ local: "こんにちは", pinyin: "Konnichiwa", zh: "你好" },
{ local: "ありがとう", pinyin: "Arigatou", zh: "谢谢" },
{ local: "いくらですか", pinyin: "Ikura desu ka", zh: "多少钱?" },
{ local: "Wi-Fiはありますか", pinyin: "Wi-Fi wa arimasu ka", zh: "有 WiFi 吗?" },
{ local: "おいしい", pinyin: "Oishii", zh: "好吃" },
{ local: "すみません", pinyin: "Sumimasen", zh: "不好意思 / 借过" },
],
},
};
interface Props {
destinations: Destination[];
}
export default function Phrasebook({ destinations }: Props) {
const { toast } = useToast();
const [slug, setSlug] = useState(destinations[0]?.slug || "bali");
const book = useMemo(() => PHRASES[slug] || PHRASES.bali, [slug]);
const dest = destinations.find((d) => d.slug === slug);
const copy = async (text: string) => {
await navigator.clipboard.writeText(text);
toast(`已复制:${text}`);
};
return (
<section className="section phrase-section" id="phrases">
<div className="container">
<div className="section-header reveal">
<span className="section-tag">🗣️ PHRASES</span>
<h2>游民生存短语本</h2>
<p>点一下复制常用语,落地就能开口</p>
</div>
<div className="housing-cities reveal">
{destinations.map((d) => (
<button
key={d.slug}
className={`season-city-btn${slug === d.slug ? " active" : ""}`}
onClick={() => setSlug(d.slug)}
>
{d.emoji} {d.name}
</button>
))}
</div>
<div className="phrase-card reveal">
<div className="phrase-lang">
{book.flag} {dest?.name} · {book.lang}
</div>
<div className="phrase-grid">
{book.items.map((p) => (
<button key={p.local} className="phrase-item" onClick={() => copy(p.local)} title="点击复制">
<strong>{p.local}</strong>
{p.pinyin && <span className="phrase-pinyin">{p.pinyin}</span>}
<span className="phrase-zh">{p.zh}</span>
</button>
))}
</div>
</div>
</div>
</section>
);
}