diff --git a/src/i18n/en.ts b/src/i18n/en.ts index ef9693d..3ace6a3 100644 --- a/src/i18n/en.ts +++ b/src/i18n/en.ts @@ -6864,6 +6864,7 @@ walletUi: { membersLabel: 'Members', dismissReport: 'Dismiss report', reportedBy: 'reported by', + completingLoginAmber: 'Completing login with Amber...', entityMembersHint: 'Add the Nostr accounts of your crew. Each member confirms their own spot with their key — until then they show as “pending”.', entityVerifiedHint: 'Membership is verified per member — nobody can be added to your crew without confirming from their own account.', entityInvalidPubkey: 'Enter a valid npub, nprofile, or 64-char hex pubkey.', @@ -6882,6 +6883,83 @@ walletUi: { offlineViewDetails: 'View cached spot details', offlineKeepActing: 'Keep acting — posts, RSVPs, listings and messages are queued and send when you reconnect', }, + seoUi: { + becomeBuilderTitle: 'Become a Builder - SkateSpots', + becomeBuilderDesc: 'List your ramp, park and DIY building services — what you build, materials and scale.', + tradesTitle: 'My Trades — SkateSpots', + tradesDesc: 'Manage your marketplace trades, confirmations and reputation.', + eventsTitle: 'Skate Events & Tours - SkateSpots', + eventsDesc: 'Discover skateboarding events, competitions, tours, and sessions near you.', + editShopDesc: 'Edit your skate shop.', + createTourDesc: 'Organize skate tours - local hidden gem tours, national road trips, international adventures, or KOTR-style destruction competitions.', + deckDesignerTitle: 'Deck Designer — SkateSpots DIY', + deckDesignerDesc: 'Design a skateboard or longboard outline, then export a printable 1:1 template, SVG or DXF — concave, kicks, wheelbase and truck holes included.', + createEventTitle: 'Create a Skate Event - SkateSpots', + createEventDesc: 'Create a new skateboarding event for the community.', + editTourDesc: 'Edit your skate tour — update stops, dates and details.', + walletTitle: 'Wallet - SkateSpots', + walletDesc: 'Connect your Lightning wallet to send zaps to skaters and spot creators.', + becomePhotographerTitle: 'Become a Photographer - SkateSpots', + becomePhotographerDesc: 'Create your skate photography profile — cameras, lenses, lighting and services.', + newsletterTitle: 'Newsletter - SkateSpots', + newsletterDesc: 'Weekly drops from the SkateSpots community. App updates, new spots, events and the people keeping skateboarding alive.', + homeTitle: 'SkateSpots - Discover Skateboarding Locations Worldwide', + zapLeaderboardTitle: 'Zap Leaderboards - SkateSpots', + zapLeaderboardDesc: 'Top zapped skate spots and the community members supporting them.', + adminReviewTitle: 'Review queue — SkateSpots', + gearSwapTitle: 'Gear Swap - SkateSpots', + gearSwapDesc: 'Buy & sell used skate gear with other skaters. Decks, trucks, wheels, shoes, and more.', + donateTitle: 'Donate — SkateSpots', + donateDesc: 'Support SkateSpots — Lightning, private on-chain (silent payments), or fund the build on Angor.', + verifyCardTitle: 'Verify — SkateSpots', + verifyCardDesc: 'Verify a SkateSpots card or access pass.', + mapTitle: 'Skate Spots Map - SkateSpots', + mapDesc: 'Explore skateboarding spots around the world on our interactive map.', + diyTitle: 'DIY — Make Your Own | SkateSpots', + diyDesc: 'Guides, open tools and plans to make your own skateboards, ramps and concrete spots — and the people and places to do it with.', + appShopTitle: 'App Shop — SkateSpots', + becomeSkaterTitle: 'Join the Skater Directory - SkateSpots', + becomeSkaterDesc: 'Create your skater profile so the community can find you.', + marketplaceTitle: 'Marketplace - SkateSpots', + marketplaceDesc: 'Buy and sell skateboarding gear on the decentralized Plebeian Market.', + becomeEditorTitle: 'Become a Video Editor - SkateSpots', + becomeEditorDesc: 'Create your skateboarding video editor profile on SkateSpots.', + amberCallbackTitle: 'Signing in with Amber - SkateSpots', + amberCallbackDesc: 'Completing your Amber (NIP-55) login.', + supportersTitle: 'Supporters - SkateSpots', + supportersDesc: 'The community keeping SkateSpots alive — supporters, collaborators, partners and friends.', + becomeCoachTitle: 'Become a Coach - SkateSpots', + becomeCoachDesc: 'Create your skateboarding coach profile.', + riderCardTitle: 'Skater Card — SkateSpots', + riderCardDesc: 'Create your printable SkateSpots Skater License or Park Access Card.', + editEventDesc: 'Edit your skateboarding event.', + createShopTitle: 'Add a Skate Shop - SkateSpots', + createShopDesc: 'Add your skate shop to the community.', + deckCostTitle: 'Deck Cost Calculator — SkateSpots DIY', + deckCostDesc: 'Work out what a deck costs to make — labour, equipment, materials, process time, packaging and margin — and a fair selling price.', + pyramidTitle: 'Pyramid Rankings - SkateSpots', + pyramidDesc: 'Top contributors and community leaders.', + foodTitle: 'Food Near Spots - SkateSpots', + foodDesc: 'Find restaurants, cafés, and food options near skateboarding spots.', + createListingTitle: 'List Gear - SkateSpots', + createListingDesc: 'List skateboarding gear for sale or find gear you want to buy.', + becomeFilmerTitle: 'Become a Filmer - SkateSpots', + becomeFilmerDesc: 'Create your skateboarding filming profile.', + skatersTitle: 'Skaters - SkateSpots', + skatersDesc: 'Discover skaters in the SkateSpots community. Browse by style, stance, level and location.', + communityStatsTitle: 'Community Stats — SkateSpots', + communityStatsDesc: 'Live community metrics for SkateSpots — spots, events, coaches, filmers, editors, tours and growth over time. Pulled directly from Nostr.', + labTitle: 'The Lab - SkateSpots', + labDesc: 'Find skateboarding coaches, filmers, photographers, and video editors.', + stashTitle: 'My Stash - SkateSpots', + stashDesc: 'Manage your private gear inventory. Track what you own, what you want to sell or trade.', + bountyLeaderboardTitle: 'Map & Earn - SkateSpots', + bountyLeaderboardDesc: 'Map skate spots and shops, earn zaps from the community. See the top contributors.', + spotCollectionsTitle: 'My Spot Collections - SkateSpots', + spotCollectionsDesc: 'Organize your favorite skate spots into personalized collections', + mintsTitle: 'Cashu Mints — SkateSpots', + mintsDesc: 'Browse Cashu mints, check their health, and add one to your wallet.', + }, statusUi: { quickStatus: 'Quick Status', skating: 'Skating', diff --git a/src/i18n/es.ts b/src/i18n/es.ts index 28ff639..71802da 100644 --- a/src/i18n/es.ts +++ b/src/i18n/es.ts @@ -6864,6 +6864,7 @@ walletUi: { membersLabel: 'Miembros', dismissReport: 'Descartar reporte', reportedBy: 'reportado por', + completingLoginAmber: 'Completando el inicio de sesión con Amber...', entityMembersHint: 'Añade las cuentas Nostr de tu crew. Cada miembro confirma su propio spot con su clave — hasta entonces aparecen como “pendientes”.', entityVerifiedHint: 'La membresía se verifica por miembro — nadie puede añadirse a tu crew sin confirmarlo desde su propia cuenta.', entityInvalidPubkey: 'Introduce un npub, nprofile o clave hex de 64 caracteres válidos.', @@ -6882,6 +6883,83 @@ walletUi: { offlineViewDetails: 'Ver detalles de spots en caché', offlineKeepActing: 'Sigue actuando — publicaciones, RSVPs, anuncios y mensajes se ponen en cola y se envían al reconectar', }, + seoUi: { + becomeBuilderTitle: 'Ser Constructor - SkateSpots', + becomeBuilderDesc: 'Publica tus servicios de construcción de rampas, parques y DIY — qué construyes, materiales y escala.', + tradesTitle: 'Mis Tratos — SkateSpots', + tradesDesc: 'Gestiona tus tratos del marketplace, confirmaciones y reputación.', + eventsTitle: 'Eventos y Rutas de Skate - SkateSpots', + eventsDesc: 'Descubre eventos, competiciones, rutas y sesiones de skate cerca de ti.', + editShopDesc: 'Edita tu tienda de skate.', + createTourDesc: 'Organiza rutas de skate: rutas locales a spots ocultos, viajes nacionales, aventuras internacionales o competiciones de destrucción estilo KOTR.', + deckDesignerTitle: 'Diseñador de Tablas — SkateSpots DIY', + deckDesignerDesc: 'Diseña el contorno de una tabla de skate o longboard y exporta una plantilla imprimible 1:1, SVG o DXF — con concave, colas, distancia entre ejes y agujeros de ejes.', + createEventTitle: 'Crear un Evento de Skate - SkateSpots', + createEventDesc: 'Crea un nuevo evento de skate para la comunidad.', + editTourDesc: 'Edita tu ruta de skate — actualiza paradas, fechas y detalles.', + walletTitle: 'Cartera - SkateSpots', + walletDesc: 'Conecta tu cartera Lightning para enviar zaps a skaters y creadores de spots.', + becomePhotographerTitle: 'Ser Fotógrafo - SkateSpots', + becomePhotographerDesc: 'Crea tu perfil de fotografía de skate — cámaras, objetivos, iluminación y servicios.', + newsletterTitle: 'Boletín - SkateSpots', + newsletterDesc: 'Entregas semanales de la comunidad SkateSpots. Novedades de la app, nuevos spots, eventos y las personas que mantienen vivo el skate.', + homeTitle: 'SkateSpots - Descubre Lugares de Skate en Todo el Mundo', + zapLeaderboardTitle: 'Clasificación de Zaps - SkateSpots', + zapLeaderboardDesc: 'Los spots más zapeados y los miembros de la comunidad que los apoyan.', + adminReviewTitle: 'Cola de revisión — SkateSpots', + gearSwapTitle: 'Intercambio de Equipo - SkateSpots', + gearSwapDesc: 'Compra y vende equipo de skate usado con otros skaters. Tablas, ejes, ruedas, zapatillas y más.', + donateTitle: 'Donar — SkateSpots', + donateDesc: 'Apoya a SkateSpots — Lightning, on-chain privado (silent payments) o financia el desarrollo en Angor.', + verifyCardTitle: 'Verificar — SkateSpots', + verifyCardDesc: 'Verifica un carné de SkateSpots o un pase de acceso.', + mapTitle: 'Mapa de Spots de Skate - SkateSpots', + mapDesc: 'Explora spots de skate de todo el mundo en nuestro mapa interactivo.', + diyTitle: 'DIY — Hazlo Tú Mismo | SkateSpots', + diyDesc: 'Guías, herramientas abiertas y planos para hacer tus propias tablas, rampas y spots de hormigón — y la gente y los lugares para hacerlo.', + appShopTitle: 'Tienda de la App — SkateSpots', + becomeSkaterTitle: 'Únete al Directorio de Skaters - SkateSpots', + becomeSkaterDesc: 'Crea tu perfil de skater para que la comunidad te encuentre.', + marketplaceTitle: 'Marketplace - SkateSpots', + marketplaceDesc: 'Compra y vende equipo de skate en el mercado descentralizado Plebeian.', + becomeEditorTitle: 'Ser Editor de Vídeo - SkateSpots', + becomeEditorDesc: 'Crea tu perfil de editor de vídeo de skate en SkateSpots.', + amberCallbackTitle: 'Iniciando sesión con Amber - SkateSpots', + amberCallbackDesc: 'Completando tu inicio de sesión con Amber (NIP-55).', + supportersTitle: 'Colaboradores - SkateSpots', + supportersDesc: 'La comunidad que mantiene vivo SkateSpots — colaboradores, socios y amigos.', + becomeCoachTitle: 'Ser Coach - SkateSpots', + becomeCoachDesc: 'Crea tu perfil de coach de skate.', + riderCardTitle: 'Carné de Skater — SkateSpots', + riderCardDesc: 'Crea tu Licencia de Skater o Carné de Acceso a Parque imprimible de SkateSpots.', + editEventDesc: 'Edita tu evento de skate.', + createShopTitle: 'Añadir una Tienda de Skate - SkateSpots', + createShopDesc: 'Añade tu tienda de skate a la comunidad.', + deckCostTitle: 'Calculadora de Coste de Tablas — SkateSpots DIY', + deckCostDesc: 'Calcula cuánto cuesta hacer una tabla — mano de obra, equipo, materiales, tiempo de proceso, embalaje y margen — y un precio de venta justo.', + pyramidTitle: 'Clasificación Pirámide - SkateSpots', + pyramidDesc: 'Principales contribuidores y líderes de la comunidad.', + foodTitle: 'Comida Cerca de los Spots - SkateSpots', + foodDesc: 'Encuentra restaurantes, cafeterías y opciones de comida cerca de los spots de skate.', + createListingTitle: 'Publicar Equipo - SkateSpots', + createListingDesc: 'Publica equipo de skate en venta o encuentra el equipo que quieres comprar.', + becomeFilmerTitle: 'Ser Filmer - SkateSpots', + becomeFilmerDesc: 'Crea tu perfil de filmación de skate.', + skatersTitle: 'Skaters - SkateSpots', + skatersDesc: 'Descubre skaters de la comunidad SkateSpots. Explora por estilo, postura, nivel y ubicación.', + communityStatsTitle: 'Estadísticas de la Comunidad — SkateSpots', + communityStatsDesc: 'Métricas en vivo de la comunidad SkateSpots — spots, eventos, coaches, filmers, editores, rutas y crecimiento. Directo desde Nostr.', + labTitle: 'The Lab - SkateSpots', + labDesc: 'Encuentra coaches, filmers, fotógrafos y editores de vídeo de skate.', + stashTitle: 'Mi Stash - SkateSpots', + stashDesc: 'Gestiona tu inventario privado de equipo. Controla lo que tienes, lo que quieres vender o intercambiar.', + bountyLeaderboardTitle: 'Mapea y Gana - SkateSpots', + bountyLeaderboardDesc: 'Mapea spots y tiendas de skate, gana zaps de la comunidad. Mira los principales contribuidores.', + spotCollectionsTitle: 'Mis Colecciones de Spots - SkateSpots', + spotCollectionsDesc: 'Organiza tus spots de skate favoritos en colecciones personalizadas', + mintsTitle: 'Mints de Cashu — SkateSpots', + mintsDesc: 'Explora mints de Cashu, revisa su estado y añade uno a tu cartera.', + }, statusUi: { quickStatus: 'Estado rápido', skating: 'Patinando', diff --git a/src/pages/AdminReviewPage.tsx b/src/pages/AdminReviewPage.tsx index e9abe5b..7e19a39 100644 --- a/src/pages/AdminReviewPage.tsx +++ b/src/pages/AdminReviewPage.tsx @@ -718,7 +718,7 @@ export default function AdminReviewPage() { const { t } = useTranslation(); const { user } = useCurrentUser(); const [kind, setKind] = useState('reports'); - useSeoMeta({ title: 'Review queue — SkateSpots' }); + useSeoMeta({ title: t('seoUi.adminReviewTitle') }); // Admins only. Everyone else sees a 404 rather than a hint that this exists. if (!isAdminPubkey(user?.pubkey)) return ; diff --git a/src/pages/AmberCallbackPage.tsx b/src/pages/AmberCallbackPage.tsx index 358e52e..fc6b7be 100644 --- a/src/pages/AmberCallbackPage.tsx +++ b/src/pages/AmberCallbackPage.tsx @@ -14,6 +14,7 @@ import { useNavigate, Link } from 'react-router-dom'; import { useSeoMeta } from '@unhead/react'; import { useLoginActions } from '@/hooks/useLoginActions'; import { useToast } from '@/hooks/useToast'; +import { useTranslation } from '@/contexts/language'; import { getAmberCallbackResult, getAmberPendingOperationAsync, @@ -28,16 +29,17 @@ import { Button } from '@/components/ui/button'; type Status = 'processing' | 'success' | 'error'; export default function AmberCallbackPage() { + const { t } = useTranslation(); const navigate = useNavigate(); const login = useLoginActions(); const { toast } = useToast(); const hasProcessed = useRef(false); const [status, setStatus] = useState('processing'); - const [message, setMessage] = useState('Completing login with Amber...'); + const [message, setMessage] = useState(t('miscUi.completingLoginAmber')); useSeoMeta({ - title: 'Signing in with Amber - SkateSpots', - description: 'Completing your Amber (NIP-55) login.', + title: t('seoUi.amberCallbackTitle'), + description: t('seoUi.amberCallbackDesc'), }); useEffect(() => { diff --git a/src/pages/AppShopPage.tsx b/src/pages/AppShopPage.tsx index fce85a3..a74ddf8 100644 --- a/src/pages/AppShopPage.tsx +++ b/src/pages/AppShopPage.tsx @@ -73,7 +73,7 @@ const PAY_CHIPS = [ export default function AppShopPage() { const { t } = useTranslation(); useSeoMeta({ - title: 'App Shop — SkateSpots', + title: t('seoUi.appShopTitle'), description: 'SkateSpots merch, paid in sats over Lightning or Cashu. Nothing in stock yet — here is what is coming.', }); diff --git a/src/pages/BecomeBuilderPage.tsx b/src/pages/BecomeBuilderPage.tsx index 2d9c4a6..702af51 100644 --- a/src/pages/BecomeBuilderPage.tsx +++ b/src/pages/BecomeBuilderPage.tsx @@ -94,8 +94,8 @@ function ChipGrid({ export default function BecomeBuilderPage() { const { t } = useTranslation(); useSeoMeta({ - title: 'Become a Builder - SkateSpots', - description: 'List your ramp, park and DIY building services — what you build, materials and scale.', + title: t('seoUi.becomeBuilderTitle'), + description: t('seoUi.becomeBuilderDesc'), }); const navigate = useNavigate(); diff --git a/src/pages/BecomeCoachPage.tsx b/src/pages/BecomeCoachPage.tsx index 302f18f..c59233f 100644 --- a/src/pages/BecomeCoachPage.tsx +++ b/src/pages/BecomeCoachPage.tsx @@ -102,8 +102,8 @@ function OptionChips({ export default function BecomeCoachPage() { const { t } = useTranslation(); useSeoMeta({ - title: 'Become a Coach - SkateSpots', - description: 'Create your skateboarding coach profile.', + title: t('seoUi.becomeCoachTitle'), + description: t('seoUi.becomeCoachDesc'), }); const navigate = useNavigate(); diff --git a/src/pages/BecomeEditorPage.tsx b/src/pages/BecomeEditorPage.tsx index eaccf51..4554f2f 100644 --- a/src/pages/BecomeEditorPage.tsx +++ b/src/pages/BecomeEditorPage.tsx @@ -51,8 +51,8 @@ const SKILL_ENTRIES = Object.entries(EDITOR_SKILL_LABELS); export default function BecomeEditorPage() { const { t } = useTranslation(); useSeoMeta({ - title: 'Become a Video Editor - SkateSpots', - description: 'Create your skateboarding video editor profile on SkateSpots.', + title: t('seoUi.becomeEditorTitle'), + description: t('seoUi.becomeEditorDesc'), }); const navigate = useNavigate(); diff --git a/src/pages/BecomeFilmerPage.tsx b/src/pages/BecomeFilmerPage.tsx index 139b4fe..43653b2 100644 --- a/src/pages/BecomeFilmerPage.tsx +++ b/src/pages/BecomeFilmerPage.tsx @@ -45,8 +45,8 @@ const SKILL_ENTRIES = Object.entries(FILMING_SKILL_LABELS); export default function BecomeFilmerPage() { const { t } = useTranslation(); useSeoMeta({ - title: 'Become a Filmer - SkateSpots', - description: 'Create your skateboarding filming profile.', + title: t('seoUi.becomeFilmerTitle'), + description: t('seoUi.becomeFilmerDesc'), }); const navigate = useNavigate(); diff --git a/src/pages/BecomePhotographerPage.tsx b/src/pages/BecomePhotographerPage.tsx index 0b65f80..195f469 100644 --- a/src/pages/BecomePhotographerPage.tsx +++ b/src/pages/BecomePhotographerPage.tsx @@ -103,8 +103,8 @@ function ChipGrid({ export default function BecomePhotographerPage() { const { t } = useTranslation(); useSeoMeta({ - title: 'Become a Photographer - SkateSpots', - description: 'Create your skate photography profile — cameras, lenses, lighting and services.', + title: t('seoUi.becomePhotographerTitle'), + description: t('seoUi.becomePhotographerDesc'), }); const navigate = useNavigate(); diff --git a/src/pages/BecomeSkaterPage.tsx b/src/pages/BecomeSkaterPage.tsx index 683fcf2..8d18fd1 100644 --- a/src/pages/BecomeSkaterPage.tsx +++ b/src/pages/BecomeSkaterPage.tsx @@ -37,8 +37,8 @@ import { X, Loader2, ArrowLeft, MapPin, PersonStanding, Sparkles, UserRound, Shi export default function BecomeSkaterPage() { const { t } = useTranslation(); useSeoMeta({ - title: 'Join the Skater Directory - SkateSpots', - description: 'Create your skater profile so the community can find you.', + title: t('seoUi.becomeSkaterTitle'), + description: t('seoUi.becomeSkaterDesc'), }); const navigate = useNavigate(); diff --git a/src/pages/BountyLeaderboardPage.tsx b/src/pages/BountyLeaderboardPage.tsx index 1f3fb74..3025bf3 100644 --- a/src/pages/BountyLeaderboardPage.tsx +++ b/src/pages/BountyLeaderboardPage.tsx @@ -124,8 +124,8 @@ function LeaderboardSkeleton() { export default function BountyLeaderboardPage() { const { t } = useTranslation(); useSeoMeta({ - title: 'Map & Earn - SkateSpots', - description: 'Map skate spots and shops, earn zaps from the community. See the top contributors.', + title: t('seoUi.bountyLeaderboardTitle'), + description: t('seoUi.bountyLeaderboardDesc'), }); const { user } = useCurrentUser(); diff --git a/src/pages/CommunityStatsPage.tsx b/src/pages/CommunityStatsPage.tsx index 716b51d..a81a621 100644 --- a/src/pages/CommunityStatsPage.tsx +++ b/src/pages/CommunityStatsPage.tsx @@ -179,8 +179,8 @@ function ChartSkeleton() { export default function CommunityStatsPage() { const { t } = useTranslation(); useSeoMeta({ - title: 'Community Stats — SkateSpots', - description: 'Live community metrics for SkateSpots — spots, events, coaches, filmers, editors, tours and growth over time. Pulled directly from Nostr.', + title: t('seoUi.communityStatsTitle'), + description: t('seoUi.communityStatsDesc'), }); const { data: stats, isLoading, isError, isFetching, refetch, dataUpdatedAt } = useGrowthStats(); diff --git a/src/pages/CreateEventPage.tsx b/src/pages/CreateEventPage.tsx index 906f378..e3bc42d 100644 --- a/src/pages/CreateEventPage.tsx +++ b/src/pages/CreateEventPage.tsx @@ -59,8 +59,8 @@ export default function CreateEventPage() { const { t } = useTranslation(); useSeoMeta({ - title: 'Create a Skate Event - SkateSpots', - description: 'Create a new skateboarding event for the community.', + title: t('seoUi.createEventTitle'), + description: t('seoUi.createEventDesc'), }); const navigate = useNavigate(); diff --git a/src/pages/CreateListingPage.tsx b/src/pages/CreateListingPage.tsx index 33c45a8..bdd1d0f 100644 --- a/src/pages/CreateListingPage.tsx +++ b/src/pages/CreateListingPage.tsx @@ -30,8 +30,8 @@ import { useTranslation } from '@/contexts/language'; export default function CreateListingPage() { const { t } = useTranslation(); useSeoMeta({ - title: 'List Gear - SkateSpots', - description: 'List skateboarding gear for sale or find gear you want to buy.', + title: t('seoUi.createListingTitle'), + description: t('seoUi.createListingDesc'), }); const navigate = useNavigate(); diff --git a/src/pages/CreateShopPage.tsx b/src/pages/CreateShopPage.tsx index c28b46d..fc20817 100644 --- a/src/pages/CreateShopPage.tsx +++ b/src/pages/CreateShopPage.tsx @@ -31,8 +31,8 @@ import { useTranslation } from '@/contexts/language'; export default function CreateShopPage() { const { t } = useTranslation(); useSeoMeta({ - title: 'Add a Skate Shop - SkateSpots', - description: 'Add your skate shop to the community.', + title: t('seoUi.createShopTitle'), + description: t('seoUi.createShopDesc'), }); const navigate = useNavigate(); diff --git a/src/pages/CreateTourPage.tsx b/src/pages/CreateTourPage.tsx index b26b286..c598ed2 100644 --- a/src/pages/CreateTourPage.tsx +++ b/src/pages/CreateTourPage.tsx @@ -169,7 +169,7 @@ export default function CreateTourPage({ existing }: { existing?: SkateTour } = useSeoMeta({ title: isEditing ? 'Edit Tour - SkateSpots' : 'Create a Tour - SkateSpots', - description: 'Organize skate tours - local hidden gem tours, national road trips, international adventures, or KOTR-style destruction competitions.', + description: t('seoUi.createTourDesc'), }); const navigate = useNavigate(); diff --git a/src/pages/DeckCostPage.tsx b/src/pages/DeckCostPage.tsx index 85a7cfb..93233dc 100644 --- a/src/pages/DeckCostPage.tsx +++ b/src/pages/DeckCostPage.tsx @@ -9,8 +9,8 @@ export default function DeckCostPage() { const { t } = useTranslation(); useSeoMeta({ - title: 'Deck Cost Calculator — SkateSpots DIY', - description: 'Work out what a deck costs to make — labour, equipment, materials, process time, packaging and margin — and a fair selling price.', + title: t('seoUi.deckCostTitle'), + description: t('seoUi.deckCostDesc'), }); return ( diff --git a/src/pages/DeckDesignerPage.tsx b/src/pages/DeckDesignerPage.tsx index 3f7566a..ba68586 100644 --- a/src/pages/DeckDesignerPage.tsx +++ b/src/pages/DeckDesignerPage.tsx @@ -19,8 +19,8 @@ export default function DeckDesignerPage() { const { user } = useCurrentUser(); useSeoMeta({ - title: 'Deck Designer — SkateSpots DIY', - description: 'Design a skateboard or longboard outline, then export a printable 1:1 template, SVG or DXF — concave, kicks, wheelbase and truck holes included.', + title: t('seoUi.deckDesignerTitle'), + description: t('seoUi.deckDesignerDesc'), }); if (!isAdminPubkey(user?.pubkey)) return ; diff --git a/src/pages/DiyPage.tsx b/src/pages/DiyPage.tsx index 9c473d7..9325690 100644 --- a/src/pages/DiyPage.tsx +++ b/src/pages/DiyPage.tsx @@ -60,8 +60,8 @@ export default function DiyPage() { const { t } = useTranslation(); useSeoMeta({ - title: 'DIY — Make Your Own | SkateSpots', - description: 'Guides, open tools and plans to make your own skateboards, ramps and concrete spots — and the people and places to do it with.', + title: t('seoUi.diyTitle'), + description: t('seoUi.diyDesc'), }); return ( diff --git a/src/pages/DonatePage.tsx b/src/pages/DonatePage.tsx index 0f329d7..54b65b8 100644 --- a/src/pages/DonatePage.tsx +++ b/src/pages/DonatePage.tsx @@ -12,8 +12,8 @@ import { useTranslation } from '@/contexts/language'; export default function DonatePage() { const { t } = useTranslation(); useSeoMeta({ - title: 'Donate — SkateSpots', - description: 'Support SkateSpots — Lightning, private on-chain (silent payments), or fund the build on Angor.', + title: t('seoUi.donateTitle'), + description: t('seoUi.donateDesc'), ogTitle: 'Support SkateSpots', ogDescription: 'Keep the map free, ad-free and open-source.', ogImage: 'https://skate-spots.org/images/icon-1024.png', diff --git a/src/pages/EditEventPage.tsx b/src/pages/EditEventPage.tsx index 1f574fa..f1c55d1 100644 --- a/src/pages/EditEventPage.tsx +++ b/src/pages/EditEventPage.tsx @@ -109,7 +109,7 @@ export default function EditEventPage() { useSeoMeta({ title: event ? `Edit ${event.title} - SkateSpots` : 'Edit Event - SkateSpots', - description: 'Edit your skateboarding event.', + description: t('seoUi.editEventDesc'), }); // Initialize form with existing data diff --git a/src/pages/EditShopPage.tsx b/src/pages/EditShopPage.tsx index a503bd4..c316bca 100644 --- a/src/pages/EditShopPage.tsx +++ b/src/pages/EditShopPage.tsx @@ -88,7 +88,7 @@ export default function EditShopPage() { useSeoMeta({ title: shop ? `Edit ${shop.name} - SkateSpots` : 'Edit Shop - SkateSpots', - description: 'Edit your skate shop.', + description: t('seoUi.editShopDesc'), }); // Initialize form with existing data diff --git a/src/pages/EditTourPage.tsx b/src/pages/EditTourPage.tsx index b17486b..9d1d366 100644 --- a/src/pages/EditTourPage.tsx +++ b/src/pages/EditTourPage.tsx @@ -24,7 +24,7 @@ export default function EditTourPage() { useSeoMeta({ title: tour?.title ? `Edit ${tour.title} - SkateSpots` : 'Edit Tour - SkateSpots', - description: 'Edit your skate tour — update stops, dates and details.', + description: t('seoUi.editTourDesc'), }); if (isLoading) { diff --git a/src/pages/EventsPage.tsx b/src/pages/EventsPage.tsx index 7ad82f2..f3ee1bf 100644 --- a/src/pages/EventsPage.tsx +++ b/src/pages/EventsPage.tsx @@ -814,8 +814,8 @@ export default function EventsPage() { const { t } = useTranslation(); useSeoMeta({ - title: 'Skate Events & Tours - SkateSpots', - description: 'Discover skateboarding events, competitions, tours, and sessions near you.', + title: t('seoUi.eventsTitle'), + description: t('seoUi.eventsDesc'), }); const { user } = useCurrentUser(); diff --git a/src/pages/FoodPage.tsx b/src/pages/FoodPage.tsx index cf299e4..5044595 100644 --- a/src/pages/FoodPage.tsx +++ b/src/pages/FoodPage.tsx @@ -170,8 +170,8 @@ function FoodSpotSkeleton() { export default function FoodPage() { const { t } = useTranslation(); useSeoMeta({ - title: 'Food Near Spots - SkateSpots', - description: 'Find restaurants, cafés, and food options near skateboarding spots.', + title: t('seoUi.foodTitle'), + description: t('seoUi.foodDesc'), }); const { data: spots, isLoading: spotsLoading } = useSkateSpots(50); diff --git a/src/pages/GearSwapPage.tsx b/src/pages/GearSwapPage.tsx index 68fe056..39a6bfe 100644 --- a/src/pages/GearSwapPage.tsx +++ b/src/pages/GearSwapPage.tsx @@ -29,8 +29,8 @@ type TrustFilter = 'all' | 'verified' | 'trusted' | 'following'; export default function GearSwapPage() { const { t } = useTranslation(); useSeoMeta({ - title: 'Gear Swap - SkateSpots', - description: 'Buy & sell used skate gear with other skaters. Decks, trucks, wheels, shoes, and more.', + title: t('seoUi.gearSwapTitle'), + description: t('seoUi.gearSwapDesc'), }); const { user } = useCurrentUser(); diff --git a/src/pages/Index.tsx b/src/pages/Index.tsx index 635bdbd..625d3d8 100644 --- a/src/pages/Index.tsx +++ b/src/pages/Index.tsx @@ -621,8 +621,9 @@ function Footer() { } export default function Index() { + const { t } = useTranslation(); useSeoMeta({ - title: 'SkateSpots - Discover Skateboarding Locations Worldwide', + title: t('seoUi.homeTitle'), description: 'Find the best skateboarding spots, connect with fellow skaters, and discover events near you. Built on Nostr for true decentralization.', }); diff --git a/src/pages/LabPage.tsx b/src/pages/LabPage.tsx index 01c9067..1f0dea5 100644 --- a/src/pages/LabPage.tsx +++ b/src/pages/LabPage.tsx @@ -1354,8 +1354,8 @@ export default function LabPage() { const { t } = useTranslation(); useSeoMeta({ - title: 'The Lab - SkateSpots', - description: 'Find skateboarding coaches, filmers, photographers, and video editors.', + title: t('seoUi.labTitle'), + description: t('seoUi.labDesc'), }); const { user } = useCurrentUser(); diff --git a/src/pages/MapPage.tsx b/src/pages/MapPage.tsx index b7f0fbb..12182a0 100644 --- a/src/pages/MapPage.tsx +++ b/src/pages/MapPage.tsx @@ -536,8 +536,8 @@ export default function MapPage() { const { t } = useTranslation(); useSeoMeta({ - title: 'Skate Spots Map - SkateSpots', - description: 'Explore skateboarding spots around the world on our interactive map.', + title: t('seoUi.mapTitle'), + description: t('seoUi.mapDesc'), }); const navigate = useNavigate(); diff --git a/src/pages/MarketplacePage.tsx b/src/pages/MarketplacePage.tsx index 25d30e1..e95a5df 100644 --- a/src/pages/MarketplacePage.tsx +++ b/src/pages/MarketplacePage.tsx @@ -9,8 +9,8 @@ import { useTranslation } from '@/contexts/language'; export default function MarketplacePage() { const { t } = useTranslation(); useSeoMeta({ - title: 'Marketplace - SkateSpots', - description: 'Buy and sell skateboarding gear on the decentralized Plebeian Market.', + title: t('seoUi.marketplaceTitle'), + description: t('seoUi.marketplaceDesc'), }); return ( diff --git a/src/pages/MintsPage.tsx b/src/pages/MintsPage.tsx index 827d29d..46b9c1b 100644 --- a/src/pages/MintsPage.tsx +++ b/src/pages/MintsPage.tsx @@ -187,8 +187,8 @@ export default function MintsPage() { }; useSeoMeta({ - title: 'Cashu Mints — SkateSpots', - description: 'Browse Cashu mints, check their health, and add one to your wallet.', + title: t('seoUi.mintsTitle'), + description: t('seoUi.mintsDesc'), }); const canAdd = isNativePlatform() && isInitialized && !!coco; diff --git a/src/pages/NewsletterPage.tsx b/src/pages/NewsletterPage.tsx index bfab190..81ce598 100644 --- a/src/pages/NewsletterPage.tsx +++ b/src/pages/NewsletterPage.tsx @@ -629,8 +629,8 @@ function LiveIssuesSection() { export default function NewsletterPage() { const { t } = useTranslation(); useSeoMeta({ - title: 'Newsletter - SkateSpots', - description: 'Weekly drops from the SkateSpots community. App updates, new spots, events and the people keeping skateboarding alive.', + title: t('seoUi.newsletterTitle'), + description: t('seoUi.newsletterDesc'), }); // Opening the newsletter clears the "News" unread badge in the bell — once diff --git a/src/pages/PyramidPage.tsx b/src/pages/PyramidPage.tsx index 58b642e..afb00d4 100644 --- a/src/pages/PyramidPage.tsx +++ b/src/pages/PyramidPage.tsx @@ -77,8 +77,8 @@ function ContributorRow({ pubkey, spots, events, rank }: { pubkey: string; spots export default function PyramidPage() { const { t } = useTranslation(); useSeoMeta({ - title: 'Pyramid Rankings - SkateSpots', - description: 'Top contributors and community leaders.', + title: t('seoUi.pyramidTitle'), + description: t('seoUi.pyramidDesc'), }); const { user } = useCurrentUser(); diff --git a/src/pages/RiderCardPage.tsx b/src/pages/RiderCardPage.tsx index d58630a..94eb841 100644 --- a/src/pages/RiderCardPage.tsx +++ b/src/pages/RiderCardPage.tsx @@ -161,8 +161,8 @@ function BadgeEventRow({ event }: { event: NostrEvent }) { export default function RiderCardPage() { const { t } = useTranslation(); useSeoMeta({ - title: 'Skater Card — SkateSpots', - description: 'Create your printable SkateSpots Skater License or Park Access Card.', + title: t('seoUi.riderCardTitle'), + description: t('seoUi.riderCardDesc'), }); const { user } = useCurrentUser(); diff --git a/src/pages/SkatersPage.tsx b/src/pages/SkatersPage.tsx index c9fc516..f1e1eeb 100644 --- a/src/pages/SkatersPage.tsx +++ b/src/pages/SkatersPage.tsx @@ -135,8 +135,8 @@ function SkaterCardSkeleton() { export default function SkatersPage() { const { t } = useTranslation(); useSeoMeta({ - title: 'Skaters - SkateSpots', - description: 'Discover skaters in the SkateSpots community. Browse by style, stance, level and location.', + title: t('seoUi.skatersTitle'), + description: t('seoUi.skatersDesc'), }); const { data: skaters, isLoading } = useSkaters(); diff --git a/src/pages/SpotCollectionsPage.tsx b/src/pages/SpotCollectionsPage.tsx index fe056de..ce093da 100644 --- a/src/pages/SpotCollectionsPage.tsx +++ b/src/pages/SpotCollectionsPage.tsx @@ -149,8 +149,8 @@ export default function SpotCollectionsPage() { const [editDescription, setEditDescription] = useState(''); useSeoMeta({ - title: 'My Spot Collections - SkateSpots', - description: 'Organize your favorite skate spots into personalized collections', + title: t('seoUi.spotCollectionsTitle'), + description: t('seoUi.spotCollectionsDesc'), }); const handleCreate = async (e: React.FormEvent) => { diff --git a/src/pages/StashPage.tsx b/src/pages/StashPage.tsx index 723d9da..f2395f9 100644 --- a/src/pages/StashPage.tsx +++ b/src/pages/StashPage.tsx @@ -650,8 +650,8 @@ function ListAtSpotDialog({ item, open, onOpenChange, onSelectSpot }: { export default function StashPage() { const { t } = useTranslation(); useSeoMeta({ - title: 'My Stash - SkateSpots', - description: 'Manage your private gear inventory. Track what you own, what you want to sell or trade.', + title: t('seoUi.stashTitle'), + description: t('seoUi.stashDesc'), }); const navigate = useNavigate(); diff --git a/src/pages/SupportersPage.tsx b/src/pages/SupportersPage.tsx index 91cf6ae..7de30b7 100644 --- a/src/pages/SupportersPage.tsx +++ b/src/pages/SupportersPage.tsx @@ -519,8 +519,8 @@ export default function SupportersPage() { : []; useSeoMeta({ - title: 'Supporters - SkateSpots', - description: 'The community keeping SkateSpots alive — supporters, collaborators, partners and friends.', + title: t('seoUi.supportersTitle'), + description: t('seoUi.supportersDesc'), ogTitle: 'Supporters — SkateSpots', ogDescription: 'Built by skaters, funded by skaters. Back the map on Angor or zap it.', ogImage: 'https://skate-spots.org/images/icon-1024.png', diff --git a/src/pages/TradesPage.tsx b/src/pages/TradesPage.tsx index aba2d98..c740bbd 100644 --- a/src/pages/TradesPage.tsx +++ b/src/pages/TradesPage.tsx @@ -69,8 +69,8 @@ export default function TradesPage() { const [revoking, setRevoking] = useState(false); useSeoMeta({ - title: 'My Trades — SkateSpots', - description: 'Manage your marketplace trades, confirmations and reputation.', + title: t('seoUi.tradesTitle'), + description: t('seoUi.tradesDesc'), }); const handleConfirmSale = async ( diff --git a/src/pages/VerifyCardPage.tsx b/src/pages/VerifyCardPage.tsx index e491cc5..d4de823 100644 --- a/src/pages/VerifyCardPage.tsx +++ b/src/pages/VerifyCardPage.tsx @@ -59,7 +59,7 @@ export default function VerifyCardPage() { const { nostr } = useNostr(); const parsed = parseNaddr(naddr); - useSeoMeta({ title: 'Verify — SkateSpots', description: 'Verify a SkateSpots card or access pass.' }); + useSeoMeta({ title: t('seoUi.verifyCardTitle'), description: t('seoUi.verifyCardDesc') }); const result = useQuery({ queryKey: ['verify-card', naddr], diff --git a/src/pages/WalletPage.tsx b/src/pages/WalletPage.tsx index feab602..08d7e2d 100644 --- a/src/pages/WalletPage.tsx +++ b/src/pages/WalletPage.tsx @@ -236,8 +236,8 @@ function NotLoggedIn() { export default function WalletPage() { const { t } = useTranslation(); useSeoMeta({ - title: 'Wallet - SkateSpots', - description: 'Connect your Lightning wallet to send zaps to skaters and spot creators.', + title: t('seoUi.walletTitle'), + description: t('seoUi.walletDesc'), }); const { user } = useCurrentUser(); diff --git a/src/pages/ZapLeaderboardPage.tsx b/src/pages/ZapLeaderboardPage.tsx index dfe212d..00b007e 100644 --- a/src/pages/ZapLeaderboardPage.tsx +++ b/src/pages/ZapLeaderboardPage.tsx @@ -150,8 +150,8 @@ function RowsSkeleton() { export default function ZapLeaderboardPage() { const { t } = useTranslation(); useSeoMeta({ - title: 'Zap Leaderboards - SkateSpots', - description: 'Top zapped skate spots and the community members supporting them.', + title: t('seoUi.zapLeaderboardTitle'), + description: t('seoUi.zapLeaderboardDesc'), }); const [range, setRange] = useState('month');